From 7e9c30dfaf391ebb299381d6077644870c227853 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20K=C3=B6hler?= Date: Sun, 22 Apr 2012 19:53:48 +0200 Subject: [PATCH 01/20] fix bug 825205 - Keep/restore selected book, chapters and verses when changing the bible translation in advanced search --- openlp/plugins/bibles/lib/mediaitem.py | 31 +++++++++++++++++++++----- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index bca73fc70..c36ffa2bf 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -408,7 +408,7 @@ class BibleMediaItem(MediaManagerItem): self.plugin.appStartup() self.updateAutoCompleter() - def initialiseAdvancedBible(self, bible): + def initialiseAdvancedBible(self, bible, last_book_id=None): """ This initialises the given bible, which means that its book names and their chapter numbers is added to the combo boxes on the @@ -417,8 +417,12 @@ class BibleMediaItem(MediaManagerItem): ``bible`` The bible to initialise (unicode). + + ``last_book_id`` + The "book reference id" of the book which is choosen at the moment. + (int) """ - log.debug(u'initialiseAdvancedBible %s', bible) + log.debug(u'initialiseAdvancedBible %s, %s', bible, last_book_id) book_data = self.plugin.manager.get_books(bible) secondbible = unicode(self.advancedSecondComboBox.currentText()) if secondbible != u'': @@ -451,8 +455,19 @@ class BibleMediaItem(MediaManagerItem): row, QtCore.QVariant(book[u'book_reference_id'])) if first: first = False - self.initialiseChapterVerse(bible, book[u'name'], - book[u'book_reference_id']) + first_book = book + initialise_chapter_verse = True + if last_book_id and last_book_id == int(book[u'book_reference_id']): + index = self.advancedBookComboBox.findData( + QtCore.QVariant(book[u'book_reference_id'])) + if index == -1: + # Not Found. + index = 0 + self.advancedBookComboBox.setCurrentIndex(index) + initialise_chapter_verse = False + if initialise_chapter_verse: + self.initialiseChapterVerse(bible, first_book[u'name'], + first_book[u'book_reference_id']) def initialiseChapterVerse(self, bible, book, book_ref_id): log.debug(u'initialiseChapterVerse %s, %s, %s', bible, book, @@ -597,11 +612,15 @@ class BibleMediaItem(MediaManagerItem): QtCore.QSettings().setValue(self.settingsSection + u'/advanced bible', QtCore.QVariant(self.advancedVersionComboBox.currentText())) self.initialiseAdvancedBible( - unicode(self.advancedVersionComboBox.currentText())) + unicode(self.advancedVersionComboBox.currentText()), + self.advancedBookComboBox.itemData( + int(self.advancedBookComboBox.currentIndex()))) def onAdvancedSecondComboBox(self): self.initialiseAdvancedBible( - unicode(self.advancedVersionComboBox.currentText())) + unicode(self.advancedVersionComboBox.currentText()), + self.advancedBookComboBox.itemData( + int(self.advancedBookComboBox.currentIndex()))) def onAdvancedBookComboBox(self): item = int(self.advancedBookComboBox.currentIndex()) From 19ea43ead86413458e8d9970b124431ad0f480d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armin=20K=C3=B6hler?= Date: Mon, 23 Apr 2012 22:21:38 +0200 Subject: [PATCH 02/20] fixes --- openlp/plugins/bibles/lib/mediaitem.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index c36ffa2bf..8afb933dd 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -436,6 +436,7 @@ class BibleMediaItem(MediaManagerItem): book_data = book_data_temp self.advancedBookComboBox.clear() first = True + initialise_chapter_verse = False language_selection = self.plugin.manager.get_language_selection(bible) booknames = BibleStrings().Booknames for book in book_data: @@ -614,13 +615,13 @@ class BibleMediaItem(MediaManagerItem): self.initialiseAdvancedBible( unicode(self.advancedVersionComboBox.currentText()), self.advancedBookComboBox.itemData( - int(self.advancedBookComboBox.currentIndex()))) + int(self.advancedBookComboBox.currentIndex()))) def onAdvancedSecondComboBox(self): self.initialiseAdvancedBible( unicode(self.advancedVersionComboBox.currentText()), self.advancedBookComboBox.itemData( - int(self.advancedBookComboBox.currentIndex()))) + int(self.advancedBookComboBox.currentIndex()))) def onAdvancedBookComboBox(self): item = int(self.advancedBookComboBox.currentIndex()) From cbbfa8c23b69c7543d4e748b05b3b613ab8b6f98 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 28 Apr 2012 13:13:16 +0200 Subject: [PATCH 03/20] clean ups --- openlp/core/ui/media/__init__.py | 13 +-- openlp/core/ui/media/mediacontroller.py | 114 +++++++++++------------- openlp/core/ui/media/vlcplayer.py | 19 ++-- 3 files changed, 71 insertions(+), 75 deletions(-) diff --git a/openlp/core/ui/media/__init__.py b/openlp/core/ui/media/__init__.py index f4f4d98d0..ff6f7ae91 100644 --- a/openlp/core/ui/media/__init__.py +++ b/openlp/core/ui/media/__init__.py @@ -69,12 +69,13 @@ class MediaInfo(object): def get_media_players(): """ This method extract the configured media players and overridden player from - the settings + the settings. ``players_list`` - this is a python list with all active media players + Awith all active media players. + ``overridden_player`` - here an special media player is choosen for all media actions + Here an special media player is chosen for all media actions. """ log.debug(u'get_media_players') players = unicode(QtCore.QSettings().value(u'media/players').toString()) @@ -92,15 +93,17 @@ def get_media_players(): players_list = players.replace(u'[', u'').replace(u']', u'').split(u',') return players_list, overridden_player + def set_media_players(players_list, overridden_player=u'auto'): """ This method saves the configured media players and overridden player to the settings ``players_list`` - this is a python list with all active media players + A list with all active media players. + ``overridden_player`` - here an special media player is choosen for all media actions + Here an special media player is chosen for all media actions. """ log.debug(u'set_media_players') players = u','.join(players_list) diff --git a/openlp/core/ui/media/mediacontroller.py b/openlp/core/ui/media/mediacontroller.py index 7ccbd9245..bf5bcf808 100644 --- a/openlp/core/ui/media/mediacontroller.py +++ b/openlp/core/ui/media/mediacontroller.py @@ -84,10 +84,7 @@ class MediaController(object): def set_active_players(self): savedPlayers = get_media_players()[0] for player in self.mediaPlayers.keys(): - if player in savedPlayers: - self.mediaPlayers[player].isActive = True - else: - self.mediaPlayers[player].isActive = False + self.mediaPlayers[player].isActive = player in savedPlayers def register_controllers(self, controller): """ @@ -106,8 +103,8 @@ class MediaController(object): AppLocation.get_directory(AppLocation.AppDir), u'core', u'ui', u'media') for filename in os.listdir(controller_dir): - if filename.endswith(u'player.py') and \ - not filename == 'media_player.py': + if filename.endswith(u'player.py') and not \ + filename == 'media_player.py': path = os.path.join(controller_dir, filename) if os.path.isfile(path): modulename = u'openlp.core.ui.media.' + \ @@ -122,38 +119,36 @@ class MediaController(object): for controller_class in controller_classes: controller = controller_class(self) self.register_controllers(controller) - if self.mediaPlayers: - savedPlayers, overriddenPlayer = get_media_players() - invalidMediaPlayers = [mediaPlayer for mediaPlayer in savedPlayers \ - if not mediaPlayer in self.mediaPlayers or \ - not self.mediaPlayers[mediaPlayer].check_available()] - if len(invalidMediaPlayers) > 0: - for invalidPlayer in invalidMediaPlayers: - savedPlayers.remove(invalidPlayer) - set_media_players(savedPlayers, overriddenPlayer) - self.set_active_players() - return True - else: + if not self.mediaPlayers: return False + savedPlayers, overriddenPlayer = get_media_players() + invalidMediaPlayers = [mediaPlayer for mediaPlayer in savedPlayers + if not mediaPlayer in self.mediaPlayers or not + self.mediaPlayers[mediaPlayer].check_available()] + if invalidMediaPlayers: + for invalidPlayer in invalidMediaPlayers: + savedPlayers.remove(invalidPlayer) + set_media_players(savedPlayers, overriddenPlayer) + self.set_active_players() + return True def video_state(self): """ Check if there is a running media Player and do updating stuff (e.g. update the UI) """ - if len(self.curDisplayMediaPlayer.keys()) == 0: + if not self.curDisplayMediaPlayer.keys(): self.timer.stop() else: for display in self.curDisplayMediaPlayer.keys(): self.curDisplayMediaPlayer[display].resize(display) self.curDisplayMediaPlayer[display].update_ui(display) - if self.curDisplayMediaPlayer[display] \ - .state == MediaState.Playing: + if self.curDisplayMediaPlayer[display].state == \ + MediaState.Playing: return # no players are active anymore for display in self.curDisplayMediaPlayer.keys(): - if self.curDisplayMediaPlayer[display] \ - .state != MediaState.Paused: + if self.curDisplayMediaPlayer[display].state != MediaState.Paused: display.controller.seekSlider.setSliderPosition(0) self.timer.stop() @@ -333,8 +328,7 @@ class MediaController(object): 'Unsupported File'))) return False # dont care about actual theme, set a black background - if controller.isLive and ( \ - controller.media_info.is_background == False): + if controller.isLive and not controller.media_info.is_background: display.frame.evaluateJavaScript(u'show_video( \ "setBackBoard", null, null, null,"visible");') # now start playing @@ -395,7 +389,7 @@ class MediaController(object): """ Responds to the request to play a loaded video - ``msg`` + ``msg`` First element is the controller which should be used """ log.debug(u'video_play') @@ -497,15 +491,15 @@ class MediaController(object): First element is the boolean for Live indication """ isLive = msg[1] - if isLive: - controller = self.parent.liveController - for display in self.curDisplayMediaPlayer.keys(): - if display.controller == controller: - if self.curDisplayMediaPlayer[display] \ - .state == MediaState.Playing: - self.curDisplayMediaPlayer[display].pause(display) - self.curDisplayMediaPlayer[display] \ - .set_visible(display, False) + if not isLive: + return + controller = self.parent.liveController + for display in self.curDisplayMediaPlayer.keys(): + if display.controller != controller or \ + self.curDisplayMediaPlayer[display].state == MediaState.Playing: + continue + self.curDisplayMediaPlayer[display].pause(display) + self.curDisplayMediaPlayer[display].set_visible(display, False) def video_blank(self, msg): """ @@ -517,16 +511,16 @@ class MediaController(object): """ isLive = msg[1] hide_mode = msg[2] - if isLive: - Receiver.send_message(u'live_display_hide', hide_mode) - controller = self.parent.liveController - for display in self.curDisplayMediaPlayer.keys(): - if display.controller == controller: - if self.curDisplayMediaPlayer[display] \ - .state == MediaState.Playing: - self.curDisplayMediaPlayer[display].pause(display) - self.curDisplayMediaPlayer[display] \ - .set_visible(display, False) + if not isLive: + return + Receiver.send_message(u'live_display_hide', hide_mode) + controller = self.parent.liveController + for display in self.curDisplayMediaPlayer.keys(): + if display.controller != controller or \ + self.curDisplayMediaPlayer[display].state != MediaState.Playing: + continue + self.curDisplayMediaPlayer[display].pause(display) + self.curDisplayMediaPlayer[display].set_visible(display, False) def video_unblank(self, msg): """ @@ -538,19 +532,18 @@ class MediaController(object): """ Receiver.send_message(u'live_display_show') isLive = msg[1] - if isLive: - controller = self.parent.liveController - for display in self.curDisplayMediaPlayer.keys(): - if display.controller == controller: - if self.curDisplayMediaPlayer[display] \ - .state == MediaState.Paused: - if self.curDisplayMediaPlayer[display].play(display): - self.curDisplayMediaPlayer[display] \ - .set_visible(display, True) - # Start Timer for ui updates - if not self.timer.isActive(): - self.timer.start() - + if not isLive: + return + controller = self.parent.liveController + for display in self.curDisplayMediaPlayer.keys(): + if display.controller != controller or \ + self.curDisplayMediaPlayer[display].state != MediaState.Paused: + continue + if self.curDisplayMediaPlayer[display].play(display): + self.curDisplayMediaPlayer[display].set_visible(display, True) + # Start Timer for ui updates + if not self.timer.isActive(): + self.timer.start() def get_audio_extensions_list(self): audio_list = [] @@ -565,9 +558,8 @@ class MediaController(object): video_list = [] for player in self.mediaPlayers.values(): if player.isActive: - for item in player.video_extensions_list: - if not item in video_list: - video_list.append(item) + video_list.extend([item for item in player.video_extensions_list + if item not in video_list]) return video_list def finalise(self): diff --git a/openlp/core/ui/media/vlcplayer.py b/openlp/core/ui/media/vlcplayer.py index 70a5c1cb5..21f8efedf 100644 --- a/openlp/core/ui/media/vlcplayer.py +++ b/openlp/core/ui/media/vlcplayer.py @@ -26,23 +26,24 @@ ############################################################################### import logging -import sys, os +import os +import sys from datetime import datetime + +from PyQt4 import QtCore, QtGui + +VLC_AVAILABLE = False try: import vlc - vlc_available = bool(vlc.get_default_instance()) + VLC_AVAILABLE = bool(vlc.get_default_instance()) except (ImportError, NameError): - vlc_available = False + pass except OSError, e: if sys.platform.startswith('win'): - if isinstance(e, WindowsError) and e.winerror == 126: - vlc_available = False - else: + if not isinstance(e, WindowsError) and e.winerror != 126: raise else: raise - -from PyQt4 import QtCore, QtGui from openlp.core.lib import Receiver from openlp.core.lib.mediaplayer import MediaPlayer from openlp.core.ui.media import MediaState @@ -128,7 +129,7 @@ class VlcPlayer(MediaPlayer): self.hasOwnWidget = True def check_available(self): - return vlc_available + return VLC_AVAILABLE def load(self, display): log.debug(u'load vid in Vlc Controller') From f01252c23b0d029827d785e1baa0040cb5fd40fb Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 28 Apr 2012 13:31:42 +0200 Subject: [PATCH 04/20] fixed missing word --- openlp/core/ui/media/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/ui/media/__init__.py b/openlp/core/ui/media/__init__.py index ff6f7ae91..5c4428a61 100644 --- a/openlp/core/ui/media/__init__.py +++ b/openlp/core/ui/media/__init__.py @@ -72,7 +72,7 @@ def get_media_players(): the settings. ``players_list`` - Awith all active media players. + A list with all active media players. ``overridden_player`` Here an special media player is chosen for all media actions. From 1869fd7593b3f003da06b8ad7ecd478219f55f6a Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 28 Apr 2012 13:37:39 +0200 Subject: [PATCH 05/20] fixed clean up mistake --- openlp/core/ui/media/mediacontroller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/ui/media/mediacontroller.py b/openlp/core/ui/media/mediacontroller.py index bf5bcf808..b7356fcf5 100644 --- a/openlp/core/ui/media/mediacontroller.py +++ b/openlp/core/ui/media/mediacontroller.py @@ -496,7 +496,7 @@ class MediaController(object): controller = self.parent.liveController for display in self.curDisplayMediaPlayer.keys(): if display.controller != controller or \ - self.curDisplayMediaPlayer[display].state == MediaState.Playing: + self.curDisplayMediaPlayer[display].state != MediaState.Playing: continue self.curDisplayMediaPlayer[display].pause(display) self.curDisplayMediaPlayer[display].set_visible(display, False) From 42bc12f3fb1b908e4b3ee7b572db9fbc12a78bb5 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 28 Apr 2012 14:27:18 +0200 Subject: [PATCH 06/20] try to prevent traceback when vlc too old Fixes: https://launchpad.net/bugs/966086 --- openlp/core/ui/media/vlcplayer.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/openlp/core/ui/media/vlcplayer.py b/openlp/core/ui/media/vlcplayer.py index 21f8efedf..2f3b2a5d7 100644 --- a/openlp/core/ui/media/vlcplayer.py +++ b/openlp/core/ui/media/vlcplayer.py @@ -25,13 +25,19 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### +from datetime import datetime import logging import os import sys -from datetime import datetime from PyQt4 import QtCore, QtGui +from openlp.core.lib import Receiver +from openlp.core.lib.mediaplayer import MediaPlayer +from openlp.core.ui.media import MediaState + +log = logging.getLogger(__name__) + VLC_AVAILABLE = False try: import vlc @@ -44,11 +50,19 @@ except OSError, e: raise else: raise -from openlp.core.lib import Receiver -from openlp.core.lib.mediaplayer import MediaPlayer -from openlp.core.ui.media import MediaState -log = logging.getLogger(__name__) +if VLC_AVAILABLE: + try: + # Older versions of vlc fail here. + vlcInstance = vlc.Instance() + vlcInstance.media_player_new() + except AttributeError: + VLC_AVAILABLE = False + version = u'0.0.0' + try: + version = vlc.libvlc_get_version() + finally: + log.debug(u'VlC could not be loaded: %s' % version) AUDIO_EXT = [ u'*.mp3' From 9dc7d1a7952887c76390dd7e9861df835dfd1239 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 28 Apr 2012 14:28:38 +0200 Subject: [PATCH 07/20] fixed cap --- openlp/core/ui/media/vlcplayer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/ui/media/vlcplayer.py b/openlp/core/ui/media/vlcplayer.py index 2f3b2a5d7..79b972838 100644 --- a/openlp/core/ui/media/vlcplayer.py +++ b/openlp/core/ui/media/vlcplayer.py @@ -62,7 +62,7 @@ if VLC_AVAILABLE: try: version = vlc.libvlc_get_version() finally: - log.debug(u'VlC could not be loaded: %s' % version) + log.debug(u'VLC could not be loaded: %s' % version) AUDIO_EXT = [ u'*.mp3' From 9668b22f859ce157c59324fbd55aa632a7873835 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 28 Apr 2012 15:10:54 +0200 Subject: [PATCH 08/20] shorter solution --- openlp/core/ui/media/vlcplayer.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/openlp/core/ui/media/vlcplayer.py b/openlp/core/ui/media/vlcplayer.py index 79b972838..2e0b250d3 100644 --- a/openlp/core/ui/media/vlcplayer.py +++ b/openlp/core/ui/media/vlcplayer.py @@ -26,6 +26,7 @@ ############################################################################### from datetime import datetime +from distutils.version import LooseVersion import logging import os import sys @@ -53,16 +54,12 @@ except OSError, e: if VLC_AVAILABLE: try: - # Older versions of vlc fail here. - vlcInstance = vlc.Instance() - vlcInstance.media_player_new() - except AttributeError: - VLC_AVAILABLE = False + version = vlc.libvlc_get_version() + except: version = u'0.0.0' - try: - version = vlc.libvlc_get_version() - finally: - log.debug(u'VLC could not be loaded: %s' % version) + if LooseVersion(version) < '1.1.0': + VLC_AVAILABLE = False + log.debug(u'VLC could not be loaded: %s' % version) AUDIO_EXT = [ u'*.mp3' From 69affcc059976c8d9910aed386b0d3e3b11b7a20 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 28 Apr 2012 15:51:05 +0200 Subject: [PATCH 09/20] removed dead code, refactor version comparison code --- openlp/.version | 2 +- openlp/core/__init__.py | 3 +- openlp/core/utils/__init__.py | 62 ++++++++++------------------------- 3 files changed, 19 insertions(+), 48 deletions(-) diff --git a/openlp/.version b/openlp/.version index 998994b7f..eeef06aee 100644 --- a/openlp/.version +++ b/openlp/.version @@ -1 +1 @@ -1.9.5-bzr1421 \ No newline at end of file +1.9.9-bzr1956 diff --git a/openlp/core/__init__.py b/openlp/core/__init__.py index b6efd5595..71c27a1d0 100644 --- a/openlp/core/__init__.py +++ b/openlp/core/__init__.py @@ -49,7 +49,7 @@ from openlp.core.ui.firsttimeform import FirstTimeForm from openlp.core.ui.exceptionform import ExceptionForm from openlp.core.ui import SplashScreen, ScreenList from openlp.core.utils import AppLocation, LanguageManager, VersionThread, \ - get_application_version, DelayStartThread + get_application_version __all__ = [u'OpenLP', u'main'] @@ -145,7 +145,6 @@ class OpenLP(QtGui.QApplication): VersionThread(self.mainWindow).start() Receiver.send_message(u'live_display_blank_check') self.mainWindow.appStartup() - DelayStartThread(self.mainWindow).start() # Skip exec_() for gui tests if not testing: return self.exec_() diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py index e06c5ed32..594ce9623 100644 --- a/openlp/core/utils/__init__.py +++ b/openlp/core/utils/__init__.py @@ -27,14 +27,15 @@ """ The :mod:`openlp.core.utils` module provides the utility libraries for OpenLP. """ +from datetime import datetime +from distutils.version import LooseVersion import logging import os import re +from subprocess import Popen, PIPE import sys import time import urllib2 -from datetime import datetime -from subprocess import Popen, PIPE from PyQt4 import QtGui, QtCore @@ -55,7 +56,6 @@ UNO_CONNECTION_TYPE = u'pipe' #UNO_CONNECTION_TYPE = u'socket' CONTROL_CHARS = re.compile(r'[\x00-\x1F\x7F-\x9F]', re.UNICODE) INVALID_FILE_CHARS = re.compile(r'[\\/:\*\?"<>\|\+\[\]%]', re.UNICODE) -VERSION_SPLITTER = re.compile(r'([0-9]+).([0-9]+).([0-9]+)(?:-bzr([0-9]+))?') class VersionThread(QtCore.QThread): """ @@ -72,49 +72,8 @@ class VersionThread(QtCore.QThread): time.sleep(1) app_version = get_application_version() version = check_latest_version(app_version) - print app_version, version - remote_version = {} - local_version = {} - match = VERSION_SPLITTER.match(version) - if match: - remote_version[u'major'] = int(match.group(1)) - remote_version[u'minor'] = int(match.group(2)) - remote_version[u'release'] = int(match.group(3)) - if len(match.groups()) > 3 and match.group(4): - remote_version[u'revision'] = int(match.group(4)) - else: - return - match = VERSION_SPLITTER.match(app_version[u'full']) - if match: - local_version[u'major'] = int(match.group(1)) - local_version[u'minor'] = int(match.group(2)) - local_version[u'release'] = int(match.group(3)) - if len(match.groups()) > 3 and match.group(4): - local_version[u'revision'] = int(match.group(4)) - else: - return - if remote_version[u'major'] > local_version[u'major'] or \ - remote_version[u'minor'] > local_version[u'minor'] or \ - remote_version[u'release'] > local_version[u'release']: + if LooseVersion(str(version)) > str(app_version[u'full']): Receiver.send_message(u'openlp_version_check', u'%s' % version) - elif remote_version.get(u'revision') and \ - local_version.get(u'revision') and \ - remote_version[u'revision'] > local_version[u'revision']: - Receiver.send_message(u'openlp_version_check', u'%s' % version) - - -class DelayStartThread(QtCore.QThread): - """ - A special Qt thread class to build things after OpenLP has started - """ - def __init__(self, parent): - QtCore.QThread.__init__(self, parent) - - def run(self): - """ - Run the thread. - """ - Receiver.send_message(u'openlp_phonon_creation') class AppLocation(object): @@ -182,6 +141,7 @@ class AppLocation(object): check_directory_exists(path) return path + def _get_os_dir_path(dir_type): """ Return a path based on which OS and environment we are running in. @@ -221,6 +181,7 @@ def _get_os_dir_path(dir_type): u'.openlp', u'data') return os.path.join(unicode(os.getenv(u'HOME'), encoding), u'.openlp') + def _get_frozen_path(frozen_option, non_frozen_option): """ Return a path based on the system status. @@ -229,6 +190,7 @@ def _get_frozen_path(frozen_option, non_frozen_option): return frozen_option return non_frozen_option + def get_application_version(): """ Returns the application version of the running instance of OpenLP:: @@ -308,6 +270,7 @@ def get_application_version(): log.info(u'Openlp version %s' % APPLICATION_VERSION[u'version']) return APPLICATION_VERSION + def check_latest_version(current_version): """ Check the latest version of OpenLP against the version file on the OpenLP @@ -341,6 +304,7 @@ def check_latest_version(current_version): version_string = remote_version return version_string + def add_actions(target, actions): """ Adds multiple actions to a menu or toolbar in one command. @@ -358,6 +322,7 @@ def add_actions(target, actions): else: target.addAction(action) + def get_filesystem_encoding(): """ Returns the name of the encoding used to convert Unicode filenames into @@ -368,6 +333,7 @@ def get_filesystem_encoding(): encoding = sys.getdefaultencoding() return encoding + def get_images_filter(): """ Returns a filter string for a file dialog containing all the supported @@ -384,6 +350,7 @@ def get_images_filter(): visible_formats, actual_formats) return IMAGES_FILTER + def split_filename(path): """ Return a list of the parts in a given path. @@ -394,6 +361,7 @@ def split_filename(path): else: return os.path.split(path) + def clean_filename(filename): """ Removes invalid characters from the given ``filename``. @@ -405,6 +373,7 @@ def clean_filename(filename): filename = unicode(filename, u'utf-8') return INVALID_FILE_CHARS.sub(u'_', CONTROL_CHARS.sub(u'', filename)) + def delete_file(file_path_name): """ Deletes a file from the system. @@ -422,6 +391,7 @@ def delete_file(file_path_name): log.exception("Unable to delete file %s" % file_path_name) return False + def get_web_page(url, header=None, update_openlp=False): """ Attempts to download the webpage at url and returns that page or None. @@ -458,6 +428,7 @@ def get_web_page(url, header=None, update_openlp=False): log.debug(page) return page + def get_uno_command(): """ Returns the UNO command to launch an openoffice.org instance. @@ -470,6 +441,7 @@ def get_uno_command(): CONNECTION = u'"-accept=socket,host=localhost,port=2002;urp;"' return u'%s %s %s' % (COMMAND, OPTIONS, CONNECTION) + def get_uno_instance(resolver): """ Returns a running openoffice.org instance. From 76c004bb9a0d5cfd8d39876641c4f9ef790280a4 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 28 Apr 2012 18:19:02 +0200 Subject: [PATCH 10/20] fixed LooseVersion usage --- openlp/core/utils/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py index 594ce9623..c276c1f8e 100644 --- a/openlp/core/utils/__init__.py +++ b/openlp/core/utils/__init__.py @@ -72,7 +72,7 @@ class VersionThread(QtCore.QThread): time.sleep(1) app_version = get_application_version() version = check_latest_version(app_version) - if LooseVersion(str(version)) > str(app_version[u'full']): + if LooseVersion(str(version)) > LooseVersion(str(app_version[u'full'])): Receiver.send_message(u'openlp_version_check', u'%s' % version) From f6177c62b2de8a083db21d817780f3ae46452f04 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 28 Apr 2012 18:22:37 +0200 Subject: [PATCH 11/20] fixed LooseVersion usage --- openlp/core/ui/media/vlcplayer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/ui/media/vlcplayer.py b/openlp/core/ui/media/vlcplayer.py index 2e0b250d3..6d26f3b34 100644 --- a/openlp/core/ui/media/vlcplayer.py +++ b/openlp/core/ui/media/vlcplayer.py @@ -57,7 +57,7 @@ if VLC_AVAILABLE: version = vlc.libvlc_get_version() except: version = u'0.0.0' - if LooseVersion(version) < '1.1.0': + if LooseVersion(version) < LooseVersion('1.1.0'): VLC_AVAILABLE = False log.debug(u'VLC could not be loaded: %s' % version) From c6c62bdcad6c79f90ca6c7e0558f3a2596103227 Mon Sep 17 00:00:00 2001 From: M2j Date: Sun, 29 Apr 2012 17:31:56 +0200 Subject: [PATCH 12/20] pythonifying code: - replace has_key() by key in dict - remove len() method from sequence and mapping types when mapping to bool --- openlp/core/theme/theme.py | 2 +- openlp/core/ui/mainwindow.py | 2 +- openlp/core/ui/media/mediacontroller.py | 4 +- openlp/core/ui/servicemanager.py | 6 +-- openlp/core/ui/shortcutlistform.py | 12 ++--- openlp/core/ui/slidecontroller.py | 4 +- openlp/core/ui/thememanager.py | 2 +- openlp/core/utils/__init__.py | 2 +- openlp/core/utils/actions.py | 4 +- openlp/plugins/alerts/forms/alertform.py | 2 +- openlp/plugins/bibles/bibleplugin.py | 4 +- .../plugins/bibles/forms/bibleupgradeform.py | 9 ++-- openlp/plugins/bibles/lib/__init__.py | 4 +- openlp/plugins/bibles/lib/http.py | 2 +- openlp/plugins/bibles/lib/mediaitem.py | 6 +-- openlp/plugins/custom/forms/editcustomform.py | 2 +- openlp/plugins/custom/lib/mediaitem.py | 2 +- openlp/plugins/songs/forms/editverseform.py | 6 +-- .../songs/forms/songmaintenanceform.py | 4 +- openlp/plugins/songs/lib/__init__.py | 2 +- openlp/plugins/songs/lib/cclifileimport.py | 2 +- openlp/plugins/songs/lib/easyslidesimport.py | 44 ++++++------------- openlp/plugins/songs/lib/ewimport.py | 10 ++--- openlp/plugins/songs/lib/mediaitem.py | 17 ++++--- openlp/plugins/songs/lib/olp1import.py | 5 +-- openlp/plugins/songs/lib/opensongimport.py | 24 ++++------ openlp/plugins/songs/lib/songimport.py | 15 +++---- .../plugins/songs/lib/songshowplusimport.py | 2 +- openlp/plugins/songs/lib/xml.py | 2 +- openlp/plugins/songs/songsplugin.py | 2 +- 30 files changed, 87 insertions(+), 117 deletions(-) diff --git a/openlp/core/theme/theme.py b/openlp/core/theme/theme.py index 90ca6cf6c..a77ab0c54 100644 --- a/openlp/core/theme/theme.py +++ b/openlp/core/theme/theme.py @@ -209,7 +209,7 @@ class Theme(object): val = int(element_text[1:], 16) except ValueError: # nope pass - elif DELPHI_COLORS.has_key(element_text): + elif element_text in DELPHI_COLORS: val = DELPHI_COLORS[element_text] delphi_color_change = True else: diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 0ebf66aab..e4a4e1616 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -730,7 +730,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): if self.liveController.display.isVisible(): self.liveController.display.setFocus() self.activateWindow() - if len(self.arguments): + if self.arguments: args = [] for a in self.arguments: args.extend([a]) diff --git a/openlp/core/ui/media/mediacontroller.py b/openlp/core/ui/media/mediacontroller.py index 7ccbd9245..73283ea9b 100644 --- a/openlp/core/ui/media/mediacontroller.py +++ b/openlp/core/ui/media/mediacontroller.py @@ -127,7 +127,7 @@ class MediaController(object): invalidMediaPlayers = [mediaPlayer for mediaPlayer in savedPlayers \ if not mediaPlayer in self.mediaPlayers or \ not self.mediaPlayers[mediaPlayer].check_available()] - if len(invalidMediaPlayers) > 0: + if invalidMediaPlayers: for invalidPlayer in invalidMediaPlayers: savedPlayers.remove(invalidPlayer) set_media_players(savedPlayers, overriddenPlayer) @@ -141,7 +141,7 @@ class MediaController(object): Check if there is a running media Player and do updating stuff (e.g. update the UI) """ - if len(self.curDisplayMediaPlayer.keys()) == 0: + if not self.curDisplayMediaPlayer: self.timer.stop() else: for display in self.curDisplayMediaPlayer.keys(): diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index deb2b306f..c9dfeae50 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -483,7 +483,7 @@ class ServiceManager(QtGui.QWidget): service_item = item[u'service_item'].get_service_repr() # Get all the audio files, and ready them for embedding in the # service file. - if len(service_item[u'header'][u'background_audio']) > 0: + if service_item[u'header'][u'background_audio']: for i, filename in \ enumerate(service_item[u'header'][u'background_audio']): new_file = os.path.join(u'audio', @@ -822,7 +822,7 @@ class ServiceManager(QtGui.QWidget): """ Called by the SlideController to select the next service item. """ - if len(self.serviceManagerList.selectedItems()) == 0: + if not self.serviceManagerList.selectedItems(): return selected = self.serviceManagerList.selectedItems()[0] lookFor = 0 @@ -840,7 +840,7 @@ class ServiceManager(QtGui.QWidget): """ Called by the SlideController to select the previous service item. """ - if len(self.serviceManagerList.selectedItems()) == 0: + if not self.serviceManagerList.selectedItems(): return selected = self.serviceManagerList.selectedItems()[0] prevItem = None diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index 211946793..457525dc0 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -151,7 +151,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): if action is None: continue shortcuts = self._actionShortcuts(action) - if len(shortcuts) == 0: + if not shortcuts: item.setText(1, u'') item.setText(2, u'') elif len(shortcuts) == 1: @@ -195,7 +195,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): return shortcuts = self._actionShortcuts(action) new_shortcuts = [] - if len(shortcuts) != 0: + if shortcuts: new_shortcuts.append(shortcuts[0]) new_shortcuts.append( QtGui.QKeySequence(self.alternatePushButton.text())) @@ -241,7 +241,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): self.primaryPushButton.setChecked(False) self.alternatePushButton.setChecked(False) else: - if len(action.defaultShortcuts) != 0: + if action.defaultShortcuts: primary_label_text = action.defaultShortcuts[0].toString() if len(action.defaultShortcuts) == 2: alternate_label_text = action.defaultShortcuts[1].toString() @@ -313,7 +313,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): self.refreshShortcutList() primary_button_text = u'' alternate_button_text = u'' - if len(temp_shortcuts) != 0: + if temp_shortcuts: primary_button_text = temp_shortcuts[0].toString() if len(temp_shortcuts) == 2: alternate_button_text = temp_shortcuts[1].toString() @@ -363,7 +363,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): return shortcuts = self._actionShortcuts(action) new_shortcuts = [] - if len(action.defaultShortcuts) != 0: + if action.defaultShortcuts: new_shortcuts.append(action.defaultShortcuts[0]) # We have to check if the primary default shortcut is available. But # we only have to check, if the action has a default primary @@ -391,7 +391,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): return shortcuts = self._actionShortcuts(action) new_shortcuts = [] - if len(shortcuts) != 0: + if shortcuts: new_shortcuts.append(shortcuts[0]) if len(action.defaultShortcuts) == 2: new_shortcuts.append(action.defaultShortcuts[1]) diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index a1b3a84b2..9de6555f2 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -555,7 +555,7 @@ class SlideController(Controller): Process the service item request queue. The key presses can arrive faster than the processing so implement a FIFO queue. """ - if len(self.keypress_queue): + if self.keypress_queue: while len(self.keypress_queue) and not self.keypress_loop: self.keypress_loop = True keypressCommand = self.keypress_queue.popleft() @@ -694,7 +694,7 @@ class SlideController(Controller): if item.is_text(): if QtCore.QSettings().value( self.parent().songsSettingsSection + u'/display songbar', - QtCore.QVariant(True)).toBool() and len(self.slideList) > 0: + QtCore.QVariant(True)).toBool() and self.slideList: self.songMenu.show() if item.is_capable(ItemCapabilities.CanLoop) and \ len(item.get_frames()) > 1: diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 421346ba9..665c435b9 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -444,7 +444,7 @@ class ThemeManager(QtGui.QWidget): self.firstTime() files = SettingsManager.get_files(self.settingsSection, u'.png') # No themes have been found so create one - if len(files) == 0: + if not files: theme = ThemeXML() theme.theme_name = UiStrings().Default self._writeTheme(theme, None, None) diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py index 1fc75b6d8..f71ec25cd 100644 --- a/openlp/core/utils/__init__.py +++ b/openlp/core/utils/__init__.py @@ -267,7 +267,7 @@ def get_application_version(): if code != 0: raise Exception(u'Error running bzr tags') lines = output.splitlines() - if len(lines) == 0: + if not lines: tag = u'0.0.0' revision = u'0' else: diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index 1a7d99fa5..271a7c884 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -90,7 +90,7 @@ class CategoryActionList(object): def append(self, name): weight = 0 - if len(self.actions) > 0: + if self.actions: weight = self.actions[-1][0] + 1 self.add(name, weight) @@ -156,7 +156,7 @@ class CategoryList(object): def append(self, name, actions=None): weight = 0 - if len(self.categories) > 0: + if self.categories: weight = self.categories[-1].weight + 1 if actions: self.add(name, weight, actions) diff --git a/openlp/plugins/alerts/forms/alertform.py b/openlp/plugins/alerts/forms/alertform.py index d2d4813a1..4f7633062 100644 --- a/openlp/plugins/alerts/forms/alertform.py +++ b/openlp/plugins/alerts/forms/alertform.py @@ -101,7 +101,7 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog): self.alertTextEdit.setText(u'') def onNewClick(self): - if len(self.alertTextEdit.text()) == 0: + if not self.alertTextEdit.text(): QtGui.QMessageBox.information(self, translate('AlertsPlugin.AlertForm', 'New Alert'), translate('AlertsPlugin.AlertForm', 'You haven\'t specified ' diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py index 06d8b1c98..0dddab214 100644 --- a/openlp/plugins/bibles/bibleplugin.py +++ b/openlp/plugins/bibles/bibleplugin.py @@ -62,7 +62,7 @@ class BiblePlugin(Plugin): # unicode(UiStrings().Export)) # Set to invisible until we can export bibles self.exportBibleItem.setVisible(False) - if len(self.manager.old_bible_databases): + if self.manager.old_bible_databases: self.toolsUpgradeItem.setVisible(True) def finalise(self): @@ -83,7 +83,7 @@ class BiblePlugin(Plugin): """ Perform tasks on application startup """ - if len(self.manager.old_bible_databases): + if self.manager.old_bible_databases: if QtGui.QMessageBox.information(self.formParent, translate('OpenLP', 'Information'), translate('OpenLP', 'Bible format has changed.\nYou have to upgrade your ' diff --git a/openlp/plugins/bibles/forms/bibleupgradeform.py b/openlp/plugins/bibles/forms/bibleupgradeform.py index 0ef418de8..88bbd8c63 100644 --- a/openlp/plugins/bibles/forms/bibleupgradeform.py +++ b/openlp/plugins/bibles/forms/bibleupgradeform.py @@ -426,8 +426,7 @@ class BibleUpgradeForm(OpenLPWizard): if meta[u'key'] == u'download_source': web_bible = True self.includeWebBible = True - if meta.has_key(u'proxy_server'): - proxy_server = meta[u'proxy_server'] + proxy_server = meta.get(u'proxy_server') if web_bible: if meta_data[u'download_source'].lower() == u'crosswalk': handler = CWExtract(proxy_server) @@ -572,7 +571,7 @@ class BibleUpgradeForm(OpenLPWizard): int(verse[u'verse']), unicode(verse[u'text'])) Receiver.send_message(u'openlp_process_events') self.newbibles[number].session.commit() - if self.success.has_key(number) and not self.success[number]: + if not self.success.get(number, True): self.incrementProgressBar(unicode(translate( 'BiblesPlugin.UpgradeWizardForm', 'Upgrading Bible %s of %s: "%s"\nFailed')) % @@ -586,7 +585,7 @@ class BibleUpgradeForm(OpenLPWizard): 'Upgrading Bible %s of %s: "%s"\n' 'Complete')) % (number + 1, max_bibles, name)) - if self.newbibles.has_key(number): + if number in self.newbibles: self.newbibles[number].session.close() # Close the last bible's connection if possible. if old_bible is not None: @@ -599,7 +598,7 @@ class BibleUpgradeForm(OpenLPWizard): successful_import = 0 failed_import = 0 for number, filename in enumerate(self.files): - if self.success.has_key(number) and self.success[number]: + if self.success.get(number): successful_import += 1 elif self.checkBox[number].checkState() == QtCore.Qt.Checked: failed_import += 1 diff --git a/openlp/plugins/bibles/lib/__init__.py b/openlp/plugins/bibles/lib/__init__.py index 4f8582bdf..6cd8b8d8e 100644 --- a/openlp/plugins/bibles/lib/__init__.py +++ b/openlp/plugins/bibles/lib/__init__.py @@ -236,7 +236,7 @@ def get_reference_separator(separator_type): ``separator_type`` The role and format of the separator. """ - if len(REFERENCE_SEPARATORS) == 0: + if not REFERENCE_SEPARATORS: update_reference_separators() return REFERENCE_SEPARATORS[separator_type] @@ -247,7 +247,7 @@ def get_reference_match(match_type): ``match_type`` The type of match is ``range_separator``, ``range`` or ``full``. """ - if len(REFERENCE_MATCHES) == 0: + if not REFERENCE_MATCHES: update_reference_separators() return REFERENCE_MATCHES[match_type] diff --git a/openlp/plugins/bibles/lib/http.py b/openlp/plugins/bibles/lib/http.py index 302bdf999..fb79b26e1 100644 --- a/openlp/plugins/bibles/lib/http.py +++ b/openlp/plugins/bibles/lib/http.py @@ -106,7 +106,7 @@ class BGExtract(object): verse_list = {} # Cater for inconsistent mark up in the first verse of a chapter. first_verse = verses.find(u'versenum') - if first_verse and len(first_verse.contents): + if first_verse and first_verse.contents: verse_list[1] = unicode(first_verse.contents[0]) for verse in verses(u'sup', u'versenum'): raw_verse_num = verse.next diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index e3d173fad..bc456ee69 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -392,7 +392,7 @@ class BibleMediaItem(MediaManagerItem): if bible in bibles: find_and_set_in_combo_box(self.advancedVersionComboBox, bible) self.initialiseAdvancedBible(unicode(bible)) - elif len(bibles): + elif bibles: self.initialiseAdvancedBible(bibles[0]) bible = QtCore.QSettings().value( self.settingsSection + u'/quick bible', QtCore.QVariant( @@ -878,7 +878,7 @@ class BibleMediaItem(MediaManagerItem): items = item else: items = self.listView.selectedItems() - if len(items) == 0: + if not items: return False bible_text = u'' old_item = None @@ -949,7 +949,7 @@ class BibleMediaItem(MediaManagerItem): # Service Item: Title service_item.title = create_separated_list(raw_title) # Service Item: Theme - if len(self.settings.bible_theme) == 0: + if not self.settings.bible_theme: service_item.theme = None else: service_item.theme = self.settings.bible_theme diff --git a/openlp/plugins/custom/forms/editcustomform.py b/openlp/plugins/custom/forms/editcustomform.py index 7c2da3bb0..a113a9e1b 100644 --- a/openlp/plugins/custom/forms/editcustomform.py +++ b/openlp/plugins/custom/forms/editcustomform.py @@ -254,7 +254,7 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog): Checks whether a custom is valid or not. """ # We must have a title. - if len(self.titleEdit.displayText()) == 0: + if not self.titleEdit.displayText(): self.titleEdit.setFocus() critical_error_message_box( message=translate('CustomPlugin.EditCustomForm', diff --git a/openlp/plugins/custom/lib/mediaitem.py b/openlp/plugins/custom/lib/mediaitem.py index 541403cda..af2f261ca 100644 --- a/openlp/plugins/custom/lib/mediaitem.py +++ b/openlp/plugins/custom/lib/mediaitem.py @@ -258,7 +258,7 @@ class CustomMediaItem(MediaManagerItem): search_length = 2 if len(text) > search_length: self.onSearchTextButtonClicked() - elif len(text) == 0: + elif not text: self.onClearTextButtonClick() def onClearTextButtonClick(self): diff --git a/openlp/plugins/songs/forms/editverseform.py b/openlp/plugins/songs/forms/editverseform.py index 1e7bf4375..21285f39d 100644 --- a/openlp/plugins/songs/forms/editverseform.py +++ b/openlp/plugins/songs/forms/editverseform.py @@ -191,13 +191,13 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog): else: log.debug(unicode(self.getVerse()[0]).split(u'\n')) value = unicode(self.getVerse()[0]).split(u'\n')[1] - if len(value) == 0: + if not value: lines = unicode(self.getVerse()[0]).split(u'\n') index = 2 - while index < len(lines) and len(value) == 0: + while index < len(lines) and not value: value = lines[index] index += 1 - if len(value) == 0: + if not value: critical_error_message_box( message=translate('SongsPlugin.EditSongForm', 'You need to type some text in to the verse.')) diff --git a/openlp/plugins/songs/forms/songmaintenanceform.py b/openlp/plugins/songs/forms/songmaintenanceform.py index 697bcb4fe..7ba49a102 100644 --- a/openlp/plugins/songs/forms/songmaintenanceform.py +++ b/openlp/plugins/songs/forms/songmaintenanceform.py @@ -108,7 +108,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): item_id = self._getCurrentItemId(list_widget) if item_id != -1: item = self.manager.get_object(item_class, item_id) - if item and len(item.songs) == 0: + if item and not item.songs: if critical_error_message_box(dlg_title, del_text, self, True) == QtGui.QMessageBox.Yes: self.manager.delete_object(item_class, item.id) @@ -191,7 +191,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): ``edit`` If we edit an item, this should be *True*. """ - if len(objects) > 0: + if objects: # If we edit an existing object, we need to make sure that we do # not return False when nothing has changed. if edit: diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py index 91e21bb19..44cf8e113 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -316,7 +316,7 @@ def clean_song(manager, song): verse_type, verse[0][u'label'], verse[1], - verse[0][u'lang'] if verse[0].has_key(u'lang') else None + verse[0].get(u'lang') ) compare_order.append((u'%s%s' % (verse_type, verse[0][u'label']) ).upper()) diff --git a/openlp/plugins/songs/lib/cclifileimport.py b/openlp/plugins/songs/lib/cclifileimport.py index 6b99f9a16..c2df6ab4e 100644 --- a/openlp/plugins/songs/lib/cclifileimport.py +++ b/openlp/plugins/songs/lib/cclifileimport.py @@ -211,7 +211,7 @@ class CCLIFileImport(SongImport): elif verse_lines[0].startswith(u'('): verse_type = VerseType.Tags[VerseType.Other] verse_text = verse_lines[1] - if len(verse_text) > 0: + if verse_text: self.addVerse(verse_text, verse_type) check_first_verse_line = False # Handle multiple authors diff --git a/openlp/plugins/songs/lib/easyslidesimport.py b/openlp/plugins/songs/lib/easyslidesimport.py index 206c127f5..eb21e6fc2 100644 --- a/openlp/plugins/songs/lib/easyslidesimport.py +++ b/openlp/plugins/songs/lib/easyslidesimport.py @@ -162,15 +162,12 @@ class EasySlidesImport(SongImport): separatorlines = 0 for line in lines: line = line.strip() - if len(line) == 0: + if not line: continue elif line[1:7] == u'region': # this is region separator, probably [region 2] region = self._extractRegion(line) - if regionlines.has_key(region): - regionlines[region] = regionlines[region] + 1 - else: - regionlines[region] = 1 + regionlines[region] = 1 + regionlines.get(region, 0) elif line[0] == u'[': separatorlines = separatorlines + 1 # if the song has separators @@ -206,7 +203,7 @@ class EasySlidesImport(SongImport): for line in lines: line = line.strip() - if len(line) == 0: + if not line: if separators: # separators are used, so empty line means slide break # inside verse @@ -215,15 +212,11 @@ class EasySlidesImport(SongImport): else: # separators are not used, so empty line starts a new verse vt = u'V' - if verses[reg].has_key(vt): - vn = len(verses[reg][vt].keys())+1 - else: - vn = u'1' + vn = len(verses[reg].get(vt, {})) + 1 inst = 1 elif line[0:7] == u'[region': reg = self._extractRegion(line) - if not verses.has_key(reg): - verses[reg] = {} + verses.setdefault(reg, {}) if not regionsInVerses: vt = u'V' vn = u'1' @@ -238,12 +231,7 @@ class EasySlidesImport(SongImport): if match: marker = match.group(1).strip() vn = match.group(2) - if len(marker) == 0: - vt = u'V' - elif MarkTypes.has_key(marker): - vt = MarkTypes[marker] - else: - vt = u'O' + vt = MarkTypes.get(marker, u'O') if marker else u'V' if regionsInVerses: region = defaultregion inst = 1 @@ -252,14 +240,10 @@ class EasySlidesImport(SongImport): else: if not [reg, vt, vn, inst] in our_verse_order: our_verse_order.append([reg, vt, vn, inst]) - if not verses[reg].has_key(vt): - verses[reg][vt] = {} - if not verses[reg][vt].has_key(vn): - verses[reg][vt][vn] = {} - if not verses[reg][vt][vn].has_key(inst): - verses[reg][vt][vn][inst] = [] - words = self.tidyText(line) - verses[reg][vt][vn][inst].append(words) + verses[reg].setdefault(vt, {}) + verses[reg][vt].setdefault(vn, {}) + verses[reg][vt][vn].setdefault(inst, []) + verses[reg][vt][vn][inst].append(self.tidyText(line)) # done parsing versetags = [] @@ -286,11 +270,11 @@ class EasySlidesImport(SongImport): try: order = unicode(song.Sequence).strip().split(u',') for tag in order: - if len(tag) == 0: + if not tag: continue elif tag[0].isdigit(): tag = u'V' + tag - elif SeqTypes.has_key(tag.lower()): + elif tag.lower() in SeqTypes: tag = SeqTypes[tag.lower()] else: continue @@ -307,9 +291,7 @@ class EasySlidesImport(SongImport): def _listHas(self, lst, subitems): for subitem in subitems: - if isinstance(lst, dict) and lst.has_key(subitem): - lst = lst[subitem] - elif isinstance(lst, list) and subitem in lst: + if subitem in lst: lst = lst[subitem] else: return False diff --git a/openlp/plugins/songs/lib/ewimport.py b/openlp/plugins/songs/lib/ewimport.py index f17900fd5..d58734610 100644 --- a/openlp/plugins/songs/lib/ewimport.py +++ b/openlp/plugins/songs/lib/ewimport.py @@ -62,15 +62,15 @@ def strip_rtf(blob, encoding): if control: # for delimiters, set control to False if c == '{': - if len(control_word) > 0: + if control_word: depth += 1 control = False elif c == '}': - if len(control_word) > 0: + if control_word: depth -= 1 control = False elif c == '\\': - new_control = (len(control_word) > 0) + new_control = bool(control_word) control = False elif c.isspace(): control = False @@ -79,7 +79,7 @@ def strip_rtf(blob, encoding): if len(control_word) == 3 and control_word[0] == '\'': control = False if not control: - if len(control_word) == 0: + if not control_word: if c == '{' or c == '}' or c == '\\': clear_text.append(c) else: @@ -360,7 +360,7 @@ class EasyWorshipSongImport(SongImport): field_desc = self.fieldDescs[field_desc_index] # Return None in case of 'blank' entries if isinstance(field, str): - if len(field.rstrip('\0')) == 0: + if not field.rstrip('\0'): return None elif field == 0: return None diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 2836e98ad..51158a954 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -295,9 +295,8 @@ class SongMediaItem(MediaManagerItem): log.debug(u'display results Book') self.listView.clear() for book in searchresults: - songs = sorted(book.songs, key=lambda song: int( - re.sub(r'[^0-9]', u' ', song.song_number).partition(' ')[0]) - if len(re.sub(r'[^\w]', ' ', song.song_number)) else 0) + songs = sorted(book.songs, key=lambda song: + int(re.match(r'[0-9]+', u'0' + song.song_number).group())) for song in songs: # Do not display temporary songs if song.temporary: @@ -331,7 +330,7 @@ class SongMediaItem(MediaManagerItem): search_length = 3 if len(text) > search_length: self.onSearchTextButtonClicked() - elif len(text) == 0: + elif not text: self.onClearTextButtonClick() def onImportClick(self): @@ -491,7 +490,7 @@ class SongMediaItem(MediaManagerItem): else: # Loop through the verse list and expand the song accordingly. for order in song.verse_order.lower().split(): - if len(order) == 0: + if not order: break for verse in verseList: if verse[0][u'type'][0].lower() == order[0] and \ @@ -530,7 +529,7 @@ class SongMediaItem(MediaManagerItem): u'authors': u', '.join(author_list)} service_item.xml_version = self.openLyrics.song_to_xml(song) # Add the audio file to the service item. - if len(song.media_files) > 0: + if song.media_files: service_item.add_capability(ItemCapabilities.HasBackgroundAudio) service_item.background_audio = \ [m.file_name for m in song.media_files] @@ -575,12 +574,12 @@ class SongMediaItem(MediaManagerItem): editId = song.id break # If there's any backing tracks, copy them over. - if len(item.background_audio) > 0: + if item.background_audio: self._updateBackgroundAudio(song, item) if add_song and self.addSongFromService: song = self.openLyrics.xml_to_song(item.xml_version) # If there's any backing tracks, copy them over. - if len(item.background_audio) > 0: + if item.background_audio: self._updateBackgroundAudio(song, item) editId = song.id self.onSearchTextButtonClicked() @@ -588,7 +587,7 @@ class SongMediaItem(MediaManagerItem): # Make sure we temporary import formatting tags. song = self.openLyrics.xml_to_song(item.xml_version, True) # If there's any backing tracks, copy them over. - if len(item.background_audio) > 0: + if item.background_audio: self._updateBackgroundAudio(song, item) editId = song.id temporary = True diff --git a/openlp/plugins/songs/lib/olp1import.py b/openlp/plugins/songs/lib/olp1import.py index 05095aa31..2d744bd2a 100644 --- a/openlp/plugins/songs/lib/olp1import.py +++ b/openlp/plugins/songs/lib/olp1import.py @@ -122,8 +122,7 @@ class OpenLP1SongImport(SongImport): cursor.execute( u'SELECT settingsid FROM songs WHERE songid = %s' % song_id) theme_id = cursor.fetchone()[0] - if themes.has_key(theme_id): - self.themeName = themes[theme_id] + self.themeName = themes.get(theme_id, u'') verses = lyrics.split(u'\n\n') for verse in verses: if verse.strip(): @@ -191,7 +190,7 @@ class OpenLP1SongImport(SongImport): # Detect charset by songs. cursor.execute(u'SELECT name FROM sqlite_master ' u'WHERE type = \'table\' AND name = \'tracks\'') - if len(cursor.fetchall()) > 0: + if cursor.fetchall(): cursor.execute(u'SELECT fulltrackname FROM tracks') tracks = cursor.fetchall() for track in tracks: diff --git a/openlp/plugins/songs/lib/opensongimport.py b/openlp/plugins/songs/lib/opensongimport.py index ad45ef22e..f78ba474b 100644 --- a/openlp/plugins/songs/lib/opensongimport.py +++ b/openlp/plugins/songs/lib/opensongimport.py @@ -174,7 +174,7 @@ class OpenSongImport(SongImport): if semicolon >= 0: this_line = this_line[:semicolon] this_line = this_line.strip() - if not len(this_line): + if not this_line: continue # skip guitar chords and page and column breaks if this_line.startswith(u'.') or this_line.startswith(u'---') \ @@ -197,15 +197,12 @@ class OpenSongImport(SongImport): # the verse tag verse_tag = content verse_num = u'1' - if len(verse_tag) == 0: - verse_index = 0 - else: - verse_index = VerseType.from_loose_input(verse_tag) + verse_index = VerseType.from_loose_input(verse_tag) \ + if verse_tag else 0 verse_tag = VerseType.Tags[verse_index] inst = 1 if [verse_tag, verse_num, inst] in our_verse_order \ - and verses.has_key(verse_tag) \ - and verses[verse_tag].has_key(verse_num): + and verse_num in verses.get(verse_tag, {}): inst = len(verses[verse_tag][verse_num]) + 1 continue # number at start of line.. it's verse number @@ -213,11 +210,9 @@ class OpenSongImport(SongImport): verse_num = this_line[0] this_line = this_line[1:].strip() our_verse_order.append([verse_tag, verse_num, inst]) - if not verses.has_key(verse_tag): - verses[verse_tag] = {} - if not verses[verse_tag].has_key(verse_num): - verses[verse_tag][verse_num] = {} - if not verses[verse_tag][verse_num].has_key(inst): + verses.setdefault(verse_tag, {}) + verses[verse_tag].setdefault(verse_num, {}) + if inst not in verses[verse_tag][verse_num]: verses[verse_tag][verse_num][inst] = [] our_verse_order.append([verse_tag, verse_num, inst]) # Tidy text and remove the ____s from extended words @@ -252,15 +247,14 @@ class OpenSongImport(SongImport): if match is not None: verse_tag = match.group(1) verse_num = match.group(2) - if not len(verse_tag): + if not verse_tag: verse_tag = VerseType.Tags[VerseType.Verse] else: # Assume it's no.1 if there are no digits verse_tag = verse_def verse_num = u'1' verse_def = u'%s%s' % (verse_tag, verse_num) - if verses.has_key(verse_tag) and \ - verses[verse_tag].has_key(verse_num): + if verse_num in verses.get(verse_tag, {}): self.verseOrderList.append(verse_def) else: log.info(u'Got order %s but not in verse tags, dropping' diff --git a/openlp/plugins/songs/lib/songimport.py b/openlp/plugins/songs/lib/songimport.py index 6fd9dd403..74767d793 100644 --- a/openlp/plugins/songs/lib/songimport.py +++ b/openlp/plugins/songs/lib/songimport.py @@ -61,9 +61,9 @@ class SongImport(QtCore.QObject): """ self.manager = manager QtCore.QObject.__init__(self) - if kwargs.has_key(u'filename'): + if u'filename' in kwargs: self.importSource = kwargs[u'filename'] - elif kwargs.has_key(u'filenames'): + elif u'filenames' in kwargs: self.importSource = kwargs[u'filenames'] else: raise KeyError(u'Keyword arguments "filename[s]" not supplied.') @@ -273,7 +273,7 @@ class SongImport(QtCore.QObject): Author not checked here, if no author then "Author unknown" is automatically added """ - if not self.title or not len(self.verses): + if not self.title or not self.verses: return False else: return True @@ -314,13 +314,10 @@ class SongImport(QtCore.QObject): verse_def = new_verse_def sxml.add_verse_to_lyrics(verse_tag, verse_def[1:], verse_text, lang) song.lyrics = unicode(sxml.extract_xml(), u'utf-8') - if not len(self.verseOrderList) and \ - self.verseOrderListGeneratedUseful: + if not self.verseOrderList and self.verseOrderListGeneratedUseful: self.verseOrderList = self.verseOrderListGenerated - for i, current_verse_def in enumerate(self.verseOrderList): - if verses_changed_to_other.has_key(current_verse_def): - self.verseOrderList[i] = \ - verses_changed_to_other[current_verse_def] + self.verseOrderList = map(lambda v: verses_changed_to_other.get(v, v), + self.verseOrderList) song.verse_order = u' '.join(self.verseOrderList) song.copyright = self.copyright song.comments = self.comments diff --git a/openlp/plugins/songs/lib/songshowplusimport.py b/openlp/plugins/songs/lib/songshowplusimport.py index 8d2af5514..f86e061ca 100644 --- a/openlp/plugins/songs/lib/songshowplusimport.py +++ b/openlp/plugins/songs/lib/songshowplusimport.py @@ -204,7 +204,7 @@ class SongShowPlusImport(SongImport): elif verse_type == "pre-chorus": verse_tag = VerseType.Tags[VerseType.PreChorus] else: - if not self.otherList.has_key(verse_name): + if verse_name not in self.otherList: if ignore_unique: return None self.otherCount = self.otherCount + 1 diff --git a/openlp/plugins/songs/lib/xml.py b/openlp/plugins/songs/lib/xml.py index 816742d11..fdcb1dd60 100644 --- a/openlp/plugins/songs/lib/xml.py +++ b/openlp/plugins/songs/lib/xml.py @@ -611,7 +611,7 @@ class OpenLyrics(object): text += u'{%s}' % element.get(u'name') # Some formattings may have only start tag. # Handle this case if element has no children and contains no text. - if len(element) == 0 and not element.text: + if not element and not element.text: use_endtag = False # Append text from element. if element.text: diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index f55ac7a5f..4d59186e5 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -239,7 +239,7 @@ class SongsPlugin(Plugin): for sfile in os.listdir(db_dir): if sfile.startswith(u'songs_') and sfile.endswith(u'.sqlite'): song_dbs.append(os.path.join(db_dir, sfile)) - if len(song_dbs) == 0: + if not song_dbs: return progress = QtGui.QProgressDialog(self.formParent) progress.setWindowModality(QtCore.Qt.WindowModal) From 41402ab0789d744dc726e9e45d88b8892c171765 Mon Sep 17 00:00:00 2001 From: M2j Date: Sun, 29 Apr 2012 18:01:15 +0200 Subject: [PATCH 13/20] add a space character --- openlp/plugins/songs/lib/easyslidesimport.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/plugins/songs/lib/easyslidesimport.py b/openlp/plugins/songs/lib/easyslidesimport.py index eb21e6fc2..30d12964a 100644 --- a/openlp/plugins/songs/lib/easyslidesimport.py +++ b/openlp/plugins/songs/lib/easyslidesimport.py @@ -236,7 +236,7 @@ class EasySlidesImport(SongImport): region = defaultregion inst = 1 if self._listHas(verses, [reg, vt, vn, inst]): - inst = len(verses[reg][vt][vn])+1 + inst = len(verses[reg][vt][vn]) + 1 else: if not [reg, vt, vn, inst] in our_verse_order: our_verse_order.append([reg, vt, vn, inst]) From bd4cded398df67b3de98e821ed90d8c9585acaa2 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Tue, 1 May 2012 12:38:19 +0200 Subject: [PATCH 14/20] updated vlc.py --- openlp/core/ui/media/vlc.py | 61 ++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/openlp/core/ui/media/vlc.py b/openlp/core/ui/media/vlc.py index 6ad30a3e4..1f6aa0e6a 100644 --- a/openlp/core/ui/media/vlc.py +++ b/openlp/core/ui/media/vlc.py @@ -2,25 +2,26 @@ # Python ctypes bindings for VLC # -# Copyright (C) 2009-2010 the VideoLAN team +# Copyright (C) 2009-2012 the VideoLAN team # $Id: $ # # Authors: Olivier Aubert # Jean Brouwers +# Geoff Salmon # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation; either version 2.1 of the +# License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA """This module provides bindings for the LibVLC public API, see U{http://wiki.videolan.org/LibVLC}. @@ -47,7 +48,7 @@ import sys from inspect import getargspec __version__ = "N/A" -build_date = "Tue Jan 17 12:20:48 2012" +build_date = "Fri Apr 27 16:47:21 2012" # Internal guard to prevent internal classes to be directly # instanciated. @@ -126,7 +127,7 @@ class VLCException(Exception): try: _Ints = (int, long) except NameError: # no long in Python 3+ - _Ints = int + _Ints = int _Seqs = (list, tuple) # Default instance. It is used to instanciate classes directly in the @@ -904,6 +905,11 @@ class Instance(_Ctype): def media_new(self, mrl, *options): """Create a new Media instance. + If mrl contains a colon (:), it will be treated as a + URL. Else, it will be considered as a local path. If you need + more control, directly use media_new_location/media_new_path + methods. + Options can be specified as supplementary string parameters, e.g. C{m = i.media_new('foo.avi', 'sub-filter=marq{marquee=Hello}', 'vout-filter=invert')} @@ -914,7 +920,12 @@ class Instance(_Ctype): @param options: optional media option=value strings """ - m = libvlc_media_new_location(self, mrl) + if ':' in mrl: + # Assume it is a URL + m = libvlc_media_new_location(self, mrl) + else: + # Else it should be a local path. + m = libvlc_media_new_path(self, mrl) for o in options: libvlc_media_add_option(m, o) m._instance = self @@ -1511,7 +1522,7 @@ class Media(_Ctype): def save_meta(self): '''Save the meta previously set. - @return: true if the write operation was successfull. + @return: true if the write operation was successful. ''' return libvlc_media_save_meta(self) @@ -2230,7 +2241,7 @@ class MediaPlayer(_Ctype): @param format: a four-characters string identifying the sample format (e.g. "S16N" or "FL32"). @param rate: sample rate (expressed in Hz). @param channels: channels count. - @version: LibVLC 1.2.0 or later. + @version: LibVLC 2.0.0 or later. ''' return libvlc_audio_set_format(self, format, rate, channels) @@ -2378,7 +2389,7 @@ class MediaPlayer(_Ctype): def navigate(self, navigate): '''Navigate through DVD Menu. @param navigate: the Navigation mode. - @version: libVLC 1.2.0 or later. + @version: libVLC 2.0.0 or later. ''' return libvlc_media_player_navigate(self, navigate) @@ -2489,7 +2500,7 @@ class MediaPlayer(_Ctype): '''Get the current subtitle delay. Positive values means subtitles are being displayed later, negative values earlier. @return: time (in microseconds) the display of subtitles is being delayed. - @version: LibVLC 1.2.0 or later. + @version: LibVLC 2.0.0 or later. ''' return libvlc_video_get_spu_delay(self) @@ -2500,7 +2511,7 @@ class MediaPlayer(_Ctype): The subtitle delay will be reset to zero each time the media changes. @param i_delay: time (in microseconds) the display of subtitles should be delayed. @return: 0 on success, -1 on error. - @version: LibVLC 1.2.0 or later. + @version: LibVLC 2.0.0 or later. ''' return libvlc_video_set_spu_delay(self, i_delay) @@ -3247,7 +3258,7 @@ def libvlc_media_set_meta(p_md, e_meta, psz_value): def libvlc_media_save_meta(p_md): '''Save the meta previously set. @param p_md: the media desriptor. - @return: true if the write operation was successfull. + @return: true if the write operation was successful. ''' f = _Cfunctions.get('libvlc_media_save_meta', None) or \ _Cfunction('libvlc_media_save_meta', ((1,),), None, @@ -4084,7 +4095,7 @@ def libvlc_audio_set_format(mp, format, rate, channels): @param format: a four-characters string identifying the sample format (e.g. "S16N" or "FL32"). @param rate: sample rate (expressed in Hz). @param channels: channels count. - @version: LibVLC 1.2.0 or later. + @version: LibVLC 2.0.0 or later. ''' f = _Cfunctions.get('libvlc_audio_set_format', None) or \ _Cfunction('libvlc_audio_set_format', ((1,), (1,), (1,), (1,),), None, @@ -4328,7 +4339,7 @@ def libvlc_media_player_navigate(p_mi, navigate): '''Navigate through DVD Menu. @param p_mi: the Media Player. @param navigate: the Navigation mode. - @version: libVLC 1.2.0 or later. + @version: libVLC 2.0.0 or later. ''' f = _Cfunctions.get('libvlc_media_player_navigate', None) or \ _Cfunction('libvlc_media_player_navigate', ((1,), (1,),), None, @@ -4554,7 +4565,7 @@ def libvlc_video_get_spu_delay(p_mi): displayed later, negative values earlier. @param p_mi: media player. @return: time (in microseconds) the display of subtitles is being delayed. - @version: LibVLC 1.2.0 or later. + @version: LibVLC 2.0.0 or later. ''' f = _Cfunctions.get('libvlc_video_get_spu_delay', None) or \ _Cfunction('libvlc_video_get_spu_delay', ((1,),), None, @@ -4569,7 +4580,7 @@ def libvlc_video_set_spu_delay(p_mi, i_delay): @param p_mi: media player. @param i_delay: time (in microseconds) the display of subtitles should be delayed. @return: 0 on success, -1 on error. - @version: LibVLC 1.2.0 or later. + @version: LibVLC 2.0.0 or later. ''' f = _Cfunctions.get('libvlc_video_set_spu_delay', None) or \ _Cfunction('libvlc_video_set_spu_delay', ((1,), (1,),), None, From a0225d790a4da34255e7bf53772ad263bd9d68d4 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Tue, 1 May 2012 13:00:02 +0200 Subject: [PATCH 15/20] replaced numScreens by screenCount; removed 0 in range() --- openlp/core/lib/mediamanageritem.py | 6 +++--- openlp/core/ui/mediadockmanager.py | 4 ++-- openlp/core/ui/screen.py | 6 +++--- openlp/core/ui/settingsform.py | 4 ++-- openlp/core/ui/slidecontroller.py | 2 +- openlp/plugins/custom/forms/editcustomform.py | 6 +++--- .../presentations/lib/powerpointcontroller.py | 2 +- openlp/plugins/songs/forms/editsongform.py | 12 ++++++------ openlp/plugins/songs/lib/cclifileimport.py | 2 +- 9 files changed, 22 insertions(+), 22 deletions(-) diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 0fff153a3..9bd6fc2a7 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -373,12 +373,12 @@ class MediaManagerItem(QtGui.QWidget): Process a list for files either from the File Dialog or from Drag and Drop - ``files`` - The files to be loaded + ``files`` + The files to be loaded. """ names = [] fullList = [] - for count in range(0, self.listView.count()): + for count in range(self.listView.count()): names.append(unicode(self.listView.item(count).text())) fullList.append(unicode(self.listView.item(count). data(QtCore.Qt.UserRole).toString())) diff --git a/openlp/core/ui/mediadockmanager.py b/openlp/core/ui/mediadockmanager.py index 1b8666cd3..020b79257 100644 --- a/openlp/core/ui/mediadockmanager.py +++ b/openlp/core/ui/mediadockmanager.py @@ -64,7 +64,7 @@ class MediaDockManager(object): visible_title = media_item.plugin.getString(StringContent.VisibleName) log.debug(u'Inserting %s dock' % visible_title[u'title']) match = False - for dock_index in range(0, self.media_dock.count()): + for dock_index in range(self.media_dock.count()): if self.media_dock.widget(dock_index).settingsSection == \ media_item.plugin.name: match = True @@ -81,7 +81,7 @@ class MediaDockManager(object): """ visible_title = media_item.plugin.getString(StringContent.VisibleName) log.debug(u'remove %s dock' % visible_title[u'title']) - for dock_index in range(0, self.media_dock.count()): + for dock_index in range(self.media_dock.count()): if self.media_dock.widget(dock_index): if self.media_dock.widget(dock_index).settingsSection == \ media_item.plugin.name: diff --git a/openlp/core/ui/screen.py b/openlp/core/ui/screen.py index d576f0e58..21fbd6144 100644 --- a/openlp/core/ui/screen.py +++ b/openlp/core/ui/screen.py @@ -106,13 +106,13 @@ class ScreenList(object): """ # Do not log at start up. if changed_screen != -1: - log.info(u'screen_count_changed %d' % self.desktop.numScreens()) + log.info(u'screen_count_changed %d' % self.desktop.screenCount()) # Remove unplugged screens. for screen in copy.deepcopy(self.screen_list): - if screen[u'number'] == self.desktop.numScreens(): + if screen[u'number'] == self.desktop.screenCount(): self.remove_screen(screen[u'number']) # Add new screens. - for number in xrange(0, self.desktop.numScreens()): + for number in xrange(self.desktop.screenCount()): if not self.screen_exists(number): self.add_screen({ u'number': number, diff --git a/openlp/core/ui/settingsform.py b/openlp/core/ui/settingsform.py index b6808030a..7dd02826c 100644 --- a/openlp/core/ui/settingsform.py +++ b/openlp/core/ui/settingsform.py @@ -92,7 +92,7 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog): """ Process the form saving the settings """ - for tabIndex in range(0, self.stackedLayout.count()): + for tabIndex in range(self.stackedLayout.count()): self.stackedLayout.widget(tabIndex).save() # Must go after all settings are save Receiver.send_message(u'config_updated') @@ -102,7 +102,7 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog): """ Process the form saving the settings """ - for tabIndex in range(0, self.stackedLayout.count()): + for tabIndex in range(self.stackedLayout.count()): self.stackedLayout.widget(tabIndex).cancel() return QtGui.QDialog.reject(self) diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index a1b3a84b2..575a42c1f 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -373,7 +373,7 @@ class SlideController(Controller): u'text': translate('OpenLP.SlideController', 'Go to "Ending"')}, {u'key': u'O', u'configurable': True, u'text': translate('OpenLP.SlideController', 'Go to "Other"')}] - shortcuts += [{u'key': unicode(number)} for number in range(0, 10)] + shortcuts += [{u'key': unicode(number)} for number in range(10)] self.previewListWidget.addActions([create_action(self, u'shortcutAction_%s' % s[u'key'], text=s.get(u'text'), shortcuts=[QtGui.QKeySequence(s[u'key'])], diff --git a/openlp/plugins/custom/forms/editcustomform.py b/openlp/plugins/custom/forms/editcustomform.py index 7c2da3bb0..294bd324d 100644 --- a/openlp/plugins/custom/forms/editcustomform.py +++ b/openlp/plugins/custom/forms/editcustomform.py @@ -127,7 +127,7 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog): sxml.new_document() sxml.add_lyrics_to_song() count = 1 - for i in range(0, self.slideListView.count()): + for i in range(self.slideListView.count()): sxml.add_verse_to_lyrics(u'custom', unicode(count), unicode(self.slideListView.item(i).text())) count += 1 @@ -170,7 +170,7 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog): Edits all slides. """ slide_list = u'' - for row in range(0, self.slideListView.count()): + for row in range(self.slideListView.count()): item = self.slideListView.item(row) slide_list += item.text() if row != self.slideListView.count() - 1: @@ -206,7 +206,7 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog): old_row = self.slideListView.currentRow() # Create a list with all (old/unedited) slides. old_slides = [self.slideListView.item(row).text() for row in \ - range(0, self.slideListView.count())] + range(self.slideListView.count())] self.slideListView.clear() old_slides.pop(old_row) # Insert all slides to make the old_slides list complete. diff --git a/openlp/plugins/presentations/lib/powerpointcontroller.py b/openlp/plugins/presentations/lib/powerpointcontroller.py index da828b436..f97749f13 100644 --- a/openlp/plugins/presentations/lib/powerpointcontroller.py +++ b/openlp/plugins/presentations/lib/powerpointcontroller.py @@ -152,7 +152,7 @@ class PowerpointDocument(PresentationDocument): log.debug(u'create_thumbnails') if self.check_thumbnails(): return - for num in range(0, self.presentation.Slides.Count): + for num in range(self.presentation.Slides.Count): self.presentation.Slides(num + 1).Export(os.path.join( self.get_thumbnail_folder(), 'slide%d.png' % (num + 1)), 'png', 320, 240) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 1412d94cb..195dda729 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -336,7 +336,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): Tag the Song List rows based on the verse list """ row_label = [] - for row in range(0, self.verseListWidget.rowCount()): + for row in range(self.verseListWidget.rowCount()): item = self.verseListWidget.item(row, 0) verse_def = unicode(item.data(QtCore.Qt.UserRole).toString()) verse_tag = VerseType.translated_tag(verse_def[0]) @@ -494,7 +494,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): if len(tempText.split(u'\n')) != len(after_text.split(u'\n')): tempList = {} tempId = {} - for row in range(0, self.verseListWidget.rowCount()): + for row in range(self.verseListWidget.rowCount()): tempList[row] = self.verseListWidget.item(row, 0)\ .text() tempId[row] = self.verseListWidget.item(row, 0)\ @@ -511,7 +511,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): def onVerseEditAllButtonClicked(self): verse_list = u'' if self.verseListWidget.rowCount() > 0: - for row in range(0, self.verseListWidget.rowCount()): + for row in range(self.verseListWidget.rowCount()): item = self.verseListWidget.item(row, 0) field = unicode(item.data(QtCore.Qt.UserRole).toString()) verse_tag = VerseType.translated_name(field[0]) @@ -579,7 +579,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): verses = [] verse_names = [] order = self.__extractVerseOrder(text) - for index in range(0, self.verseListWidget.rowCount()): + for index in range(self.verseListWidget.rowCount()): verse = self.verseListWidget.item(index, 0) verse = unicode(verse.data(QtCore.Qt.UserRole).toString()) if verse not in verse_names: @@ -620,7 +620,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): verse_names = [] order_names = unicode(verse_order).split() order = self.__extractVerseOrder(verse_order) - for index in range(0, verse_count): + for index in range(verse_count): verse = self.verseListWidget.item(index, 0) verse = unicode(verse.data(QtCore.Qt.UserRole).toString()) if verse not in verse_names: @@ -920,7 +920,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): try: sxml = SongXML() multiple = [] - for i in range(0, self.verseListWidget.rowCount()): + for i in range(self.verseListWidget.rowCount()): item = self.verseListWidget.item(i, 0) verseId = unicode(item.data(QtCore.Qt.UserRole).toString()) verse_tag = verseId[0] diff --git a/openlp/plugins/songs/lib/cclifileimport.py b/openlp/plugins/songs/lib/cclifileimport.py index 6b99f9a16..e34a12e41 100644 --- a/openlp/plugins/songs/lib/cclifileimport.py +++ b/openlp/plugins/songs/lib/cclifileimport.py @@ -185,7 +185,7 @@ class CCLIFileImport(SongImport): check_first_verse_line = False field_list = song_fields.split(u'/t') words_list = song_words.split(u'/t') - for counter in range(0, len(field_list)): + for counter in range(len(field_list)): if field_list[counter].startswith(u'Ver'): verse_type = VerseType.Tags[VerseType.Verse] elif field_list[counter].startswith(u'Ch'): From eafd3a20ae3e811d047604448bdd9c947dd30a34 Mon Sep 17 00:00:00 2001 From: M2j Date: Tue, 1 May 2012 13:03:31 +0200 Subject: [PATCH 16/20] revert openlp/core/ui/media/mediacontroller.py to prevent merge conflict --- openlp/core/ui/media/mediacontroller.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/core/ui/media/mediacontroller.py b/openlp/core/ui/media/mediacontroller.py index 73283ea9b..7ccbd9245 100644 --- a/openlp/core/ui/media/mediacontroller.py +++ b/openlp/core/ui/media/mediacontroller.py @@ -127,7 +127,7 @@ class MediaController(object): invalidMediaPlayers = [mediaPlayer for mediaPlayer in savedPlayers \ if not mediaPlayer in self.mediaPlayers or \ not self.mediaPlayers[mediaPlayer].check_available()] - if invalidMediaPlayers: + if len(invalidMediaPlayers) > 0: for invalidPlayer in invalidMediaPlayers: savedPlayers.remove(invalidPlayer) set_media_players(savedPlayers, overriddenPlayer) @@ -141,7 +141,7 @@ class MediaController(object): Check if there is a running media Player and do updating stuff (e.g. update the UI) """ - if not self.curDisplayMediaPlayer: + if len(self.curDisplayMediaPlayer.keys()) == 0: self.timer.stop() else: for display in self.curDisplayMediaPlayer.keys(): From 6915fb9d0d93a733d1f84c7896aeb2815b9ce454 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Wed, 2 May 2012 20:25:37 +0200 Subject: [PATCH 17/20] improved renderer performance --- openlp/core/lib/formattingtags.py | 1 - openlp/core/lib/renderer.py | 10 ++++++---- openlp/core/lib/serviceitem.py | 3 +-- openlp/core/ui/formattingtagform.py | 3 ++- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/openlp/core/lib/formattingtags.py b/openlp/core/lib/formattingtags.py index bb5f1e716..bec2db63a 100644 --- a/openlp/core/lib/formattingtags.py +++ b/openlp/core/lib/formattingtags.py @@ -47,7 +47,6 @@ class FormattingTags(object): Provide access to the html_expands list. """ # Load user defined tags otherwise user defined tags are not present. - FormattingTags.load_tags() return FormattingTags.html_expands @staticmethod diff --git a/openlp/core/lib/renderer.py b/openlp/core/lib/renderer.py index 39f69dda6..2c9d2a5d1 100644 --- a/openlp/core/lib/renderer.py +++ b/openlp/core/lib/renderer.py @@ -131,7 +131,6 @@ class Renderer(object): ``override_levels`` Used to force the theme data passed in to be used. - """ log.debug(u'set override theme to %s', override_theme) theme_level = self.theme_level @@ -500,12 +499,15 @@ class Renderer(object): raw_tags.sort(key=lambda tag: tag[0]) html_tags.sort(key=lambda tag: tag[0]) # Create a list with closing tags for the raw_text. - end_tags = [tag[2] for tag in raw_tags] + end_tags = [] + start_tags = [] + for tag in raw_tags: + start_tags.append(tag[1]) + end_tags.append(tag[2]) end_tags.reverse() # Remove the indexes. - raw_tags = [tag[1] for tag in raw_tags] html_tags = [tag[1] for tag in html_tags] - return raw_text + u''.join(end_tags), u''.join(raw_tags), \ + return raw_text + u''.join(end_tags), u''.join(start_tags), \ u''.join(html_tags) def _binary_chop(self, formatted, previous_html, previous_raw, html_list, diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index a82941341..c8fbe6d8a 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -195,8 +195,7 @@ class ServiceItem(object): # avoid tracebacks. if self.raw_footer is None: self.raw_footer = [] - self.foot_text = \ - u'
'.join([footer for footer in self.raw_footer if footer]) + self.foot_text = u'
'.join(filter(None, self.raw_footer)) def add_from_image(self, path, title, background=None): """ diff --git a/openlp/core/ui/formattingtagform.py b/openlp/core/ui/formattingtagform.py index 28200d132..d6f880e3f 100644 --- a/openlp/core/ui/formattingtagform.py +++ b/openlp/core/ui/formattingtagform.py @@ -164,12 +164,13 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog): """ Reset List for loading. """ + FormattingTags.load_tags() self.tagTableWidget.clearContents() self.tagTableWidget.setRowCount(0) self.newPushButton.setEnabled(True) self.savePushButton.setEnabled(False) self.deletePushButton.setEnabled(False) - for linenumber, html in enumerate(FormattingTags.html_expands): + for linenumber, html in enumerate(FormattingTags.get_html_tags()): self.tagTableWidget.setRowCount(self.tagTableWidget.rowCount() + 1) self.tagTableWidget.setItem(linenumber, 0, QtGui.QTableWidgetItem(html[u'desc'])) From 10dbb5ee16315ba0569a426369ef57507a31c95f Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Wed, 2 May 2012 22:57:12 +0200 Subject: [PATCH 18/20] Changed some things to comply with Debian's packaging policies. --- documentation/openlp.1 | 47 + openlp/plugins/remotes/html/jquery.js | 9408 +++++++++++++++++- openlp/plugins/remotes/html/jquery.mobile.js | 7728 +++++++++++++- resources/windows/psvince.dll | Bin 36864 -> 0 bytes scripts/windows-builder.py | 2 +- 5 files changed, 17003 insertions(+), 182 deletions(-) create mode 100644 documentation/openlp.1 delete mode 100644 resources/windows/psvince.dll diff --git a/documentation/openlp.1 b/documentation/openlp.1 new file mode 100644 index 000000000..789794fb4 --- /dev/null +++ b/documentation/openlp.1 @@ -0,0 +1,47 @@ +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.40.9. +.TH OPENLP "1" "May 2012" "OpenLP 1.9.9" "User Commands" +.SH NAME +OpenLP \- Church worship presentation software +.SH SYNOPSIS +.B openlp +[\fIoptions\fR] [\fIqt-options\fR] +.SH OPTIONS +.TP +\fB\-\-version\fR +show program's version number and exit +.TP +\fB\-h\fR, \fB\-\-help\fR +show this help message and exit +.TP +\fB\-e\fR, \fB\-\-no\-error\-form\fR +Disable the error notification form. +.TP +\fB\-l\fR LEVEL, \fB\-\-log\-level\fR=\fILEVEL\fR +Set logging to LEVEL level. Valid values are "debug", +"info", "warning". +.TP +\fB\-p\fR, \fB\-\-portable\fR +Specify if this should be run as a portable app, off a +USB flash drive (not implemented). +.TP +\fB\-d\fR, \fB\-\-dev\-version\fR +Ignore the version file and pull the version directly +from Bazaar +.TP +\fB\-s\fR STYLE, \fB\-\-style\fR=\fISTYLE\fR +Set the Qt4 style (passed directly to Qt4). +.TP +\fB\-\-testing\fR +Run by testing framework +.SH "SEE ALSO" +The full documentation for +.B OpenLP +is maintained as a Texinfo manual. If the +.B info +and +.B OpenLP +programs are properly installed at your site, the command +.IP +.B info OpenLP +.PP +should give you access to the complete manual. diff --git a/openlp/plugins/remotes/html/jquery.js b/openlp/plugins/remotes/html/jquery.js index 16ad06c5a..3774ff986 100644 --- a/openlp/plugins/remotes/html/jquery.js +++ b/openlp/plugins/remotes/html/jquery.js @@ -1,4 +1,9404 @@ -/*! jQuery v1.7.2 jquery.com | jquery.org/license */ -(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cu(a){if(!cj[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),b.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write((f.support.boxModel?"":"")+""),cl.close();d=cl.createElement(a),cl.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ck)}cj[a]=e}return cj[a]}function ct(a,b){var c={};f.each(cp.concat.apply([],cp.slice(0,b)),function(){c[this]=a});return c}function cs(){cq=b}function cr(){setTimeout(cs,0);return cq=f.now()}function ci(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ch(){try{return new a.XMLHttpRequest}catch(b){}}function cb(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){if(c!=="border")for(;e=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?+d:j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){if(typeof c!="string"||!c)return null;var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c
a",d=p.getElementsByTagName("*"),e=p.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=p.getElementsByTagName("input")[0],b={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:p.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,pixelMargin:!0},f.boxModel=b.boxModel=c.compatMode==="CSS1Compat",i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete p.test}catch(r){b.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",function(){b.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),i.setAttribute("name","t"),p.appendChild(i),j=c.createDocumentFragment(),j.appendChild(p.lastChild),b.checkClone=j.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,j.removeChild(i),j.appendChild(p);if(p.attachEvent)for(n in{submit:1,change:1,focusin:1})m="on"+n,o=m in p,o||(p.setAttribute(m,"return;"),o=typeof p[m]=="function"),b[n+"Bubbles"]=o;j.removeChild(p),j=g=h=p=i=null,f(function(){var d,e,g,h,i,j,l,m,n,q,r,s,t,u=c.getElementsByTagName("body")[0];!u||(m=1,t="padding:0;margin:0;border:",r="position:absolute;top:0;left:0;width:1px;height:1px;",s=t+"0;visibility:hidden;",n="style='"+r+t+"5px solid #000;",q="
"+""+"
",d=c.createElement("div"),d.style.cssText=s+"width:0;height:0;position:static;top:0;margin-top:"+m+"px",u.insertBefore(d,u.firstChild),p=c.createElement("div"),d.appendChild(p),p.innerHTML="
t
",k=p.getElementsByTagName("td"),o=k[0].offsetHeight===0,k[0].style.display="",k[1].style.display="none",b.reliableHiddenOffsets=o&&k[0].offsetHeight===0,a.getComputedStyle&&(p.innerHTML="",l=c.createElement("div"),l.style.width="0",l.style.marginRight="0",p.style.width="2px",p.appendChild(l),b.reliableMarginRight=(parseInt((a.getComputedStyle(l,null)||{marginRight:0}).marginRight,10)||0)===0),typeof p.style.zoom!="undefined"&&(p.innerHTML="",p.style.width=p.style.padding="1px",p.style.border=0,p.style.overflow="hidden",p.style.display="inline",p.style.zoom=1,b.inlineBlockNeedsLayout=p.offsetWidth===3,p.style.display="block",p.style.overflow="visible",p.innerHTML="
",b.shrinkWrapBlocks=p.offsetWidth!==3),p.style.cssText=r+s,p.innerHTML=q,e=p.firstChild,g=e.firstChild,i=e.nextSibling.firstChild.firstChild,j={doesNotAddBorder:g.offsetTop!==5,doesAddBorderForTableAndCells:i.offsetTop===5},g.style.position="fixed",g.style.top="20px",j.fixedPosition=g.offsetTop===20||g.offsetTop===15,g.style.position=g.style.top="",e.style.overflow="hidden",e.style.position="relative",j.subtractsBorderForOverflowNotVisible=g.offsetTop===-5,j.doesNotIncludeMarginInBodyOffset=u.offsetTop!==m,a.getComputedStyle&&(p.style.marginTop="1%",b.pixelMargin=(a.getComputedStyle(p,null)||{marginTop:0}).marginTop!=="1%"),typeof d.style.zoom!="undefined"&&(d.style.zoom=1),u.removeChild(d),l=p=d=null,f.extend(b,j))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e1,null,!1)},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){var d=2;typeof a!="string"&&(c=a,a="fx",d--);if(arguments.length1)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,f.prop,a,b,arguments.length>1)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.type]||f.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.type]||f.valHooks[g.nodeName.toLowerCase()];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h,i=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;i=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/(?:^|\s)hover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function( -a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler,g=p.selector),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;le&&j.push({elem:this,matches:d.slice(e)});for(k=0;k0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));o.match.globalPOS=p;var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h0)for(h=g;h=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/]","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*",""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div
","
"]),f.fn.extend({text:function(a){return f.access(this,function(a){return a===b?f.text(this):this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f -.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){return f.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1>");try{for(;d1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||f.isXMLDoc(a)||!bc.test("<"+a.nodeName+">")?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g,h,i,j=[];b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);for(var k=0,l;(l=a[k])!=null;k++){typeof l=="number"&&(l+="");if(!l)continue;if(typeof l=="string")if(!_.test(l))l=b.createTextNode(l);else{l=l.replace(Y,"<$1>");var m=(Z.exec(l)||["",""])[1].toLowerCase(),n=bg[m]||bg._default,o=n[0],p=b.createElement("div"),q=bh.childNodes,r;b===c?bh.appendChild(p):U(b).appendChild(p),p.innerHTML=n[1]+l+n[2];while(o--)p=p.lastChild;if(!f.support.tbody){var s=$.test(l),t=m==="table"&&!s?p.firstChild&&p.firstChild.childNodes:n[1]===""&&!s?p.childNodes:[];for(i=t.length-1;i>=0;--i)f.nodeName(t[i],"tbody")&&!t[i].childNodes.length&&t[i].parentNode.removeChild(t[i])}!f.support.leadingWhitespace&&X.test(l)&&p.insertBefore(b.createTextNode(X.exec(l)[0]),p.firstChild),l=p.childNodes,p&&(p.parentNode.removeChild(p),q.length>0&&(r=q[q.length-1],r&&r.parentNode&&r.parentNode.removeChild(r)))}var u;if(!f.support.appendChecked)if(l[0]&&typeof (u=l.length)=="number")for(i=0;i1)},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=by(a,"opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bu.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(by)return by(a,c)},swap:function(a,b,c){var d={},e,f;for(f in b)d[f]=a.style[f],a.style[f]=b[f];e=c.call(a);for(f in b)a.style[f]=d[f];return e}}),f.curCSS=f.css,c.defaultView&&c.defaultView.getComputedStyle&&(bz=function(a,b){var c,d,e,g,h=a.style;b=b.replace(br,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b))),!f.support.pixelMargin&&e&&bv.test(b)&&bt.test(c)&&(g=h.width,h.width=c,c=e.width,h.width=g);return c}),c.documentElement.currentStyle&&(bA=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f==null&&g&&(e=g[b])&&(f=e),bt.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),by=bz||bA,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0?bB(a,b,d):f.swap(a,bw,function(){return bB(a,b,d)})},set:function(a,b){return bs.test(b)?b+"px":b}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bq.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bp,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bp.test(g)?g.replace(bp,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){return f.swap(a,{display:"inline-block"},function(){return b?by(a,"margin-right"):a.style.marginRight})}})}),f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)}),f.each({margin:"",padding:"",border:"Width"},function(a,b){f.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bx[d]+b]=e[d]||e[d-2]||e[0];return f}}});var bC=/%20/g,bD=/\[\]$/,bE=/\r?\n/g,bF=/#.*$/,bG=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bH=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bI=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bJ=/^(?:GET|HEAD)$/,bK=/^\/\//,bL=/\?/,bM=/)<[^<]*)*<\/script>/gi,bN=/^(?:select|textarea)/i,bO=/\s+/,bP=/([?&])_=[^&]*/,bQ=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bR=f.fn.load,bS={},bT={},bU,bV,bW=["*/"]+["*"];try{bU=e.href}catch(bX){bU=c.createElement("a"),bU.href="",bU=bU.href}bV=bQ.exec(bU.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bR)return bR.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
").append(c.replace(bM,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bN.test(this.nodeName)||bH.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bE,"\r\n")}}):{name:b.name,value:c.replace(bE,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b$(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b$(a,b);return a},ajaxSettings:{url:bU,isLocal:bI.test(bV[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bW},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bY(bS),ajaxTransport:bY(bT),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?ca(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cb(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bG.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bF,"").replace(bK,bV[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bO),d.crossDomain==null&&(r=bQ.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bV[1]&&r[2]==bV[2]&&(r[3]||(r[1]==="http:"?80:443))==(bV[3]||(bV[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bZ(bS,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bJ.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bL.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bP,"$1_="+x);d.url=y+(y===d.url?(bL.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bW+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bZ(bT,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)b_(g,a[g],c,e);return d.join("&").replace(bC,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cc=f.now(),cd=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cc++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=typeof b.data=="string"&&/^application\/x\-www\-form\-urlencoded/.test(b.contentType);if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cd.test(b.url)||e&&cd.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cd,l),b.url===j&&(e&&(k=k.replace(cd,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ce=a.ActiveXObject?function(){for(var a in cg)cg[a](0,1)}:!1,cf=0,cg;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ch()||ci()}:ch,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ce&&delete cg[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n);try{m.text=h.responseText}catch(a){}try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cf,ce&&(cg||(cg={},f(a).unload(ce)),cg[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cj={},ck,cl,cm=/^(?:toggle|show|hide)$/,cn=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,co,cp=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cq;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(ct("show",3),a,b,c);for(var g=0,h=this.length;g=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);f.fn[a]=function(e){return f.access(this,function(a,e,g){var h=cy(a);if(g===b)return h?c in h?h[c]:f.support.boxModel&&h.document.documentElement[e]||h.document.body[e]:a[e];h?h.scrollTo(d?f(h).scrollLeft():g,d?g:f(h).scrollTop()):a[e]=g},a,e,arguments.length,null)}}),f.each({Height:"height",Width:"width"},function(a,c){var d="client"+a,e="scroll"+a,g="offset"+a;f.fn["inner"+a]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,c,"padding")):this[c]():null},f.fn["outer"+a]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,c,a?"margin":"border")):this[c]():null},f.fn[c]=function(a){return f.access(this,function(a,c,h){var i,j,k,l;if(f.isWindow(a)){i=a.document,j=i.documentElement[d];return f.support.boxModel&&j||i.body&&i.body[d]||j}if(a.nodeType===9){i=a.documentElement;if(i[d]>=i[e])return i[d];return Math.max(a.body[e],i[e],a.body[g],i[g])}if(h===b){k=f.css(a,c),l=parseFloat(k);return f.isNumeric(l)?l:k}f(a).css(c,h)},c,a,arguments.length,null)}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window); \ No newline at end of file +/*! + * jQuery JavaScript Library v1.7.2 + * http://jquery.com/ + * + * Copyright 2011, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Wed Mar 21 12:46:34 2012 -0700 + */ +(function( window, undefined ) { + +// Use the correct document accordingly with window argument (sandbox) +var document = window.document, + navigator = window.navigator, + location = window.location; +var jQuery = (function() { + +// Define a local copy of jQuery +var jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }, + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$, + + // A central reference to the root jQuery(document) + rootjQuery, + + // A simple way to check for HTML strings or ID strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/, + + // Check if a string has a non-whitespace character in it + rnotwhite = /\S/, + + // Used for trimming whitespace + trimLeft = /^\s+/, + trimRight = /\s+$/, + + // Match a standalone tag + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, + + // JSON RegExp + rvalidchars = /^[\],:{}\s]*$/, + rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, + rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, + rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, + + // Useragent RegExp + rwebkit = /(webkit)[ \/]([\w.]+)/, + ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/, + rmsie = /(msie) ([\w.]+)/, + rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/, + + // Matches dashed string for camelizing + rdashAlpha = /-([a-z]|[0-9])/ig, + rmsPrefix = /^-ms-/, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return ( letter + "" ).toUpperCase(); + }, + + // Keep a UserAgent string for use with jQuery.browser + userAgent = navigator.userAgent, + + // For matching the engine and version of the browser + browserMatch, + + // The deferred used on DOM ready + readyList, + + // The ready event handler + DOMContentLoaded, + + // Save a reference to some core methods + toString = Object.prototype.toString, + hasOwn = Object.prototype.hasOwnProperty, + push = Array.prototype.push, + slice = Array.prototype.slice, + trim = String.prototype.trim, + indexOf = Array.prototype.indexOf, + + // [[Class]] -> type pairs + class2type = {}; + +jQuery.fn = jQuery.prototype = { + constructor: jQuery, + init: function( selector, context, rootjQuery ) { + var match, elem, ret, doc; + + // Handle $(""), $(null), or $(undefined) + if ( !selector ) { + return this; + } + + // Handle $(DOMElement) + if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + } + + // The body element only exists once, optimize finding it + if ( selector === "body" && !context && document.body ) { + this.context = document; + this[0] = document.body; + this.selector = selector; + this.length = 1; + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + // Are we dealing with HTML string or an ID? + if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = quickExpr.exec( selector ); + } + + // Verify a match, and that no context was specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + doc = ( context ? context.ownerDocument || context : document ); + + // If a single string is passed in and it's a single tag + // just do a createElement and skip the rest + ret = rsingleTag.exec( selector ); + + if ( ret ) { + if ( jQuery.isPlainObject( context ) ) { + selector = [ document.createElement( ret[1] ) ]; + jQuery.fn.attr.call( selector, context, true ); + + } else { + selector = [ doc.createElement( ret[1] ) ]; + } + + } else { + ret = jQuery.buildFragment( [ match[1] ], [ doc ] ); + selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes; + } + + return jQuery.merge( this, selector ); + + // HANDLE: $("#id") + } else { + elem = document.getElementById( match[2] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || rootjQuery ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return rootjQuery.ready( selector ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }, + + // Start with an empty selector + selector: "", + + // The current version of jQuery being used + jquery: "1.7.2", + + // The default length of a jQuery object is 0 + length: 0, + + // The number of elements contained in the matched element set + size: function() { + return this.length; + }, + + toArray: function() { + return slice.call( this, 0 ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this[ this.length + num ] : this[ num ] ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems, name, selector ) { + // Build a new jQuery matched element set + var ret = this.constructor(); + + if ( jQuery.isArray( elems ) ) { + push.apply( ret, elems ); + + } else { + jQuery.merge( ret, elems ); + } + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + ret.context = this.context; + + if ( name === "find" ) { + ret.selector = this.selector + ( this.selector ? " " : "" ) + selector; + } else if ( name ) { + ret.selector = this.selector + "." + name + "(" + selector + ")"; + } + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + ready: function( fn ) { + // Attach the listeners + jQuery.bindReady(); + + // Add the callback + readyList.add( fn ); + + return this; + }, + + eq: function( i ) { + i = +i; + return i === -1 ? + this.slice( i ) : + this.slice( i, i + 1 ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ), + "slice", slice.call(arguments).join(",") ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: [].sort, + splice: [].splice +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( length === i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + noConflict: function( deep ) { + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; + }, + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + // Either a released hold or an DOMready/load event and not yet ready + if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready, 1 ); + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.fireWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.trigger ) { + jQuery( document ).trigger( "ready" ).off( "ready" ); + } + } + }, + + bindReady: function() { + if ( readyList ) { + return; + } + + readyList = jQuery.Callbacks( "once memory" ); + + // Catch cases where $(document).ready() is called after the + // browser event has already occurred. + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + return setTimeout( jQuery.ready, 1 ); + } + + // Mozilla, Opera and webkit nightlies currently support this event + if ( document.addEventListener ) { + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", jQuery.ready, false ); + + // If IE event model is used + } else if ( document.attachEvent ) { + // ensure firing before onload, + // maybe late but safe also for iframes + document.attachEvent( "onreadystatechange", DOMContentLoaded ); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", jQuery.ready ); + + // If IE and not a frame + // continually check to see if the document is ready + var toplevel = false; + + try { + toplevel = window.frameElement == null; + } catch(e) {} + + if ( document.documentElement.doScroll && toplevel ) { + doScrollCheck(); + } + } + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type(obj) === "array"; + }, + + isWindow: function( obj ) { + return obj != null && obj == obj.window; + }, + + isNumeric: function( obj ) { + return !isNaN( parseFloat(obj) ) && isFinite( obj ); + }, + + type: function( obj ) { + return obj == null ? + String( obj ) : + class2type[ toString.call(obj) ] || "object"; + }, + + isPlainObject: function( obj ) { + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + try { + // Not own constructor property must be Object + if ( obj.constructor && + !hasOwn.call(obj, "constructor") && + !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + } catch ( e ) { + // IE8,9 Will throw exceptions on certain host objects #9897 + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + + var key; + for ( key in obj ) {} + + return key === undefined || hasOwn.call( obj, key ); + }, + + isEmptyObject: function( obj ) { + for ( var name in obj ) { + return false; + } + return true; + }, + + error: function( msg ) { + throw new Error( msg ); + }, + + parseJSON: function( data ) { + if ( typeof data !== "string" || !data ) { + return null; + } + + // Make sure leading/trailing whitespace is removed (IE can't handle it) + data = jQuery.trim( data ); + + // Attempt to parse using the native JSON parser first + if ( window.JSON && window.JSON.parse ) { + return window.JSON.parse( data ); + } + + // Make sure the incoming data is actual JSON + // Logic borrowed from http://json.org/json2.js + if ( rvalidchars.test( data.replace( rvalidescape, "@" ) + .replace( rvalidtokens, "]" ) + .replace( rvalidbraces, "")) ) { + + return ( new Function( "return " + data ) )(); + + } + jQuery.error( "Invalid JSON: " + data ); + }, + + // Cross-browser xml parsing + parseXML: function( data ) { + if ( typeof data !== "string" || !data ) { + return null; + } + var xml, tmp; + try { + if ( window.DOMParser ) { // Standard + tmp = new DOMParser(); + xml = tmp.parseFromString( data , "text/xml" ); + } else { // IE + xml = new ActiveXObject( "Microsoft.XMLDOM" ); + xml.async = "false"; + xml.loadXML( data ); + } + } catch( e ) { + xml = undefined; + } + if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; + }, + + noop: function() {}, + + // Evaluates a script in a global context + // Workarounds based on findings by Jim Driscoll + // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context + globalEval: function( data ) { + if ( data && rnotwhite.test( data ) ) { + // We use execScript on Internet Explorer + // We use an anonymous function so that context is window + // rather than jQuery in Firefox + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); + } )( data ); + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); + }, + + // args is for internal usage only + each: function( object, callback, args ) { + var name, i = 0, + length = object.length, + isObj = length === undefined || jQuery.isFunction( object ); + + if ( args ) { + if ( isObj ) { + for ( name in object ) { + if ( callback.apply( object[ name ], args ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.apply( object[ i++ ], args ) === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isObj ) { + for ( name in object ) { + if ( callback.call( object[ name ], name, object[ name ] ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) { + break; + } + } + } + } + + return object; + }, + + // Use native String.trim function wherever possible + trim: trim ? + function( text ) { + return text == null ? + "" : + trim.call( text ); + } : + + // Otherwise use our own trimming functionality + function( text ) { + return text == null ? + "" : + text.toString().replace( trimLeft, "" ).replace( trimRight, "" ); + }, + + // results is for internal usage only + makeArray: function( array, results ) { + var ret = results || []; + + if ( array != null ) { + // The window, strings (and functions) also have 'length' + // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930 + var type = jQuery.type( array ); + + if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) { + push.call( ret, array ); + } else { + jQuery.merge( ret, array ); + } + } + + return ret; + }, + + inArray: function( elem, array, i ) { + var len; + + if ( array ) { + if ( indexOf ) { + return indexOf.call( array, elem, i ); + } + + len = array.length; + i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; + + for ( ; i < len; i++ ) { + // Skip accessing in sparse arrays + if ( i in array && array[ i ] === elem ) { + return i; + } + } + } + + return -1; + }, + + merge: function( first, second ) { + var i = first.length, + j = 0; + + if ( typeof second.length === "number" ) { + for ( var l = second.length; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, inv ) { + var ret = [], retVal; + inv = !!inv; + + // Go through the array, only saving the items + // that pass the validator function + for ( var i = 0, length = elems.length; i < length; i++ ) { + retVal = !!callback( elems[ i ], i ); + if ( inv !== retVal ) { + ret.push( elems[ i ] ); + } + } + + return ret; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var value, key, ret = [], + i = 0, + length = elems.length, + // jquery objects are treated as arrays + isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ; + + // Go through the array, translating each of the items to their + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + + // Go through every key on the object, + } else { + for ( key in elems ) { + value = callback( elems[ key ], key, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + } + + // Flatten any nested arrays + return ret.concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + if ( typeof context === "string" ) { + var tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + var args = slice.call( arguments, 2 ), + proxy = function() { + return fn.apply( context, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; + + return proxy; + }, + + // Mutifunctional method to get and set values to a collection + // The value/s can optionally be executed if it's a function + access: function( elems, fn, key, value, chainable, emptyGet, pass ) { + var exec, + bulk = key == null, + i = 0, + length = elems.length; + + // Sets many values + if ( key && typeof key === "object" ) { + for ( i in key ) { + jQuery.access( elems, fn, i, key[i], 1, emptyGet, value ); + } + chainable = 1; + + // Sets one value + } else if ( value !== undefined ) { + // Optionally, function values get executed if exec is true + exec = pass === undefined && jQuery.isFunction( value ); + + if ( bulk ) { + // Bulk operations only iterate when executing function values + if ( exec ) { + exec = fn; + fn = function( elem, key, value ) { + return exec.call( jQuery( elem ), value ); + }; + + // Otherwise they run against the entire set + } else { + fn.call( elems, value ); + fn = null; + } + } + + if ( fn ) { + for (; i < length; i++ ) { + fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); + } + } + + chainable = 1; + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + length ? fn( elems[0], key ) : emptyGet; + }, + + now: function() { + return ( new Date() ).getTime(); + }, + + // Use of jQuery.browser is frowned upon. + // More details: http://docs.jquery.com/Utilities/jQuery.browser + uaMatch: function( ua ) { + ua = ua.toLowerCase(); + + var match = rwebkit.exec( ua ) || + ropera.exec( ua ) || + rmsie.exec( ua ) || + ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) || + []; + + return { browser: match[1] || "", version: match[2] || "0" }; + }, + + sub: function() { + function jQuerySub( selector, context ) { + return new jQuerySub.fn.init( selector, context ); + } + jQuery.extend( true, jQuerySub, this ); + jQuerySub.superclass = this; + jQuerySub.fn = jQuerySub.prototype = this(); + jQuerySub.fn.constructor = jQuerySub; + jQuerySub.sub = this.sub; + jQuerySub.fn.init = function init( selector, context ) { + if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) { + context = jQuerySub( context ); + } + + return jQuery.fn.init.call( this, selector, context, rootjQuerySub ); + }; + jQuerySub.fn.init.prototype = jQuerySub.fn; + var rootjQuerySub = jQuerySub(document); + return jQuerySub; + }, + + browser: {} +}); + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +browserMatch = jQuery.uaMatch( userAgent ); +if ( browserMatch.browser ) { + jQuery.browser[ browserMatch.browser ] = true; + jQuery.browser.version = browserMatch.version; +} + +// Deprecated, use jQuery.browser.webkit instead +if ( jQuery.browser.webkit ) { + jQuery.browser.safari = true; +} + +// IE doesn't match non-breaking spaces with \s +if ( rnotwhite.test( "\xA0" ) ) { + trimLeft = /^[\s\xA0]+/; + trimRight = /[\s\xA0]+$/; +} + +// All jQuery objects should point back to these +rootjQuery = jQuery(document); + +// Cleanup functions for the document ready method +if ( document.addEventListener ) { + DOMContentLoaded = function() { + document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + jQuery.ready(); + }; + +} else if ( document.attachEvent ) { + DOMContentLoaded = function() { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( document.readyState === "complete" ) { + document.detachEvent( "onreadystatechange", DOMContentLoaded ); + jQuery.ready(); + } + }; +} + +// The DOM ready check for Internet Explorer +function doScrollCheck() { + if ( jQuery.isReady ) { + return; + } + + try { + // If IE is used, use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + document.documentElement.doScroll("left"); + } catch(e) { + setTimeout( doScrollCheck, 1 ); + return; + } + + // and execute any waiting functions + jQuery.ready(); +} + +return jQuery; + +})(); + + +// String to Object flags format cache +var flagsCache = {}; + +// Convert String-formatted flags into Object-formatted ones and store in cache +function createFlags( flags ) { + var object = flagsCache[ flags ] = {}, + i, length; + flags = flags.split( /\s+/ ); + for ( i = 0, length = flags.length; i < length; i++ ) { + object[ flags[i] ] = true; + } + return object; +} + +/* + * Create a callback list using the following parameters: + * + * flags: an optional list of space-separated flags that will change how + * the callback list behaves + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible flags: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( flags ) { + + // Convert flags from String-formatted to Object-formatted + // (we check in cache first) + flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {}; + + var // Actual callback list + list = [], + // Stack of fire calls for repeatable lists + stack = [], + // Last fire value (for non-forgettable lists) + memory, + // Flag to know if list was already fired + fired, + // Flag to know if list is currently firing + firing, + // First callback to fire (used internally by add and fireWith) + firingStart, + // End of the loop when firing + firingLength, + // Index of currently firing callback (modified by remove if needed) + firingIndex, + // Add one or several callbacks to the list + add = function( args ) { + var i, + length, + elem, + type, + actual; + for ( i = 0, length = args.length; i < length; i++ ) { + elem = args[ i ]; + type = jQuery.type( elem ); + if ( type === "array" ) { + // Inspect recursively + add( elem ); + } else if ( type === "function" ) { + // Add if not in unique mode and callback is not in + if ( !flags.unique || !self.has( elem ) ) { + list.push( elem ); + } + } + } + }, + // Fire callbacks + fire = function( context, args ) { + args = args || []; + memory = !flags.memory || [ context, args ]; + fired = true; + firing = true; + firingIndex = firingStart || 0; + firingStart = 0; + firingLength = list.length; + for ( ; list && firingIndex < firingLength; firingIndex++ ) { + if ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) { + memory = true; // Mark as halted + break; + } + } + firing = false; + if ( list ) { + if ( !flags.once ) { + if ( stack && stack.length ) { + memory = stack.shift(); + self.fireWith( memory[ 0 ], memory[ 1 ] ); + } + } else if ( memory === true ) { + self.disable(); + } else { + list = []; + } + } + }, + // Actual Callbacks object + self = { + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + var length = list.length; + add( arguments ); + // Do we need to add the callbacks to the + // current firing batch? + if ( firing ) { + firingLength = list.length; + // With memory, if we're not firing then + // we should call right away, unless previous + // firing was halted (stopOnFalse) + } else if ( memory && memory !== true ) { + firingStart = length; + fire( memory[ 0 ], memory[ 1 ] ); + } + } + return this; + }, + // Remove a callback from the list + remove: function() { + if ( list ) { + var args = arguments, + argIndex = 0, + argLength = args.length; + for ( ; argIndex < argLength ; argIndex++ ) { + for ( var i = 0; i < list.length; i++ ) { + if ( args[ argIndex ] === list[ i ] ) { + // Handle firingIndex and firingLength + if ( firing ) { + if ( i <= firingLength ) { + firingLength--; + if ( i <= firingIndex ) { + firingIndex--; + } + } + } + // Remove the element + list.splice( i--, 1 ); + // If we have some unicity property then + // we only need to do this once + if ( flags.unique ) { + break; + } + } + } + } + } + return this; + }, + // Control if a given callback is in the list + has: function( fn ) { + if ( list ) { + var i = 0, + length = list.length; + for ( ; i < length; i++ ) { + if ( fn === list[ i ] ) { + return true; + } + } + } + return false; + }, + // Remove all callbacks from the list + empty: function() { + list = []; + return this; + }, + // Have the list do nothing anymore + disable: function() { + list = stack = memory = undefined; + return this; + }, + // Is it disabled? + disabled: function() { + return !list; + }, + // Lock the list in its current state + lock: function() { + stack = undefined; + if ( !memory || memory === true ) { + self.disable(); + } + return this; + }, + // Is it locked? + locked: function() { + return !stack; + }, + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( stack ) { + if ( firing ) { + if ( !flags.once ) { + stack.push( [ context, args ] ); + } + } else if ( !( flags.once && memory ) ) { + fire( context, args ); + } + } + return this; + }, + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + + + +var // Static reference to slice + sliceDeferred = [].slice; + +jQuery.extend({ + + Deferred: function( func ) { + var doneList = jQuery.Callbacks( "once memory" ), + failList = jQuery.Callbacks( "once memory" ), + progressList = jQuery.Callbacks( "memory" ), + state = "pending", + lists = { + resolve: doneList, + reject: failList, + notify: progressList + }, + promise = { + done: doneList.add, + fail: failList.add, + progress: progressList.add, + + state: function() { + return state; + }, + + // Deprecated + isResolved: doneList.fired, + isRejected: failList.fired, + + then: function( doneCallbacks, failCallbacks, progressCallbacks ) { + deferred.done( doneCallbacks ).fail( failCallbacks ).progress( progressCallbacks ); + return this; + }, + always: function() { + deferred.done.apply( deferred, arguments ).fail.apply( deferred, arguments ); + return this; + }, + pipe: function( fnDone, fnFail, fnProgress ) { + return jQuery.Deferred(function( newDefer ) { + jQuery.each( { + done: [ fnDone, "resolve" ], + fail: [ fnFail, "reject" ], + progress: [ fnProgress, "notify" ] + }, function( handler, data ) { + var fn = data[ 0 ], + action = data[ 1 ], + returned; + if ( jQuery.isFunction( fn ) ) { + deferred[ handler ](function() { + returned = fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise().then( newDefer.resolve, newDefer.reject, newDefer.notify ); + } else { + newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] ); + } + }); + } else { + deferred[ handler ]( newDefer[ action ] ); + } + }); + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + if ( obj == null ) { + obj = promise; + } else { + for ( var key in promise ) { + obj[ key ] = promise[ key ]; + } + } + return obj; + } + }, + deferred = promise.promise({}), + key; + + for ( key in lists ) { + deferred[ key ] = lists[ key ].fire; + deferred[ key + "With" ] = lists[ key ].fireWith; + } + + // Handle state + deferred.done( function() { + state = "resolved"; + }, failList.disable, progressList.lock ).fail( function() { + state = "rejected"; + }, doneList.disable, progressList.lock ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( firstParam ) { + var args = sliceDeferred.call( arguments, 0 ), + i = 0, + length = args.length, + pValues = new Array( length ), + count = length, + pCount = length, + deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ? + firstParam : + jQuery.Deferred(), + promise = deferred.promise(); + function resolveFunc( i ) { + return function( value ) { + args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; + if ( !( --count ) ) { + deferred.resolveWith( deferred, args ); + } + }; + } + function progressFunc( i ) { + return function( value ) { + pValues[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; + deferred.notifyWith( promise, pValues ); + }; + } + if ( length > 1 ) { + for ( ; i < length; i++ ) { + if ( args[ i ] && args[ i ].promise && jQuery.isFunction( args[ i ].promise ) ) { + args[ i ].promise().then( resolveFunc(i), deferred.reject, progressFunc(i) ); + } else { + --count; + } + } + if ( !count ) { + deferred.resolveWith( deferred, args ); + } + } else if ( deferred !== firstParam ) { + deferred.resolveWith( deferred, length ? [ firstParam ] : [] ); + } + return promise; + } +}); + + + + +jQuery.support = (function() { + + var support, + all, + a, + select, + opt, + input, + fragment, + tds, + events, + eventName, + i, + isSupported, + div = document.createElement( "div" ), + documentElement = document.documentElement; + + // Preliminary tests + div.setAttribute("className", "t"); + div.innerHTML = "
a"; + + all = div.getElementsByTagName( "*" ); + a = div.getElementsByTagName( "a" )[ 0 ]; + + // Can't get basic test support + if ( !all || !all.length || !a ) { + return {}; + } + + // First batch of supports tests + select = document.createElement( "select" ); + opt = select.appendChild( document.createElement("option") ); + input = div.getElementsByTagName( "input" )[ 0 ]; + + support = { + // IE strips leading whitespace when .innerHTML is used + leadingWhitespace: ( div.firstChild.nodeType === 3 ), + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + tbody: !div.getElementsByTagName("tbody").length, + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + htmlSerialize: !!div.getElementsByTagName("link").length, + + // Get the style information from getAttribute + // (IE uses .cssText instead) + style: /top/.test( a.getAttribute("style") ), + + // Make sure that URLs aren't manipulated + // (IE normalizes it by default) + hrefNormalized: ( a.getAttribute("href") === "/a" ), + + // Make sure that element opacity exists + // (IE uses filter instead) + // Use a regex to work around a WebKit issue. See #5145 + opacity: /^0.55/.test( a.style.opacity ), + + // Verify style float existence + // (IE uses styleFloat instead of cssFloat) + cssFloat: !!a.style.cssFloat, + + // Make sure that if no value is specified for a checkbox + // that it defaults to "on". + // (WebKit defaults to "" instead) + checkOn: ( input.value === "on" ), + + // Make sure that a selected-by-default option has a working selected property. + // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) + optSelected: opt.selected, + + // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) + getSetAttribute: div.className !== "t", + + // Tests for enctype support on a form(#6743) + enctype: !!document.createElement("form").enctype, + + // Makes sure cloning an html5 element does not cause problems + // Where outerHTML is undefined, this still works + html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav>", + + // Will be defined later + submitBubbles: true, + changeBubbles: true, + focusinBubbles: false, + deleteExpando: true, + noCloneEvent: true, + inlineBlockNeedsLayout: false, + shrinkWrapBlocks: false, + reliableMarginRight: true, + pixelMargin: true + }; + + // jQuery.boxModel DEPRECATED in 1.3, use jQuery.support.boxModel instead + jQuery.boxModel = support.boxModel = (document.compatMode === "CSS1Compat"); + + // Make sure checked status is properly cloned + input.checked = true; + support.noCloneChecked = input.cloneNode( true ).checked; + + // Make sure that the options inside disabled selects aren't marked as disabled + // (WebKit marks them as disabled) + select.disabled = true; + support.optDisabled = !opt.disabled; + + // Test to see if it's possible to delete an expando from an element + // Fails in Internet Explorer + try { + delete div.test; + } catch( e ) { + support.deleteExpando = false; + } + + if ( !div.addEventListener && div.attachEvent && div.fireEvent ) { + div.attachEvent( "onclick", function() { + // Cloning a node shouldn't copy over any + // bound event handlers (IE does this) + support.noCloneEvent = false; + }); + div.cloneNode( true ).fireEvent( "onclick" ); + } + + // Check if a radio maintains its value + // after being appended to the DOM + input = document.createElement("input"); + input.value = "t"; + input.setAttribute("type", "radio"); + support.radioValue = input.value === "t"; + + input.setAttribute("checked", "checked"); + + // #11217 - WebKit loses check when the name is after the checked attribute + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + fragment = document.createDocumentFragment(); + fragment.appendChild( div.lastChild ); + + // WebKit doesn't clone checked state correctly in fragments + support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Check if a disconnected checkbox will retain its checked + // value of true after appended to the DOM (IE6/7) + support.appendChecked = input.checked; + + fragment.removeChild( input ); + fragment.appendChild( div ); + + // Technique from Juriy Zaytsev + // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/ + // We only care about the case where non-standard event systems + // are used, namely in IE. Short-circuiting here helps us to + // avoid an eval call (in setAttribute) which can cause CSP + // to go haywire. See: https://developer.mozilla.org/en/Security/CSP + if ( div.attachEvent ) { + for ( i in { + submit: 1, + change: 1, + focusin: 1 + }) { + eventName = "on" + i; + isSupported = ( eventName in div ); + if ( !isSupported ) { + div.setAttribute( eventName, "return;" ); + isSupported = ( typeof div[ eventName ] === "function" ); + } + support[ i + "Bubbles" ] = isSupported; + } + } + + fragment.removeChild( div ); + + // Null elements to avoid leaks in IE + fragment = select = opt = div = input = null; + + // Run tests that need a body at doc ready + jQuery(function() { + var container, outer, inner, table, td, offsetSupport, + marginDiv, conMarginTop, style, html, positionTopLeftWidthHeight, + paddingMarginBorderVisibility, paddingMarginBorder, + body = document.getElementsByTagName("body")[0]; + + if ( !body ) { + // Return for frameset docs that don't have a body + return; + } + + conMarginTop = 1; + paddingMarginBorder = "padding:0;margin:0;border:"; + positionTopLeftWidthHeight = "position:absolute;top:0;left:0;width:1px;height:1px;"; + paddingMarginBorderVisibility = paddingMarginBorder + "0;visibility:hidden;"; + style = "style='" + positionTopLeftWidthHeight + paddingMarginBorder + "5px solid #000;"; + html = "
" + + "" + + "
"; + + container = document.createElement("div"); + container.style.cssText = paddingMarginBorderVisibility + "width:0;height:0;position:static;top:0;margin-top:" + conMarginTop + "px"; + body.insertBefore( container, body.firstChild ); + + // Construct the test element + div = document.createElement("div"); + container.appendChild( div ); + + // Check if table cells still have offsetWidth/Height when they are set + // to display:none and there are still other visible table cells in a + // table row; if so, offsetWidth/Height are not reliable for use when + // determining if an element has been hidden directly using + // display:none (it is still safe to use offsets if a parent element is + // hidden; don safety goggles and see bug #4512 for more information). + // (only IE 8 fails this test) + div.innerHTML = "
t
"; + tds = div.getElementsByTagName( "td" ); + isSupported = ( tds[ 0 ].offsetHeight === 0 ); + + tds[ 0 ].style.display = ""; + tds[ 1 ].style.display = "none"; + + // Check if empty table cells still have offsetWidth/Height + // (IE <= 8 fail this test) + support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); + + // Check if div with explicit width and no margin-right incorrectly + // gets computed margin-right based on width of container. For more + // info see bug #3333 + // Fails in WebKit before Feb 2011 nightlies + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + if ( window.getComputedStyle ) { + div.innerHTML = ""; + marginDiv = document.createElement( "div" ); + marginDiv.style.width = "0"; + marginDiv.style.marginRight = "0"; + div.style.width = "2px"; + div.appendChild( marginDiv ); + support.reliableMarginRight = + ( parseInt( ( window.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0; + } + + if ( typeof div.style.zoom !== "undefined" ) { + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + // (IE < 8 does this) + div.innerHTML = ""; + div.style.width = div.style.padding = "1px"; + div.style.border = 0; + div.style.overflow = "hidden"; + div.style.display = "inline"; + div.style.zoom = 1; + support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 ); + + // Check if elements with layout shrink-wrap their children + // (IE 6 does this) + div.style.display = "block"; + div.style.overflow = "visible"; + div.innerHTML = "
"; + support.shrinkWrapBlocks = ( div.offsetWidth !== 3 ); + } + + div.style.cssText = positionTopLeftWidthHeight + paddingMarginBorderVisibility; + div.innerHTML = html; + + outer = div.firstChild; + inner = outer.firstChild; + td = outer.nextSibling.firstChild.firstChild; + + offsetSupport = { + doesNotAddBorder: ( inner.offsetTop !== 5 ), + doesAddBorderForTableAndCells: ( td.offsetTop === 5 ) + }; + + inner.style.position = "fixed"; + inner.style.top = "20px"; + + // safari subtracts parent border width here which is 5px + offsetSupport.fixedPosition = ( inner.offsetTop === 20 || inner.offsetTop === 15 ); + inner.style.position = inner.style.top = ""; + + outer.style.overflow = "hidden"; + outer.style.position = "relative"; + + offsetSupport.subtractsBorderForOverflowNotVisible = ( inner.offsetTop === -5 ); + offsetSupport.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== conMarginTop ); + + if ( window.getComputedStyle ) { + div.style.marginTop = "1%"; + support.pixelMargin = ( window.getComputedStyle( div, null ) || { marginTop: 0 } ).marginTop !== "1%"; + } + + if ( typeof container.style.zoom !== "undefined" ) { + container.style.zoom = 1; + } + + body.removeChild( container ); + marginDiv = div = container = null; + + jQuery.extend( support, offsetSupport ); + }); + + return support; +})(); + + + + +var rbrace = /^(?:\{.*\}|\[.*\])$/, + rmultiDash = /([A-Z])/g; + +jQuery.extend({ + cache: {}, + + // Please use with caution + uuid: 0, + + // Unique for each copy of jQuery on the page + // Non-digits removed to match rinlinejQuery + expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), + + // The following elements throw uncatchable exceptions if you + // attempt to add expando properties to them. + noData: { + "embed": true, + // Ban all objects except for Flash (which handle expandos) + "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", + "applet": true + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var privateCache, thisCache, ret, + internalKey = jQuery.expando, + getByName = typeof name === "string", + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey, + isEvents = name === "events"; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) { + return; + } + + if ( !id ) { + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + elem[ internalKey ] = id = ++jQuery.uuid; + } else { + id = internalKey; + } + } + + if ( !cache[ id ] ) { + cache[ id ] = {}; + + // Avoids exposing jQuery metadata on plain JS objects when the object + // is serialized using JSON.stringify + if ( !isNode ) { + cache[ id ].toJSON = jQuery.noop; + } + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ] = jQuery.extend( cache[ id ], name ); + } else { + cache[ id ].data = jQuery.extend( cache[ id ].data, name ); + } + } + + privateCache = thisCache = cache[ id ]; + + // jQuery data() is stored in a separate object inside the object's internal data + // cache in order to avoid key collisions between internal data and user-defined + // data. + if ( !pvt ) { + if ( !thisCache.data ) { + thisCache.data = {}; + } + + thisCache = thisCache.data; + } + + if ( data !== undefined ) { + thisCache[ jQuery.camelCase( name ) ] = data; + } + + // Users should not attempt to inspect the internal events object using jQuery.data, + // it is undocumented and subject to change. But does anyone listen? No. + if ( isEvents && !thisCache[ name ] ) { + return privateCache.events; + } + + // Check for both converted-to-camel and non-converted data property names + // If a data property was specified + if ( getByName ) { + + // First Try to find as-is property data + ret = thisCache[ name ]; + + // Test for null|undefined property data + if ( ret == null ) { + + // Try to find the camelCased property + ret = thisCache[ jQuery.camelCase( name ) ]; + } + } else { + ret = thisCache; + } + + return ret; + }, + + removeData: function( elem, name, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var thisCache, i, l, + + // Reference to internal data cache key + internalKey = jQuery.expando, + + isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + + // See jQuery.data for more information + id = isNode ? elem[ internalKey ] : internalKey; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + + thisCache = pvt ? cache[ id ] : cache[ id ].data; + + if ( thisCache ) { + + // Support array or space separated string names for data keys + if ( !jQuery.isArray( name ) ) { + + // try the string as a key before any manipulation + if ( name in thisCache ) { + name = [ name ]; + } else { + + // split the camel cased version by spaces unless a key with the spaces exists + name = jQuery.camelCase( name ); + if ( name in thisCache ) { + name = [ name ]; + } else { + name = name.split( " " ); + } + } + } + + for ( i = 0, l = name.length; i < l; i++ ) { + delete thisCache[ name[i] ]; + } + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( !pvt ) { + delete cache[ id ].data; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject(cache[ id ]) ) { + return; + } + } + + // Browsers that fail expando deletion also refuse to delete expandos on + // the window, but it will allow it on all other JS objects; other browsers + // don't care + // Ensure that `cache` is not a window object #10080 + if ( jQuery.support.deleteExpando || !cache.setInterval ) { + delete cache[ id ]; + } else { + cache[ id ] = null; + } + + // We destroyed the cache and need to eliminate the expando on the node to avoid + // false lookups in the cache for entries that no longer exist + if ( isNode ) { + // IE does not allow us to delete expando properties from nodes, + // nor does it have a removeAttribute function on Document nodes; + // we must handle all of these cases + if ( jQuery.support.deleteExpando ) { + delete elem[ internalKey ]; + } else if ( elem.removeAttribute ) { + elem.removeAttribute( internalKey ); + } else { + elem[ internalKey ] = null; + } + } + }, + + // For internal use only. + _data: function( elem, name, data ) { + return jQuery.data( elem, name, data, true ); + }, + + // A method for determining if a DOM node can handle the data expando + acceptData: function( elem ) { + if ( elem.nodeName ) { + var match = jQuery.noData[ elem.nodeName.toLowerCase() ]; + + if ( match ) { + return !(match === true || elem.getAttribute("classid") !== match); + } + } + + return true; + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var parts, part, attr, name, l, + elem = this[0], + i = 0, + data = null; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = jQuery.data( elem ); + + if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { + attr = elem.attributes; + for ( l = attr.length; i < l; i++ ) { + name = attr[i].name; + + if ( name.indexOf( "data-" ) === 0 ) { + name = jQuery.camelCase( name.substring(5) ); + + dataAttr( elem, name, data[ name ] ); + } + } + jQuery._data( elem, "parsedAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each(function() { + jQuery.data( this, key ); + }); + } + + parts = key.split( ".", 2 ); + parts[1] = parts[1] ? "." + parts[1] : ""; + part = parts[1] + "!"; + + return jQuery.access( this, function( value ) { + + if ( value === undefined ) { + data = this.triggerHandler( "getData" + part, [ parts[0] ] ); + + // Try to fetch any internally stored data first + if ( data === undefined && elem ) { + data = jQuery.data( elem, key ); + data = dataAttr( elem, key, data ); + } + + return data === undefined && parts[1] ? + this.data( parts[0] ) : + data; + } + + parts[1] = value; + this.each(function() { + var self = jQuery( this ); + + self.triggerHandler( "setData" + part, parts ); + jQuery.data( this, key, value ); + self.triggerHandler( "changeData" + part, parts ); + }); + }, null, value, arguments.length > 1, null, false ); + }, + + removeData: function( key ) { + return this.each(function() { + jQuery.removeData( this, key ); + }); + } +}); + +function dataAttr( elem, key, data ) { + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + + var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + jQuery.isNumeric( data ) ? +data : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// checks a cache object for emptiness +function isEmptyDataObject( obj ) { + for ( var name in obj ) { + + // if the public data object is empty, the private is still empty + if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { + continue; + } + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} + + + + +function handleQueueMarkDefer( elem, type, src ) { + var deferDataKey = type + "defer", + queueDataKey = type + "queue", + markDataKey = type + "mark", + defer = jQuery._data( elem, deferDataKey ); + if ( defer && + ( src === "queue" || !jQuery._data(elem, queueDataKey) ) && + ( src === "mark" || !jQuery._data(elem, markDataKey) ) ) { + // Give room for hard-coded callbacks to fire first + // and eventually mark/queue something else on the element + setTimeout( function() { + if ( !jQuery._data( elem, queueDataKey ) && + !jQuery._data( elem, markDataKey ) ) { + jQuery.removeData( elem, deferDataKey, true ); + defer.fire(); + } + }, 0 ); + } +} + +jQuery.extend({ + + _mark: function( elem, type ) { + if ( elem ) { + type = ( type || "fx" ) + "mark"; + jQuery._data( elem, type, (jQuery._data( elem, type ) || 0) + 1 ); + } + }, + + _unmark: function( force, elem, type ) { + if ( force !== true ) { + type = elem; + elem = force; + force = false; + } + if ( elem ) { + type = type || "fx"; + var key = type + "mark", + count = force ? 0 : ( (jQuery._data( elem, key ) || 1) - 1 ); + if ( count ) { + jQuery._data( elem, key, count ); + } else { + jQuery.removeData( elem, key, true ); + handleQueueMarkDefer( elem, type, "mark" ); + } + } + }, + + queue: function( elem, type, data ) { + var q; + if ( elem ) { + type = ( type || "fx" ) + "queue"; + q = jQuery._data( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !q || jQuery.isArray(data) ) { + q = jQuery._data( elem, type, jQuery.makeArray(data) ); + } else { + q.push( data ); + } + } + return q || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + fn = queue.shift(), + hooks = {}; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + } + + if ( fn ) { + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + jQuery._data( elem, type + ".run", hooks ); + fn.call( elem, function() { + jQuery.dequeue( elem, type ); + }, hooks ); + } + + if ( !queue.length ) { + jQuery.removeData( elem, type + "queue " + type + ".run", true ); + handleQueueMarkDefer( elem, type, "queue" ); + } + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[0], type ); + } + + return data === undefined ? + this : + this.each(function() { + var queue = jQuery.queue( this, type, data ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + // Based off of the plugin by Clint Helfers, with permission. + // http://blindsignals.com/index.php/2009/07/jquery-delay/ + delay: function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = setTimeout( next, time ); + hooks.stop = function() { + clearTimeout( timeout ); + }; + }); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, object ) { + if ( typeof type !== "string" ) { + object = type; + type = undefined; + } + type = type || "fx"; + var defer = jQuery.Deferred(), + elements = this, + i = elements.length, + count = 1, + deferDataKey = type + "defer", + queueDataKey = type + "queue", + markDataKey = type + "mark", + tmp; + function resolve() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + } + while( i-- ) { + if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) || + ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) || + jQuery.data( elements[ i ], markDataKey, undefined, true ) ) && + jQuery.data( elements[ i ], deferDataKey, jQuery.Callbacks( "once memory" ), true ) )) { + count++; + tmp.add( resolve ); + } + } + resolve(); + return defer.promise( object ); + } +}); + + + + +var rclass = /[\n\t\r]/g, + rspace = /\s+/, + rreturn = /\r/g, + rtype = /^(?:button|input)$/i, + rfocusable = /^(?:button|input|object|select|textarea)$/i, + rclickable = /^a(?:rea)?$/i, + rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, + getSetAttribute = jQuery.support.getSetAttribute, + nodeHook, boolHook, fixSpecified; + +jQuery.fn.extend({ + attr: function( name, value ) { + return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each(function() { + jQuery.removeAttr( this, name ); + }); + }, + + prop: function( name, value ) { + return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + name = jQuery.propFix[ name ] || name; + return this.each(function() { + // try/catch handles cases where IE balks (such as removing a property on window) + try { + this[ name ] = undefined; + delete this[ name ]; + } catch( e ) {} + }); + }, + + addClass: function( value ) { + var classNames, i, l, elem, + setClass, c, cl; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).addClass( value.call(this, j, this.className) ); + }); + } + + if ( value && typeof value === "string" ) { + classNames = value.split( rspace ); + + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; + + if ( elem.nodeType === 1 ) { + if ( !elem.className && classNames.length === 1 ) { + elem.className = value; + + } else { + setClass = " " + elem.className + " "; + + for ( c = 0, cl = classNames.length; c < cl; c++ ) { + if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) { + setClass += classNames[ c ] + " "; + } + } + elem.className = jQuery.trim( setClass ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classNames, i, l, elem, className, c, cl; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).removeClass( value.call(this, j, this.className) ); + }); + } + + if ( (value && typeof value === "string") || value === undefined ) { + classNames = ( value || "" ).split( rspace ); + + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; + + if ( elem.nodeType === 1 && elem.className ) { + if ( value ) { + className = (" " + elem.className + " ").replace( rclass, " " ); + for ( c = 0, cl = classNames.length; c < cl; c++ ) { + className = className.replace(" " + classNames[ c ] + " ", " "); + } + elem.className = jQuery.trim( className ); + + } else { + elem.className = ""; + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isBool = typeof stateVal === "boolean"; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( i ) { + jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); + }); + } + + return this.each(function() { + if ( type === "string" ) { + // toggle individual class names + var className, + i = 0, + self = jQuery( this ), + state = stateVal, + classNames = value.split( rspace ); + + while ( (className = classNames[ i++ ]) ) { + // check each className given, space seperated list + state = isBool ? state : !self.hasClass( className ); + self[ state ? "addClass" : "removeClass" ]( className ); + } + + } else if ( type === "undefined" || type === "boolean" ) { + if ( this.className ) { + // store className if set + jQuery._data( this, "__className__", this.className ); + } + + // toggle whole className + this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; + } + }); + }, + + hasClass: function( selector ) { + var className = " " + selector + " ", + i = 0, + l = this.length; + for ( ; i < l; i++ ) { + if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { + return true; + } + } + + return false; + }, + + val: function( value ) { + var hooks, ret, isFunction, + elem = this[0]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { + return ret; + } + + ret = elem.value; + + return typeof ret === "string" ? + // handle most common string cases + ret.replace(rreturn, "") : + // handle cases where value is null/undef or number + ret == null ? "" : ret; + } + + return; + } + + isFunction = jQuery.isFunction( value ); + + return this.each(function( i ) { + var self = jQuery(this), val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call( this, i, self.val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + } else if ( typeof val === "number" ) { + val += ""; + } else if ( jQuery.isArray( val ) ) { + val = jQuery.map(val, function ( value ) { + return value == null ? "" : value + ""; + }); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + }); + } +}); + +jQuery.extend({ + valHooks: { + option: { + get: function( elem ) { + // attributes.value is undefined in Blackberry 4.7 but + // uses .value. See #6932 + var val = elem.attributes.value; + return !val || val.specified ? elem.value : elem.text; + } + }, + select: { + get: function( elem ) { + var value, i, max, option, + index = elem.selectedIndex, + values = [], + options = elem.options, + one = elem.type === "select-one"; + + // Nothing was selected + if ( index < 0 ) { + return null; + } + + // Loop through all the selected options + i = one ? index : 0; + max = one ? index + 1 : options.length; + for ( ; i < max; i++ ) { + option = options[ i ]; + + // Don't return options that are disabled or in a disabled optgroup + if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && + (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + // Fixes Bug #2551 -- select.val() broken in IE after form.reset() + if ( one && !values.length && options.length ) { + return jQuery( options[ index ] ).val(); + } + + return values; + }, + + set: function( elem, value ) { + var values = jQuery.makeArray( value ); + + jQuery(elem).find("option").each(function() { + this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; + }); + + if ( !values.length ) { + elem.selectedIndex = -1; + } + return values; + } + } + }, + + attrFn: { + val: true, + css: true, + html: true, + text: true, + data: true, + width: true, + height: true, + offset: true + }, + + attr: function( elem, name, value, pass ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set attributes on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( pass && name in jQuery.attrFn ) { + return jQuery( elem )[ name ]( value ); + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + // All attributes are lowercase + // Grab necessary hook if one is defined + if ( notxml ) { + name = name.toLowerCase(); + hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook ); + } + + if ( value !== undefined ) { + + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + + } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + elem.setAttribute( name, "" + value ); + return value; + } + + } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + + ret = elem.getAttribute( name ); + + // Non-existent attributes return null, we normalize to undefined + return ret === null ? + undefined : + ret; + } + }, + + removeAttr: function( elem, value ) { + var propName, attrNames, name, l, isBool, + i = 0; + + if ( value && elem.nodeType === 1 ) { + attrNames = value.toLowerCase().split( rspace ); + l = attrNames.length; + + for ( ; i < l; i++ ) { + name = attrNames[ i ]; + + if ( name ) { + propName = jQuery.propFix[ name ] || name; + isBool = rboolean.test( name ); + + // See #9699 for explanation of this approach (setting first, then removal) + // Do not do this for boolean attributes (see #10870) + if ( !isBool ) { + jQuery.attr( elem, name, "" ); + } + elem.removeAttribute( getSetAttribute ? name : propName ); + + // Set corresponding property to false for boolean attributes + if ( isBool && propName in elem ) { + elem[ propName ] = false; + } + } + } + } + }, + + attrHooks: { + type: { + set: function( elem, value ) { + // We can't allow the type property to be changed (since it causes problems in IE) + if ( rtype.test( elem.nodeName ) && elem.parentNode ) { + jQuery.error( "type property can't be changed" ); + } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { + // Setting the type on a radio button after the value resets the value in IE6-9 + // Reset value to it's default in case type is set after value + // This is for element creation + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + }, + // Use the value property for back compat + // Use the nodeHook for button elements in IE6/7 (#1954) + value: { + get: function( elem, name ) { + if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { + return nodeHook.get( elem, name ); + } + return name in elem ? + elem.value : + null; + }, + set: function( elem, value, name ) { + if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { + return nodeHook.set( elem, value, name ); + } + // Does not return so that setAttribute is also used + elem.value = value; + } + } + }, + + propFix: { + tabindex: "tabIndex", + readonly: "readOnly", + "for": "htmlFor", + "class": "className", + maxlength: "maxLength", + cellspacing: "cellSpacing", + cellpadding: "cellPadding", + rowspan: "rowSpan", + colspan: "colSpan", + usemap: "useMap", + frameborder: "frameBorder", + contenteditable: "contentEditable" + }, + + prop: function( elem, name, value ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set properties on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + if ( notxml ) { + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + return ( elem[ name ] = value ); + } + + } else { + if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + return elem[ name ]; + } + } + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set + // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + var attributeNode = elem.getAttributeNode("tabindex"); + + return attributeNode && attributeNode.specified ? + parseInt( attributeNode.value, 10 ) : + rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? + 0 : + undefined; + } + } + } +}); + +// Add the tabIndex propHook to attrHooks for back-compat (different case is intentional) +jQuery.attrHooks.tabindex = jQuery.propHooks.tabIndex; + +// Hook for boolean attributes +boolHook = { + get: function( elem, name ) { + // Align boolean attributes with corresponding properties + // Fall back to attribute presence where some booleans are not supported + var attrNode, + property = jQuery.prop( elem, name ); + return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ? + name.toLowerCase() : + undefined; + }, + set: function( elem, value, name ) { + var propName; + if ( value === false ) { + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + // value is true since we know at this point it's type boolean and not false + // Set boolean attributes to the same name and set the DOM property + propName = jQuery.propFix[ name ] || name; + if ( propName in elem ) { + // Only set the IDL specifically if it already exists on the element + elem[ propName ] = true; + } + + elem.setAttribute( name, name.toLowerCase() ); + } + return name; + } +}; + +// IE6/7 do not support getting/setting some attributes with get/setAttribute +if ( !getSetAttribute ) { + + fixSpecified = { + name: true, + id: true, + coords: true + }; + + // Use this for any attribute in IE6/7 + // This fixes almost every IE6/7 issue + nodeHook = jQuery.valHooks.button = { + get: function( elem, name ) { + var ret; + ret = elem.getAttributeNode( name ); + return ret && ( fixSpecified[ name ] ? ret.nodeValue !== "" : ret.specified ) ? + ret.nodeValue : + undefined; + }, + set: function( elem, value, name ) { + // Set the existing or create a new attribute node + var ret = elem.getAttributeNode( name ); + if ( !ret ) { + ret = document.createAttribute( name ); + elem.setAttributeNode( ret ); + } + return ( ret.nodeValue = value + "" ); + } + }; + + // Apply the nodeHook to tabindex + jQuery.attrHooks.tabindex.set = nodeHook.set; + + // Set width and height to auto instead of 0 on empty string( Bug #8150 ) + // This is for removals + jQuery.each([ "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + set: function( elem, value ) { + if ( value === "" ) { + elem.setAttribute( name, "auto" ); + return value; + } + } + }); + }); + + // Set contenteditable to false on removals(#10429) + // Setting to empty string throws an error as an invalid value + jQuery.attrHooks.contenteditable = { + get: nodeHook.get, + set: function( elem, value, name ) { + if ( value === "" ) { + value = "false"; + } + nodeHook.set( elem, value, name ); + } + }; +} + + +// Some attributes require a special call on IE +if ( !jQuery.support.hrefNormalized ) { + jQuery.each([ "href", "src", "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + get: function( elem ) { + var ret = elem.getAttribute( name, 2 ); + return ret === null ? undefined : ret; + } + }); + }); +} + +if ( !jQuery.support.style ) { + jQuery.attrHooks.style = { + get: function( elem ) { + // Return undefined in the case of empty string + // Normalize to lowercase since IE uppercases css property names + return elem.style.cssText.toLowerCase() || undefined; + }, + set: function( elem, value ) { + return ( elem.style.cssText = "" + value ); + } + }; +} + +// Safari mis-reports the default selected property of an option +// Accessing the parent's selectedIndex property fixes it +if ( !jQuery.support.optSelected ) { + jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, { + get: function( elem ) { + var parent = elem.parentNode; + + if ( parent ) { + parent.selectedIndex; + + // Make sure that it also works with optgroups, see #5701 + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + return null; + } + }); +} + +// IE6/7 call enctype encoding +if ( !jQuery.support.enctype ) { + jQuery.propFix.enctype = "encoding"; +} + +// Radios and checkboxes getter/setter +if ( !jQuery.support.checkOn ) { + jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + get: function( elem ) { + // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified + return elem.getAttribute("value") === null ? "on" : elem.value; + } + }; + }); +} +jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], { + set: function( elem, value ) { + if ( jQuery.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); + } + } + }); +}); + + + + +var rformElems = /^(?:textarea|input|select)$/i, + rtypenamespace = /^([^\.]*)?(?:\.(.+))?$/, + rhoverHack = /(?:^|\s)hover(\.\S+)?\b/, + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|contextmenu)|click/, + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + rquickIs = /^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/, + quickParse = function( selector ) { + var quick = rquickIs.exec( selector ); + if ( quick ) { + // 0 1 2 3 + // [ _, tag, id, class ] + quick[1] = ( quick[1] || "" ).toLowerCase(); + quick[3] = quick[3] && new RegExp( "(?:^|\\s)" + quick[3] + "(?:\\s|$)" ); + } + return quick; + }, + quickIs = function( elem, m ) { + var attrs = elem.attributes || {}; + return ( + (!m[1] || elem.nodeName.toLowerCase() === m[1]) && + (!m[2] || (attrs.id || {}).value === m[2]) && + (!m[3] || m[3].test( (attrs[ "class" ] || {}).value )) + ); + }, + hoverHack = function( events ) { + return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" ); + }; + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + add: function( elem, types, handler, data, selector ) { + + var elemData, eventHandle, events, + t, tns, type, namespaces, handleObj, + handleObjIn, quick, handlers, special; + + // Don't attach events to noData or text/comment nodes (allow plain objects tho) + if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + events = elemData.events; + if ( !events ) { + elemData.events = events = {}; + } + eventHandle = elemData.handle; + if ( !eventHandle ) { + elemData.handle = eventHandle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ? + jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : + undefined; + }; + // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events + eventHandle.elem = elem; + } + + // Handle multiple events separated by a space + // jQuery(...).bind("mouseover mouseout", fn); + types = jQuery.trim( hoverHack(types) ).split( " " ); + for ( t = 0; t < types.length; t++ ) { + + tns = rtypenamespace.exec( types[t] ) || []; + type = tns[1]; + namespaces = ( tns[2] || "" ).split( "." ).sort(); + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend({ + type: type, + origType: tns[1], + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + quick: selector && quickParse( selector ), + namespace: namespaces.join(".") + }, handleObjIn ); + + // Init the event handler queue if we're the first + handlers = events[ type ]; + if ( !handlers ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener/attachEvent if the special events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + global: {}, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var elemData = jQuery.hasData( elem ) && jQuery._data( elem ), + t, tns, type, origType, namespaces, origCount, + j, events, special, handle, eventType, handleObj; + + if ( !elemData || !(events = elemData.events) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = jQuery.trim( hoverHack( types || "" ) ).split(" "); + for ( t = 0; t < types.length; t++ ) { + tns = rtypenamespace.exec( types[t] ) || []; + type = origType = tns[1]; + namespaces = tns[2]; + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector? special.delegateType : special.bindType ) || type; + eventType = events[ type ] || []; + origCount = eventType.length; + namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.)?") + "(\\.|$)") : null; + + // Remove matching events + for ( j = 0; j < eventType.length; j++ ) { + handleObj = eventType[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !namespaces || namespaces.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { + eventType.splice( j--, 1 ); + + if ( handleObj.selector ) { + eventType.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( eventType.length === 0 && origCount !== eventType.length ) { + if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + handle = elemData.handle; + if ( handle ) { + handle.elem = null; + } + + // removeData also checks for emptiness and clears the expando if empty + // so use it instead of delete + jQuery.removeData( elem, [ "events", "handle" ], true ); + } + }, + + // Events that are safe to short-circuit if no handlers are attached. + // Native DOM events should not be added, they may have inline handlers. + customEvent: { + "getData": true, + "setData": true, + "changeData": true + }, + + trigger: function( event, data, elem, onlyHandlers ) { + // Don't do events on text and comment nodes + if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) { + return; + } + + // Event object or event type + var type = event.type || event, + namespaces = [], + cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType; + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "!" ) >= 0 ) { + // Exclusive events trigger only for the exact event (no namespaces) + type = type.slice(0, -1); + exclusive = true; + } + + if ( type.indexOf( "." ) >= 0 ) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + + if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) { + // No jQuery handlers for this event type, and it can't have inline handlers + return; + } + + // Caller can pass in an Event, Object, or just an event type string + event = typeof event === "object" ? + // jQuery.Event object + event[ jQuery.expando ] ? event : + // Object literal + new jQuery.Event( type, event ) : + // Just the event type (string) + new jQuery.Event( type ); + + event.type = type; + event.isTrigger = true; + event.exclusive = exclusive; + event.namespace = namespaces.join( "." ); + event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)") : null; + ontype = type.indexOf( ":" ) < 0 ? "on" + type : ""; + + // Handle a global trigger + if ( !elem ) { + + // TODO: Stop taunting the data cache; remove global events and always attach to document + cache = jQuery.cache; + for ( i in cache ) { + if ( cache[ i ].events && cache[ i ].events[ type ] ) { + jQuery.event.trigger( event, data, cache[ i ].handle.elem, true ); + } + } + return; + } + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data != null ? jQuery.makeArray( data ) : []; + data.unshift( event ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + eventPath = [[ elem, special.bindType || type ]]; + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode; + old = null; + for ( ; cur; cur = cur.parentNode ) { + eventPath.push([ cur, bubbleType ]); + old = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( old && old === elem.ownerDocument ) { + eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]); + } + } + + // Fire handlers on the event path + for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) { + + cur = eventPath[i][0]; + event.type = eventPath[i][1]; + + handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + // Note that this is a bare JS function and not a jQuery handler + handle = ontype && cur[ ontype ]; + if ( handle && jQuery.acceptData( cur ) && handle.apply( cur, data ) === false ) { + event.preventDefault(); + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) && + !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Can't use an .isFunction() check here because IE6/7 fails that test. + // Don't do default actions on window, that's where global variables be (#6170) + // IE<9 dies on focus/blur to hidden element (#1486) + if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + old = elem[ ontype ]; + + if ( old ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + elem[ type ](); + jQuery.event.triggered = undefined; + + if ( old ) { + elem[ ontype ] = old; + } + } + } + } + + return event.result; + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event || window.event ); + + var handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []), + delegateCount = handlers.delegateCount, + args = [].slice.call( arguments, 0 ), + run_all = !event.exclusive && !event.namespace, + special = jQuery.event.special[ event.type ] || {}, + handlerQueue = [], + i, j, cur, jqcur, ret, selMatch, matched, matches, handleObj, sel, related; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[0] = event; + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers that should run if there are delegated events + // Avoid non-left-click bubbling in Firefox (#3861) + if ( delegateCount && !(event.button && event.type === "click") ) { + + // Pregenerate a single jQuery object for reuse with .is() + jqcur = jQuery(this); + jqcur.context = this.ownerDocument || this; + + for ( cur = event.target; cur != this; cur = cur.parentNode || this ) { + + // Don't process events on disabled elements (#6911, #8165) + if ( cur.disabled !== true ) { + selMatch = {}; + matches = []; + jqcur[0] = cur; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + sel = handleObj.selector; + + if ( selMatch[ sel ] === undefined ) { + selMatch[ sel ] = ( + handleObj.quick ? quickIs( cur, handleObj.quick ) : jqcur.is( sel ) + ); + } + if ( selMatch[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push({ elem: cur, matches: matches }); + } + } + } + } + + // Add the remaining (directly-bound) handlers + if ( handlers.length > delegateCount ) { + handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) }); + } + + // Run delegates first; they may want to stop propagation beneath us + for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) { + matched = handlerQueue[ i ]; + event.currentTarget = matched.elem; + + for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) { + handleObj = matched.matches[ j ]; + + // Triggered event must either 1) be non-exclusive and have no namespace, or + // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). + if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) { + + event.data = handleObj.data; + event.handleObj = handleObj; + + ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) + .apply( matched.elem, args ); + + if ( ret !== undefined ) { + event.result = ret; + if ( ret === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + // *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 *** + props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split(" "), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), + filter: function( event, original ) { + var eventDoc, doc, body, + button = original.button, + fromElement = original.fromElement; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && fromElement ) { + event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, + originalEvent = event, + fixHook = jQuery.event.fixHooks[ event.type ] || {}, + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = jQuery.Event( originalEvent ); + + for ( i = copy.length; i; ) { + prop = copy[ --i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2) + if ( !event.target ) { + event.target = originalEvent.srcElement || document; + } + + // Target should not be a text node (#504, Safari) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // For mouse/key events; add metaKey if it's not there (#3368, IE6/7/8) + if ( event.metaKey === undefined ) { + event.metaKey = event.ctrlKey; + } + + return fixHook.filter? fixHook.filter( event, originalEvent ) : event; + }, + + special: { + ready: { + // Make sure the ready event is setup + setup: jQuery.bindReady + }, + + load: { + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + + focus: { + delegateType: "focusin" + }, + blur: { + delegateType: "focusout" + }, + + beforeunload: { + setup: function( data, namespaces, eventHandle ) { + // We only want to do this special case on windows + if ( jQuery.isWindow( this ) ) { + this.onbeforeunload = eventHandle; + } + }, + + teardown: function( namespaces, eventHandle ) { + if ( this.onbeforeunload === eventHandle ) { + this.onbeforeunload = null; + } + } + } + }, + + simulate: function( type, elem, event, bubble ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + var e = jQuery.extend( + new jQuery.Event(), + event, + { type: type, + isSimulated: true, + originalEvent: {} + } + ); + if ( bubble ) { + jQuery.event.trigger( e, null, elem ); + } else { + jQuery.event.dispatch.call( elem, e ); + } + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; + +// Some plugins are using, but it's undocumented/deprecated and will be removed. +// The 1.7 special event interface should provide all the hooks needed now. +jQuery.event.handle = jQuery.event.dispatch; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + } : + function( elem, type, handle ) { + if ( elem.detachEvent ) { + elem.detachEvent( "on" + type, handle ); + } + }; + +jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword + if ( !(this instanceof jQuery.Event) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false || + src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +function returnFalse() { + return false; +} +function returnTrue() { + return true; +} + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + preventDefault: function() { + this.isDefaultPrevented = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + + // if preventDefault exists run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // otherwise set the returnValue property of the original event to false (IE) + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + this.isPropagationStopped = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + // if stopPropagation exists run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + // otherwise set the cancelBubble property of the original event to true (IE) + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + }, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var target = this, + related = event.relatedTarget, + handleObj = event.handleObj, + selector = handleObj.selector, + ret; + + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || (related !== target && !jQuery.contains( target, related )) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +}); + +// IE submit delegation +if ( !jQuery.support.submitBubbles ) { + + jQuery.event.special.submit = { + setup: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Lazy-add a submit handler when a descendant form may potentially be submitted + jQuery.event.add( this, "click._submit keypress._submit", function( e ) { + // Node name check avoids a VML-related crash in IE (#9807) + var elem = e.target, + form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; + if ( form && !form._submit_attached ) { + jQuery.event.add( form, "submit._submit", function( event ) { + event._submit_bubble = true; + }); + form._submit_attached = true; + } + }); + // return undefined since we don't need an event listener + }, + + postDispatch: function( event ) { + // If form was submitted by the user, bubble the event up the tree + if ( event._submit_bubble ) { + delete event._submit_bubble; + if ( this.parentNode && !event.isTrigger ) { + jQuery.event.simulate( "submit", this.parentNode, event, true ); + } + } + }, + + teardown: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Remove delegated handlers; cleanData eventually reaps submit handlers attached above + jQuery.event.remove( this, "._submit" ); + } + }; +} + +// IE change delegation and checkbox/radio fix +if ( !jQuery.support.changeBubbles ) { + + jQuery.event.special.change = { + + setup: function() { + + if ( rformElems.test( this.nodeName ) ) { + // IE doesn't fire change on a check/radio until blur; trigger it on click + // after a propertychange. Eat the blur-change in special.change.handle. + // This still fires onchange a second time for check/radio after blur. + if ( this.type === "checkbox" || this.type === "radio" ) { + jQuery.event.add( this, "propertychange._change", function( event ) { + if ( event.originalEvent.propertyName === "checked" ) { + this._just_changed = true; + } + }); + jQuery.event.add( this, "click._change", function( event ) { + if ( this._just_changed && !event.isTrigger ) { + this._just_changed = false; + jQuery.event.simulate( "change", this, event, true ); + } + }); + } + return false; + } + // Delegated event; lazy-add a change handler on descendant inputs + jQuery.event.add( this, "beforeactivate._change", function( e ) { + var elem = e.target; + + if ( rformElems.test( elem.nodeName ) && !elem._change_attached ) { + jQuery.event.add( elem, "change._change", function( event ) { + if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { + jQuery.event.simulate( "change", this.parentNode, event, true ); + } + }); + elem._change_attached = true; + } + }); + }, + + handle: function( event ) { + var elem = event.target; + + // Swallow native change events from checkbox/radio, we already triggered them above + if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { + return event.handleObj.handler.apply( this, arguments ); + } + }, + + teardown: function() { + jQuery.event.remove( this, "._change" ); + + return rformElems.test( this.nodeName ); + } + }; +} + +// Create "bubbling" focus and blur events +if ( !jQuery.support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler while someone wants focusin/focusout + var attaches = 0, + handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + if ( attaches++ === 0 ) { + document.addEventListener( orig, handler, true ); + } + }, + teardown: function() { + if ( --attaches === 0 ) { + document.removeEventListener( orig, handler, true ); + } + } + }; + }); +} + +jQuery.fn.extend({ + + on: function( types, selector, data, fn, /*INTERNAL*/ one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { // && selector != null + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + this.on( type, selector, data, types[ type ], one ); + } + return this; + } + + if ( data == null && fn == null ) { + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return this; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return this.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + }); + }, + one: function( types, selector, data, fn ) { + return this.on( types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + if ( types && types.preventDefault && types.handleObj ) { + // ( event ) dispatched jQuery.Event + var handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + // ( types-object [, selector] ) + for ( var type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each(function() { + jQuery.event.remove( this, types, fn, selector ); + }); + }, + + bind: function( types, data, fn ) { + return this.on( types, null, data, fn ); + }, + unbind: function( types, fn ) { + return this.off( types, null, fn ); + }, + + live: function( types, data, fn ) { + jQuery( this.context ).on( types, this.selector, data, fn ); + return this; + }, + die: function( types, fn ) { + jQuery( this.context ).off( types, this.selector || "**", fn ); + return this; + }, + + delegate: function( selector, types, data, fn ) { + return this.on( types, selector, data, fn ); + }, + undelegate: function( selector, types, fn ) { + // ( namespace ) or ( selector, types [, fn] ) + return arguments.length == 1? this.off( selector, "**" ) : this.off( types, selector, fn ); + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + triggerHandler: function( type, data ) { + if ( this[0] ) { + return jQuery.event.trigger( type, data, this[0], true ); + } + }, + + toggle: function( fn ) { + // Save reference to arguments for access in closure + var args = arguments, + guid = fn.guid || jQuery.guid++, + i = 0, + toggler = function( event ) { + // Figure out which function to execute + var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i; + jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 ); + + // Make sure that clicks stop + event.preventDefault(); + + // and execute the function + return args[ lastToggle ].apply( this, arguments ) || false; + }; + + // link all the functions, so any of them can unbind this click handler + toggler.guid = guid; + while ( i < args.length ) { + args[ i++ ].guid = guid; + } + + return this.click( toggler ); + }, + + hover: function( fnOver, fnOut ) { + return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); + } +}); + +jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + + "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) { + + // Handle event binding + jQuery.fn[ name ] = function( data, fn ) { + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.on( name, null, data, fn ) : + this.trigger( name ); + }; + + if ( jQuery.attrFn ) { + jQuery.attrFn[ name ] = true; + } + + if ( rkeyEvent.test( name ) ) { + jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks; + } + + if ( rmouseEvent.test( name ) ) { + jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks; + } +}); + + + +/*! + * Sizzle CSS Selector Engine + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){ + +var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, + expando = "sizcache" + (Math.random() + '').replace('.', ''), + done = 0, + toString = Object.prototype.toString, + hasDuplicate = false, + baseHasDuplicate = true, + rBackslash = /\\/g, + rReturn = /\r\n/g, + rNonWord = /\W/; + +// Here we check if the JavaScript engine is using some sort of +// optimization where it does not always call our comparision +// function. If that is the case, discard the hasDuplicate value. +// Thus far that includes Google Chrome. +[0, 0].sort(function() { + baseHasDuplicate = false; + return 0; +}); + +var Sizzle = function( selector, context, results, seed ) { + results = results || []; + context = context || document; + + var origContext = context; + + if ( context.nodeType !== 1 && context.nodeType !== 9 ) { + return []; + } + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + var m, set, checkSet, extra, ret, cur, pop, i, + prune = true, + contextXML = Sizzle.isXML( context ), + parts = [], + soFar = selector; + + // Reset the position of the chunker regexp (start from head) + do { + chunker.exec( "" ); + m = chunker.exec( soFar ); + + if ( m ) { + soFar = m[3]; + + parts.push( m[1] ); + + if ( m[2] ) { + extra = m[3]; + break; + } + } + } while ( m ); + + if ( parts.length > 1 && origPOS.exec( selector ) ) { + + if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { + set = posProcess( parts[0] + parts[1], context, seed ); + + } else { + set = Expr.relative[ parts[0] ] ? + [ context ] : + Sizzle( parts.shift(), context ); + + while ( parts.length ) { + selector = parts.shift(); + + if ( Expr.relative[ selector ] ) { + selector += parts.shift(); + } + + set = posProcess( selector, set, seed ); + } + } + + } else { + // Take a shortcut and set the context if the root selector is an ID + // (but not if it'll be faster if the inner selector is an ID) + if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && + Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { + + ret = Sizzle.find( parts.shift(), context, contextXML ); + context = ret.expr ? + Sizzle.filter( ret.expr, ret.set )[0] : + ret.set[0]; + } + + if ( context ) { + ret = seed ? + { expr: parts.pop(), set: makeArray(seed) } : + Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); + + set = ret.expr ? + Sizzle.filter( ret.expr, ret.set ) : + ret.set; + + if ( parts.length > 0 ) { + checkSet = makeArray( set ); + + } else { + prune = false; + } + + while ( parts.length ) { + cur = parts.pop(); + pop = cur; + + if ( !Expr.relative[ cur ] ) { + cur = ""; + } else { + pop = parts.pop(); + } + + if ( pop == null ) { + pop = context; + } + + Expr.relative[ cur ]( checkSet, pop, contextXML ); + } + + } else { + checkSet = parts = []; + } + } + + if ( !checkSet ) { + checkSet = set; + } + + if ( !checkSet ) { + Sizzle.error( cur || selector ); + } + + if ( toString.call(checkSet) === "[object Array]" ) { + if ( !prune ) { + results.push.apply( results, checkSet ); + + } else if ( context && context.nodeType === 1 ) { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) { + results.push( set[i] ); + } + } + + } else { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && checkSet[i].nodeType === 1 ) { + results.push( set[i] ); + } + } + } + + } else { + makeArray( checkSet, results ); + } + + if ( extra ) { + Sizzle( extra, origContext, results, seed ); + Sizzle.uniqueSort( results ); + } + + return results; +}; + +Sizzle.uniqueSort = function( results ) { + if ( sortOrder ) { + hasDuplicate = baseHasDuplicate; + results.sort( sortOrder ); + + if ( hasDuplicate ) { + for ( var i = 1; i < results.length; i++ ) { + if ( results[i] === results[ i - 1 ] ) { + results.splice( i--, 1 ); + } + } + } + } + + return results; +}; + +Sizzle.matches = function( expr, set ) { + return Sizzle( expr, null, null, set ); +}; + +Sizzle.matchesSelector = function( node, expr ) { + return Sizzle( expr, null, null, [node] ).length > 0; +}; + +Sizzle.find = function( expr, context, isXML ) { + var set, i, len, match, type, left; + + if ( !expr ) { + return []; + } + + for ( i = 0, len = Expr.order.length; i < len; i++ ) { + type = Expr.order[i]; + + if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { + left = match[1]; + match.splice( 1, 1 ); + + if ( left.substr( left.length - 1 ) !== "\\" ) { + match[1] = (match[1] || "").replace( rBackslash, "" ); + set = Expr.find[ type ]( match, context, isXML ); + + if ( set != null ) { + expr = expr.replace( Expr.match[ type ], "" ); + break; + } + } + } + } + + if ( !set ) { + set = typeof context.getElementsByTagName !== "undefined" ? + context.getElementsByTagName( "*" ) : + []; + } + + return { set: set, expr: expr }; +}; + +Sizzle.filter = function( expr, set, inplace, not ) { + var match, anyFound, + type, found, item, filter, left, + i, pass, + old = expr, + result = [], + curLoop = set, + isXMLFilter = set && set[0] && Sizzle.isXML( set[0] ); + + while ( expr && set.length ) { + for ( type in Expr.filter ) { + if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) { + filter = Expr.filter[ type ]; + left = match[1]; + + anyFound = false; + + match.splice(1,1); + + if ( left.substr( left.length - 1 ) === "\\" ) { + continue; + } + + if ( curLoop === result ) { + result = []; + } + + if ( Expr.preFilter[ type ] ) { + match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); + + if ( !match ) { + anyFound = found = true; + + } else if ( match === true ) { + continue; + } + } + + if ( match ) { + for ( i = 0; (item = curLoop[i]) != null; i++ ) { + if ( item ) { + found = filter( item, match, i, curLoop ); + pass = not ^ found; + + if ( inplace && found != null ) { + if ( pass ) { + anyFound = true; + + } else { + curLoop[i] = false; + } + + } else if ( pass ) { + result.push( item ); + anyFound = true; + } + } + } + } + + if ( found !== undefined ) { + if ( !inplace ) { + curLoop = result; + } + + expr = expr.replace( Expr.match[ type ], "" ); + + if ( !anyFound ) { + return []; + } + + break; + } + } + } + + // Improper expression + if ( expr === old ) { + if ( anyFound == null ) { + Sizzle.error( expr ); + + } else { + break; + } + } + + old = expr; + } + + return curLoop; +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Utility function for retreiving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +var getText = Sizzle.getText = function( elem ) { + var i, node, + nodeType = elem.nodeType, + ret = ""; + + if ( nodeType ) { + if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent || innerText for elements + if ( typeof elem.textContent === 'string' ) { + return elem.textContent; + } else if ( typeof elem.innerText === 'string' ) { + // Replace IE's carriage returns + return elem.innerText.replace( rReturn, '' ); + } else { + // Traverse it's children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + } else { + + // If no nodeType, this is expected to be an array + for ( i = 0; (node = elem[i]); i++ ) { + // Do not traverse comment nodes + if ( node.nodeType !== 8 ) { + ret += getText( node ); + } + } + } + return ret; +}; + +var Expr = Sizzle.selectors = { + order: [ "ID", "NAME", "TAG" ], + + match: { + ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/, + ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/, + TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/, + CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/, + POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/, + PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ + }, + + leftMatch: {}, + + attrMap: { + "class": "className", + "for": "htmlFor" + }, + + attrHandle: { + href: function( elem ) { + return elem.getAttribute( "href" ); + }, + type: function( elem ) { + return elem.getAttribute( "type" ); + } + }, + + relative: { + "+": function(checkSet, part){ + var isPartStr = typeof part === "string", + isTag = isPartStr && !rNonWord.test( part ), + isPartStrNotTag = isPartStr && !isTag; + + if ( isTag ) { + part = part.toLowerCase(); + } + + for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { + if ( (elem = checkSet[i]) ) { + while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} + + checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? + elem || false : + elem === part; + } + } + + if ( isPartStrNotTag ) { + Sizzle.filter( part, checkSet, true ); + } + }, + + ">": function( checkSet, part ) { + var elem, + isPartStr = typeof part === "string", + i = 0, + l = checkSet.length; + + if ( isPartStr && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + var parent = elem.parentNode; + checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; + } + } + + } else { + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + checkSet[i] = isPartStr ? + elem.parentNode : + elem.parentNode === part; + } + } + + if ( isPartStr ) { + Sizzle.filter( part, checkSet, true ); + } + } + }, + + "": function(checkSet, part, isXML){ + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML ); + }, + + "~": function( checkSet, part, isXML ) { + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML ); + } + }, + + find: { + ID: function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [m] : []; + } + }, + + NAME: function( match, context ) { + if ( typeof context.getElementsByName !== "undefined" ) { + var ret = [], + results = context.getElementsByName( match[1] ); + + for ( var i = 0, l = results.length; i < l; i++ ) { + if ( results[i].getAttribute("name") === match[1] ) { + ret.push( results[i] ); + } + } + + return ret.length === 0 ? null : ret; + } + }, + + TAG: function( match, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( match[1] ); + } + } + }, + preFilter: { + CLASS: function( match, curLoop, inplace, result, not, isXML ) { + match = " " + match[1].replace( rBackslash, "" ) + " "; + + if ( isXML ) { + return match; + } + + for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { + if ( elem ) { + if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) { + if ( !inplace ) { + result.push( elem ); + } + + } else if ( inplace ) { + curLoop[i] = false; + } + } + } + + return false; + }, + + ID: function( match ) { + return match[1].replace( rBackslash, "" ); + }, + + TAG: function( match, curLoop ) { + return match[1].replace( rBackslash, "" ).toLowerCase(); + }, + + CHILD: function( match ) { + if ( match[1] === "nth" ) { + if ( !match[2] ) { + Sizzle.error( match[0] ); + } + + match[2] = match[2].replace(/^\+|\s*/g, ''); + + // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' + var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec( + match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || + !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); + + // calculate the numbers (first)n+(last) including if they are negative + match[2] = (test[1] + (test[2] || 1)) - 0; + match[3] = test[3] - 0; + } + else if ( match[2] ) { + Sizzle.error( match[0] ); + } + + // TODO: Move to normal caching system + match[0] = done++; + + return match; + }, + + ATTR: function( match, curLoop, inplace, result, not, isXML ) { + var name = match[1] = match[1].replace( rBackslash, "" ); + + if ( !isXML && Expr.attrMap[name] ) { + match[1] = Expr.attrMap[name]; + } + + // Handle if an un-quoted value was used + match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" ); + + if ( match[2] === "~=" ) { + match[4] = " " + match[4] + " "; + } + + return match; + }, + + PSEUDO: function( match, curLoop, inplace, result, not ) { + if ( match[1] === "not" ) { + // If we're dealing with a complex expression, or a simple one + if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { + match[3] = Sizzle(match[3], null, null, curLoop); + + } else { + var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); + + if ( !inplace ) { + result.push.apply( result, ret ); + } + + return false; + } + + } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { + return true; + } + + return match; + }, + + POS: function( match ) { + match.unshift( true ); + + return match; + } + }, + + filters: { + enabled: function( elem ) { + return elem.disabled === false && elem.type !== "hidden"; + }, + + disabled: function( elem ) { + return elem.disabled === true; + }, + + checked: function( elem ) { + return elem.checked === true; + }, + + selected: function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + parent: function( elem ) { + return !!elem.firstChild; + }, + + empty: function( elem ) { + return !elem.firstChild; + }, + + has: function( elem, i, match ) { + return !!Sizzle( match[3], elem ).length; + }, + + header: function( elem ) { + return (/h\d/i).test( elem.nodeName ); + }, + + text: function( elem ) { + var attr = elem.getAttribute( "type" ), type = elem.type; + // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) + // use getAttribute instead to test this case + return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null ); + }, + + radio: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type; + }, + + checkbox: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type; + }, + + file: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "file" === elem.type; + }, + + password: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "password" === elem.type; + }, + + submit: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && "submit" === elem.type; + }, + + image: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "image" === elem.type; + }, + + reset: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && "reset" === elem.type; + }, + + button: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && "button" === elem.type || name === "button"; + }, + + input: function( elem ) { + return (/input|select|textarea|button/i).test( elem.nodeName ); + }, + + focus: function( elem ) { + return elem === elem.ownerDocument.activeElement; + } + }, + setFilters: { + first: function( elem, i ) { + return i === 0; + }, + + last: function( elem, i, match, array ) { + return i === array.length - 1; + }, + + even: function( elem, i ) { + return i % 2 === 0; + }, + + odd: function( elem, i ) { + return i % 2 === 1; + }, + + lt: function( elem, i, match ) { + return i < match[3] - 0; + }, + + gt: function( elem, i, match ) { + return i > match[3] - 0; + }, + + nth: function( elem, i, match ) { + return match[3] - 0 === i; + }, + + eq: function( elem, i, match ) { + return match[3] - 0 === i; + } + }, + filter: { + PSEUDO: function( elem, match, i, array ) { + var name = match[1], + filter = Expr.filters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + + } else if ( name === "contains" ) { + return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0; + + } else if ( name === "not" ) { + var not = match[3]; + + for ( var j = 0, l = not.length; j < l; j++ ) { + if ( not[j] === elem ) { + return false; + } + } + + return true; + + } else { + Sizzle.error( name ); + } + }, + + CHILD: function( elem, match ) { + var first, last, + doneName, parent, cache, + count, diff, + type = match[1], + node = elem; + + switch ( type ) { + case "only": + case "first": + while ( (node = node.previousSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + if ( type === "first" ) { + return true; + } + + node = elem; + + /* falls through */ + case "last": + while ( (node = node.nextSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + return true; + + case "nth": + first = match[2]; + last = match[3]; + + if ( first === 1 && last === 0 ) { + return true; + } + + doneName = match[0]; + parent = elem.parentNode; + + if ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) { + count = 0; + + for ( node = parent.firstChild; node; node = node.nextSibling ) { + if ( node.nodeType === 1 ) { + node.nodeIndex = ++count; + } + } + + parent[ expando ] = doneName; + } + + diff = elem.nodeIndex - last; + + if ( first === 0 ) { + return diff === 0; + + } else { + return ( diff % first === 0 && diff / first >= 0 ); + } + } + }, + + ID: function( elem, match ) { + return elem.nodeType === 1 && elem.getAttribute("id") === match; + }, + + TAG: function( elem, match ) { + return (match === "*" && elem.nodeType === 1) || !!elem.nodeName && elem.nodeName.toLowerCase() === match; + }, + + CLASS: function( elem, match ) { + return (" " + (elem.className || elem.getAttribute("class")) + " ") + .indexOf( match ) > -1; + }, + + ATTR: function( elem, match ) { + var name = match[1], + result = Sizzle.attr ? + Sizzle.attr( elem, name ) : + Expr.attrHandle[ name ] ? + Expr.attrHandle[ name ]( elem ) : + elem[ name ] != null ? + elem[ name ] : + elem.getAttribute( name ), + value = result + "", + type = match[2], + check = match[4]; + + return result == null ? + type === "!=" : + !type && Sizzle.attr ? + result != null : + type === "=" ? + value === check : + type === "*=" ? + value.indexOf(check) >= 0 : + type === "~=" ? + (" " + value + " ").indexOf(check) >= 0 : + !check ? + value && result !== false : + type === "!=" ? + value !== check : + type === "^=" ? + value.indexOf(check) === 0 : + type === "$=" ? + value.substr(value.length - check.length) === check : + type === "|=" ? + value === check || value.substr(0, check.length + 1) === check + "-" : + false; + }, + + POS: function( elem, match, i, array ) { + var name = match[2], + filter = Expr.setFilters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + } + } + } +}; + +var origPOS = Expr.match.POS, + fescape = function(all, num){ + return "\\" + (num - 0 + 1); + }; + +for ( var type in Expr.match ) { + Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) ); + Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) ); +} +// Expose origPOS +// "global" as in regardless of relation to brackets/parens +Expr.match.globalPOS = origPOS; + +var makeArray = function( array, results ) { + array = Array.prototype.slice.call( array, 0 ); + + if ( results ) { + results.push.apply( results, array ); + return results; + } + + return array; +}; + +// Perform a simple check to determine if the browser is capable of +// converting a NodeList to an array using builtin methods. +// Also verifies that the returned array holds DOM nodes +// (which is not the case in the Blackberry browser) +try { + Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType; + +// Provide a fallback method if it does not work +} catch( e ) { + makeArray = function( array, results ) { + var i = 0, + ret = results || []; + + if ( toString.call(array) === "[object Array]" ) { + Array.prototype.push.apply( ret, array ); + + } else { + if ( typeof array.length === "number" ) { + for ( var l = array.length; i < l; i++ ) { + ret.push( array[i] ); + } + + } else { + for ( ; array[i]; i++ ) { + ret.push( array[i] ); + } + } + } + + return ret; + }; +} + +var sortOrder, siblingCheck; + +if ( document.documentElement.compareDocumentPosition ) { + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { + return a.compareDocumentPosition ? -1 : 1; + } + + return a.compareDocumentPosition(b) & 4 ? -1 : 1; + }; + +} else { + sortOrder = function( a, b ) { + // The nodes are identical, we can exit early + if ( a === b ) { + hasDuplicate = true; + return 0; + + // Fallback to using sourceIndex (in IE) if it's available on both nodes + } else if ( a.sourceIndex && b.sourceIndex ) { + return a.sourceIndex - b.sourceIndex; + } + + var al, bl, + ap = [], + bp = [], + aup = a.parentNode, + bup = b.parentNode, + cur = aup; + + // If the nodes are siblings (or identical) we can do a quick check + if ( aup === bup ) { + return siblingCheck( a, b ); + + // If no parents were found then the nodes are disconnected + } else if ( !aup ) { + return -1; + + } else if ( !bup ) { + return 1; + } + + // Otherwise they're somewhere else in the tree so we need + // to build up a full list of the parentNodes for comparison + while ( cur ) { + ap.unshift( cur ); + cur = cur.parentNode; + } + + cur = bup; + + while ( cur ) { + bp.unshift( cur ); + cur = cur.parentNode; + } + + al = ap.length; + bl = bp.length; + + // Start walking down the tree looking for a discrepancy + for ( var i = 0; i < al && i < bl; i++ ) { + if ( ap[i] !== bp[i] ) { + return siblingCheck( ap[i], bp[i] ); + } + } + + // We ended someplace up the tree so do a sibling check + return i === al ? + siblingCheck( a, bp[i], -1 ) : + siblingCheck( ap[i], b, 1 ); + }; + + siblingCheck = function( a, b, ret ) { + if ( a === b ) { + return ret; + } + + var cur = a.nextSibling; + + while ( cur ) { + if ( cur === b ) { + return -1; + } + + cur = cur.nextSibling; + } + + return 1; + }; +} + +// Check to see if the browser returns elements by name when +// querying by getElementById (and provide a workaround) +(function(){ + // We're going to inject a fake input element with a specified name + var form = document.createElement("div"), + id = "script" + (new Date()).getTime(), + root = document.documentElement; + + form.innerHTML = ""; + + // Inject it into the root element, check its status, and remove it quickly + root.insertBefore( form, root.firstChild ); + + // The workaround has to do additional checks after a getElementById + // Which slows things down for other browsers (hence the branching) + if ( document.getElementById( id ) ) { + Expr.find.ID = function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + + return m ? + m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? + [m] : + undefined : + []; + } + }; + + Expr.filter.ID = function( elem, match ) { + var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); + + return elem.nodeType === 1 && node && node.nodeValue === match; + }; + } + + root.removeChild( form ); + + // release memory in IE + root = form = null; +})(); + +(function(){ + // Check to see if the browser returns only elements + // when doing getElementsByTagName("*") + + // Create a fake element + var div = document.createElement("div"); + div.appendChild( document.createComment("") ); + + // Make sure no comments are found + if ( div.getElementsByTagName("*").length > 0 ) { + Expr.find.TAG = function( match, context ) { + var results = context.getElementsByTagName( match[1] ); + + // Filter out possible comments + if ( match[1] === "*" ) { + var tmp = []; + + for ( var i = 0; results[i]; i++ ) { + if ( results[i].nodeType === 1 ) { + tmp.push( results[i] ); + } + } + + results = tmp; + } + + return results; + }; + } + + // Check to see if an attribute returns normalized href attributes + div.innerHTML = ""; + + if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && + div.firstChild.getAttribute("href") !== "#" ) { + + Expr.attrHandle.href = function( elem ) { + return elem.getAttribute( "href", 2 ); + }; + } + + // release memory in IE + div = null; +})(); + +if ( document.querySelectorAll ) { + (function(){ + var oldSizzle = Sizzle, + div = document.createElement("div"), + id = "__sizzle__"; + + div.innerHTML = "

"; + + // Safari can't handle uppercase or unicode characters when + // in quirks mode. + if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { + return; + } + + Sizzle = function( query, context, extra, seed ) { + context = context || document; + + // Only use querySelectorAll on non-XML documents + // (ID selectors don't work in non-HTML documents) + if ( !seed && !Sizzle.isXML(context) ) { + // See if we find a selector to speed up + var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query ); + + if ( match && (context.nodeType === 1 || context.nodeType === 9) ) { + // Speed-up: Sizzle("TAG") + if ( match[1] ) { + return makeArray( context.getElementsByTagName( query ), extra ); + + // Speed-up: Sizzle(".CLASS") + } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) { + return makeArray( context.getElementsByClassName( match[2] ), extra ); + } + } + + if ( context.nodeType === 9 ) { + // Speed-up: Sizzle("body") + // The body element only exists once, optimize finding it + if ( query === "body" && context.body ) { + return makeArray( [ context.body ], extra ); + + // Speed-up: Sizzle("#ID") + } else if ( match && match[3] ) { + var elem = context.getElementById( match[3] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id === match[3] ) { + return makeArray( [ elem ], extra ); + } + + } else { + return makeArray( [], extra ); + } + } + + try { + return makeArray( context.querySelectorAll(query), extra ); + } catch(qsaError) {} + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + var oldContext = context, + old = context.getAttribute( "id" ), + nid = old || id, + hasParent = context.parentNode, + relativeHierarchySelector = /^\s*[+~]/.test( query ); + + if ( !old ) { + context.setAttribute( "id", nid ); + } else { + nid = nid.replace( /'/g, "\\$&" ); + } + if ( relativeHierarchySelector && hasParent ) { + context = context.parentNode; + } + + try { + if ( !relativeHierarchySelector || hasParent ) { + return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra ); + } + + } catch(pseudoError) { + } finally { + if ( !old ) { + oldContext.removeAttribute( "id" ); + } + } + } + } + + return oldSizzle(query, context, extra, seed); + }; + + for ( var prop in oldSizzle ) { + Sizzle[ prop ] = oldSizzle[ prop ]; + } + + // release memory in IE + div = null; + })(); +} + +(function(){ + var html = document.documentElement, + matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector; + + if ( matches ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9 fails this) + var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ), + pseudoWorks = false; + + try { + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( document.documentElement, "[test!='']:sizzle" ); + + } catch( pseudoError ) { + pseudoWorks = true; + } + + Sizzle.matchesSelector = function( node, expr ) { + // Make sure that attribute selectors are quoted + expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']"); + + if ( !Sizzle.isXML( node ) ) { + try { + if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) { + var ret = matches.call( node, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || !disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9, so check for that + node.document && node.document.nodeType !== 11 ) { + return ret; + } + } + } catch(e) {} + } + + return Sizzle(expr, null, null, [node]).length > 0; + }; + } +})(); + +(function(){ + var div = document.createElement("div"); + + div.innerHTML = "
"; + + // Opera can't find a second classname (in 9.6) + // Also, make sure that getElementsByClassName actually exists + if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { + return; + } + + // Safari caches class attributes, doesn't catch changes (in 3.2) + div.lastChild.className = "e"; + + if ( div.getElementsByClassName("e").length === 1 ) { + return; + } + + Expr.order.splice(1, 0, "CLASS"); + Expr.find.CLASS = function( match, context, isXML ) { + if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { + return context.getElementsByClassName(match[1]); + } + }; + + // release memory in IE + div = null; +})(); + +function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem[ expando ] === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 && !isXML ){ + elem[ expando ] = doneName; + elem.sizset = i; + } + + if ( elem.nodeName.toLowerCase() === cur ) { + match = elem; + break; + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem[ expando ] === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 ) { + if ( !isXML ) { + elem[ expando ] = doneName; + elem.sizset = i; + } + + if ( typeof cur !== "string" ) { + if ( elem === cur ) { + match = true; + break; + } + + } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { + match = elem; + break; + } + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +if ( document.documentElement.contains ) { + Sizzle.contains = function( a, b ) { + return a !== b && (a.contains ? a.contains(b) : true); + }; + +} else if ( document.documentElement.compareDocumentPosition ) { + Sizzle.contains = function( a, b ) { + return !!(a.compareDocumentPosition(b) & 16); + }; + +} else { + Sizzle.contains = function() { + return false; + }; +} + +Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; + + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +var posProcess = function( selector, context, seed ) { + var match, + tmpSet = [], + later = "", + root = context.nodeType ? [context] : context; + + // Position selectors must be done after the filter + // And so must :not(positional) so we move all PSEUDOs to the end + while ( (match = Expr.match.PSEUDO.exec( selector )) ) { + later += match[0]; + selector = selector.replace( Expr.match.PSEUDO, "" ); + } + + selector = Expr.relative[selector] ? selector + "*" : selector; + + for ( var i = 0, l = root.length; i < l; i++ ) { + Sizzle( selector, root[i], tmpSet, seed ); + } + + return Sizzle.filter( later, tmpSet ); +}; + +// EXPOSE +// Override sizzle attribute retrieval +Sizzle.attr = jQuery.attr; +Sizzle.selectors.attrMap = {}; +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.filters; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + +})(); + + +var runtil = /Until$/, + rparentsprev = /^(?:parents|prevUntil|prevAll)/, + // Note: This RegExp should be improved, or likely pulled from Sizzle + rmultiselector = /,/, + isSimple = /^.[^:#\[\.,]*$/, + slice = Array.prototype.slice, + POS = jQuery.expr.match.globalPOS, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend({ + find: function( selector ) { + var self = this, + i, l; + + if ( typeof selector !== "string" ) { + return jQuery( selector ).filter(function() { + for ( i = 0, l = self.length; i < l; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + }); + } + + var ret = this.pushStack( "", "find", selector ), + length, n, r; + + for ( i = 0, l = this.length; i < l; i++ ) { + length = ret.length; + jQuery.find( selector, this[i], ret ); + + if ( i > 0 ) { + // Make sure that the results are unique + for ( n = length; n < ret.length; n++ ) { + for ( r = 0; r < length; r++ ) { + if ( ret[r] === ret[n] ) { + ret.splice(n--, 1); + break; + } + } + } + } + } + + return ret; + }, + + has: function( target ) { + var targets = jQuery( target ); + return this.filter(function() { + for ( var i = 0, l = targets.length; i < l; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + not: function( selector ) { + return this.pushStack( winnow(this, selector, false), "not", selector); + }, + + filter: function( selector ) { + return this.pushStack( winnow(this, selector, true), "filter", selector ); + }, + + is: function( selector ) { + return !!selector && ( + typeof selector === "string" ? + // If this is a positional selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + POS.test( selector ) ? + jQuery( selector, this.context ).index( this[0] ) >= 0 : + jQuery.filter( selector, this ).length > 0 : + this.filter( selector ).length > 0 ); + }, + + closest: function( selectors, context ) { + var ret = [], i, l, cur = this[0]; + + // Array (deprecated as of jQuery 1.7) + if ( jQuery.isArray( selectors ) ) { + var level = 1; + + while ( cur && cur.ownerDocument && cur !== context ) { + for ( i = 0; i < selectors.length; i++ ) { + + if ( jQuery( cur ).is( selectors[ i ] ) ) { + ret.push({ selector: selectors[ i ], elem: cur, level: level }); + } + } + + cur = cur.parentNode; + level++; + } + + return ret; + } + + // String + var pos = POS.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( i = 0, l = this.length; i < l; i++ ) { + cur = this[i]; + + while ( cur ) { + if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) { + ret.push( cur ); + break; + + } else { + cur = cur.parentNode; + if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) { + break; + } + } + } + } + + ret = ret.length > 1 ? jQuery.unique( ret ) : ret; + + return this.pushStack( ret, "closest", selectors ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1; + } + + // index in selector + if ( typeof elem === "string" ) { + return jQuery.inArray( this[0], jQuery( elem ) ); + } + + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[0] : elem, this ); + }, + + add: function( selector, context ) { + var set = typeof selector === "string" ? + jQuery( selector, context ) : + jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), + all = jQuery.merge( this.get(), set ); + + return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ? + all : + jQuery.unique( all ) ); + }, + + andSelf: function() { + return this.add( this.prevObject ); + } +}); + +// A painfully simple check to see if an element is disconnected +// from a document (should be improved, where feasible). +function isDisconnected( node ) { + return !node || !node.parentNode || node.parentNode.nodeType === 11; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return jQuery.nth( elem, 2, "nextSibling" ); + }, + prev: function( elem ) { + return jQuery.nth( elem, 2, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.makeArray( elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ); + + if ( !runtil.test( name ) ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; + + if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + + return this.pushStack( ret, name, slice.call( arguments ).join(",") ); + }; +}); + +jQuery.extend({ + filter: function( expr, elems, not ) { + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 ? + jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] : + jQuery.find.matches(expr, elems); + }, + + dir: function( elem, dir, until ) { + var matched = [], + cur = elem[ dir ]; + + while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { + if ( cur.nodeType === 1 ) { + matched.push( cur ); + } + cur = cur[dir]; + } + return matched; + }, + + nth: function( cur, result, dir, elem ) { + result = result || 1; + var num = 0; + + for ( ; cur; cur = cur[dir] ) { + if ( cur.nodeType === 1 && ++num === result ) { + break; + } + } + + return cur; + }, + + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + } +}); + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, keep ) { + + // Can't pass null or undefined to indexOf in Firefox 4 + // Set to 0 to skip string check + qualifier = qualifier || 0; + + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep(elements, function( elem, i ) { + var retVal = !!qualifier.call( elem, i, elem ); + return retVal === keep; + }); + + } else if ( qualifier.nodeType ) { + return jQuery.grep(elements, function( elem, i ) { + return ( elem === qualifier ) === keep; + }); + + } else if ( typeof qualifier === "string" ) { + var filtered = jQuery.grep(elements, function( elem ) { + return elem.nodeType === 1; + }); + + if ( isSimple.test( qualifier ) ) { + return jQuery.filter(qualifier, filtered, !keep); + } else { + qualifier = jQuery.filter( qualifier, filtered ); + } + } + + return jQuery.grep(elements, function( elem, i ) { + return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep; + }); +} + + + + +function createSafeFragment( document ) { + var list = nodeNames.split( "|" ), + safeFrag = document.createDocumentFragment(); + + if ( safeFrag.createElement ) { + while ( list.length ) { + safeFrag.createElement( + list.pop() + ); + } + } + return safeFrag; +} + +var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + + "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", + rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g, + rleadingWhitespace = /^\s+/, + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, + rtagName = /<([\w:]+)/, + rtbody = /]", "i"), + // checked="checked" or checked + rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, + rscriptType = /\/(java|ecma)script/i, + rcleanScript = /^\s*", "" ], + legend: [ 1, "
", "
" ], + thead: [ 1, "", "
" ], + tr: [ 2, "", "
" ], + td: [ 3, "", "
" ], + col: [ 2, "", "
" ], + area: [ 1, "", "" ], + _default: [ 0, "", "" ] + }, + safeFragment = createSafeFragment( document ); + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// IE can't serialize and ' ); + + iframe_doc.close(); + + // Update the Iframe's hash, for great justice. + iframe.location.hash = hash; + } + }; + + })(); + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + // ^^^^^^^^^^^^^^^^^^^ REMOVE IF NOT SUPPORTING IE6/7/8 ^^^^^^^^^^^^^^^^^^^ + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + return self; + })(); + +})(jQuery,this); + +/*! + * jQuery UI Widget @VERSION + * + * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Widget + */ + +(function( $, undefined ) { + +// jQuery 1.4+ +if ( $.cleanData ) { + var _cleanData = $.cleanData; + $.cleanData = function( elems ) { + for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { + $( elem ).triggerHandler( "remove" ); + } + _cleanData( elems ); + }; +} else { + var _remove = $.fn.remove; + $.fn.remove = function( selector, keepData ) { + return this.each(function() { + if ( !keepData ) { + if ( !selector || $.filter( selector, [ this ] ).length ) { + $( "*", this ).add( [ this ] ).each(function() { + $( this ).triggerHandler( "remove" ); + }); + } + } + return _remove.call( $(this), selector, keepData ); + }); + }; +} + +$.widget = function( name, base, prototype ) { + var namespace = name.split( "." )[ 0 ], + fullName; + name = name.split( "." )[ 1 ]; + fullName = namespace + "-" + name; + + if ( !prototype ) { + prototype = base; + base = $.Widget; + } + + // create selector for plugin + $.expr[ ":" ][ fullName ] = function( elem ) { + return !!$.data( elem, name ); + }; + + $[ namespace ] = $[ namespace ] || {}; + $[ namespace ][ name ] = function( options, element ) { + // allow instantiation without initializing for simple inheritance + if ( arguments.length ) { + this._createWidget( options, element ); + } + }; + + var basePrototype = new base(); + // we need to make the options hash a property directly on the new instance + // otherwise we'll modify the options hash on the prototype that we're + // inheriting from +// $.each( basePrototype, function( key, val ) { +// if ( $.isPlainObject(val) ) { +// basePrototype[ key ] = $.extend( {}, val ); +// } +// }); + basePrototype.options = $.extend( true, {}, basePrototype.options ); + $[ namespace ][ name ].prototype = $.extend( true, basePrototype, { + namespace: namespace, + widgetName: name, + widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name, + widgetBaseClass: fullName + }, prototype ); + + $.widget.bridge( name, $[ namespace ][ name ] ); +}; + +$.widget.bridge = function( name, object ) { + $.fn[ name ] = function( options ) { + var isMethodCall = typeof options === "string", + args = Array.prototype.slice.call( arguments, 1 ), + returnValue = this; + + // allow multiple hashes to be passed on init + options = !isMethodCall && args.length ? + $.extend.apply( null, [ true, options ].concat(args) ) : + options; + + // prevent calls to internal methods + if ( isMethodCall && options.charAt( 0 ) === "_" ) { + return returnValue; + } + + if ( isMethodCall ) { + this.each(function() { + var instance = $.data( this, name ); + if ( !instance ) { + throw "cannot call methods on " + name + " prior to initialization; " + + "attempted to call method '" + options + "'"; + } + if ( !$.isFunction( instance[options] ) ) { + throw "no such method '" + options + "' for " + name + " widget instance"; + } + var methodValue = instance[ options ].apply( instance, args ); + if ( methodValue !== instance && methodValue !== undefined ) { + returnValue = methodValue; + return false; + } + }); + } else { + this.each(function() { + var instance = $.data( this, name ); + if ( instance ) { + instance.option( options || {} )._init(); + } else { + $.data( this, name, new object( options, this ) ); + } + }); + } + + return returnValue; + }; +}; + +$.Widget = function( options, element ) { + // allow instantiation without initializing for simple inheritance + if ( arguments.length ) { + this._createWidget( options, element ); + } +}; + +$.Widget.prototype = { + widgetName: "widget", + widgetEventPrefix: "", + options: { + disabled: false + }, + _createWidget: function( options, element ) { + // $.widget.bridge stores the plugin instance, but we do it anyway + // so that it's stored even before the _create function runs + $.data( element, this.widgetName, this ); + this.element = $( element ); + this.options = $.extend( true, {}, + this.options, + this._getCreateOptions(), + options ); + + var self = this; + this.element.bind( "remove." + this.widgetName, function() { + self.destroy(); + }); + + this._create(); + this._trigger( "create" ); + this._init(); + }, + _getCreateOptions: function() { + var options = {}; + if ( $.metadata ) { + options = $.metadata.get( element )[ this.widgetName ]; + } + return options; + }, + _create: function() {}, + _init: function() {}, + + destroy: function() { + this.element + .unbind( "." + this.widgetName ) + .removeData( this.widgetName ); + this.widget() + .unbind( "." + this.widgetName ) + .removeAttr( "aria-disabled" ) + .removeClass( + this.widgetBaseClass + "-disabled " + + "ui-state-disabled" ); + }, + + widget: function() { + return this.element; + }, + + option: function( key, value ) { + var options = key; + + if ( arguments.length === 0 ) { + // don't return a reference to the internal hash + return $.extend( {}, this.options ); + } + + if (typeof key === "string" ) { + if ( value === undefined ) { + return this.options[ key ]; + } + options = {}; + options[ key ] = value; + } + + this._setOptions( options ); + + return this; + }, + _setOptions: function( options ) { + var self = this; + $.each( options, function( key, value ) { + self._setOption( key, value ); + }); + + return this; + }, + _setOption: function( key, value ) { + this.options[ key ] = value; + + if ( key === "disabled" ) { + this.widget() + [ value ? "addClass" : "removeClass"]( + this.widgetBaseClass + "-disabled" + " " + + "ui-state-disabled" ) + .attr( "aria-disabled", value ); + } + + return this; + }, + + enable: function() { + return this._setOption( "disabled", false ); + }, + disable: function() { + return this._setOption( "disabled", true ); + }, + + _trigger: function( type, event, data ) { + var callback = this.options[ type ]; + + event = $.Event( event ); + event.type = ( type === this.widgetEventPrefix ? + type : + this.widgetEventPrefix + type ).toLowerCase(); + data = data || {}; + + // 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 ( event.originalEvent ) { + for ( var i = $.event.props.length, prop; i; ) { + prop = $.event.props[ --i ]; + event[ prop ] = event.originalEvent[ prop ]; + } + } + + this.element.trigger( event, data ); + + return !( $.isFunction(callback) && + callback.call( this.element[0], event, data ) === false || + event.isDefaultPrevented() ); + } +}; + +})( jQuery ); + +(function( $, undefined ) { + +$.widget( "mobile.widget", { + // decorate the parent _createWidget to trigger `widgetinit` for users + // who wish to do post post `widgetcreate` alterations/additions + // + // TODO create a pull request for jquery ui to trigger this event + // in the original _createWidget + _createWidget: function() { + $.Widget.prototype._createWidget.apply( this, arguments ); + this._trigger( 'init' ); + }, + + _getCreateOptions: function() { + + var elem = this.element, + options = {}; + + $.each( this.options, function( option ) { + + var value = elem.jqmData( option.replace( /[A-Z]/g, function( c ) { + return "-" + c.toLowerCase(); + }) + ); + + if ( value !== undefined ) { + options[ option ] = value; + } + }); + + return options; + }, + + enhanceWithin: function( target, useKeepNative ) { + this.enhance( $( this.options.initSelector, $( target )), useKeepNative ); + }, + + enhance: function( targets, useKeepNative ) { + var page, keepNative, $widgetElements = $( targets ), self = this; + + // if ignoreContentEnabled is set to true the framework should + // only enhance the selected elements when they do NOT have a + // parent with the data-namespace-ignore attribute + $widgetElements = $.mobile.enhanceable( $widgetElements ); + + if ( useKeepNative && $widgetElements.length ) { + // TODO remove dependency on the page widget for the keepNative. + // Currently the keepNative value is defined on the page prototype so + // the method is as well + page = $.mobile.closestPageData( $widgetElements ); + keepNative = (page && page.keepNativeSelector()) || ""; + + $widgetElements = $widgetElements.not( keepNative ); + } + + $widgetElements[ this.widgetName ](); + }, + + raise: function( msg ) { + throw "Widget [" + this.widgetName + "]: " + msg; + } +}); + +})( jQuery ); + +(function( $, window, undefined ) { + + var nsNormalizeDict = {}; + + // jQuery.mobile configurable options + $.mobile = $.extend( {}, { + + // Version of the jQuery Mobile Framework + version: "1.1.0", + + // Namespace used framework-wide for data-attrs. Default is no namespace + ns: "", + + // Define the url parameter used for referencing widget-generated sub-pages. + // Translates to to example.html&ui-page=subpageIdentifier + // hash segment before &ui-page= is used to make Ajax request + subPageUrlKey: "ui-page", + + // Class assigned to page currently in view, and during transitions + activePageClass: "ui-page-active", + + // Class used for "active" button state, from CSS framework + activeBtnClass: "ui-btn-active", + + // Class used for "focus" form element state, from CSS framework + focusClass: "ui-focus", + + // Automatically handle clicks and form submissions through Ajax, when same-domain + ajaxEnabled: true, + + // Automatically load and show pages based on location.hash + hashListeningEnabled: true, + + // disable to prevent jquery from bothering with links + linkBindingEnabled: true, + + // Set default page transition - 'none' for no transitions + defaultPageTransition: "fade", + + // Set maximum window width for transitions to apply - 'false' for no limit + maxTransitionWidth: false, + + // Minimum scroll distance that will be remembered when returning to a page + minScrollBack: 250, + + // DEPRECATED: the following property is no longer in use, but defined until 2.0 to prevent conflicts + touchOverflowEnabled: false, + + // Set default dialog transition - 'none' for no transitions + defaultDialogTransition: "pop", + + // Show loading message during Ajax requests + // if false, message will not appear, but loading classes will still be toggled on html el + loadingMessage: "loading", + + // Error response message - appears when an Ajax page request fails + pageLoadErrorMessage: "Error Loading Page", + + // Should the text be visble in the loading message? + loadingMessageTextVisible: false, + + // When the text is visible, what theme does the loading box use? + loadingMessageTheme: "a", + + // For error messages, which theme does the box uses? + pageLoadErrorMessageTheme: "e", + + //automatically initialize the DOM when it's ready + autoInitializePage: true, + + pushStateEnabled: true, + + // allows users to opt in to ignoring content by marking a parent element as + // data-ignored + ignoreContentEnabled: false, + + // turn of binding to the native orientationchange due to android orientation behavior + orientationChangeEnabled: true, + + buttonMarkup: { + hoverDelay: 200 + }, + + // TODO might be useful upstream in jquery itself ? + keyCode: { + ALT: 18, + BACKSPACE: 8, + CAPS_LOCK: 20, + COMMA: 188, + COMMAND: 91, + COMMAND_LEFT: 91, // COMMAND + COMMAND_RIGHT: 93, + CONTROL: 17, + DELETE: 46, + DOWN: 40, + END: 35, + ENTER: 13, + ESCAPE: 27, + HOME: 36, + INSERT: 45, + LEFT: 37, + MENU: 93, // COMMAND_RIGHT + NUMPAD_ADD: 107, + NUMPAD_DECIMAL: 110, + NUMPAD_DIVIDE: 111, + NUMPAD_ENTER: 108, + NUMPAD_MULTIPLY: 106, + NUMPAD_SUBTRACT: 109, + PAGE_DOWN: 34, + PAGE_UP: 33, + PERIOD: 190, + RIGHT: 39, + SHIFT: 16, + SPACE: 32, + TAB: 9, + UP: 38, + WINDOWS: 91 // COMMAND + }, + + // Scroll page vertically: scroll to 0 to hide iOS address bar, or pass a Y value + silentScroll: function( ypos ) { + if ( $.type( ypos ) !== "number" ) { + ypos = $.mobile.defaultHomeScroll; + } + + // prevent scrollstart and scrollstop events + $.event.special.scrollstart.enabled = false; + + setTimeout(function() { + window.scrollTo( 0, ypos ); + $( document ).trigger( "silentscroll", { x: 0, y: ypos }); + }, 20 ); + + setTimeout(function() { + $.event.special.scrollstart.enabled = true; + }, 150 ); + }, + + // Expose our cache for testing purposes. + nsNormalizeDict: nsNormalizeDict, + + // Take a data attribute property, prepend the namespace + // and then camel case the attribute string. Add the result + // to our nsNormalizeDict so we don't have to do this again. + nsNormalize: function( prop ) { + if ( !prop ) { + return; + } + + return nsNormalizeDict[ prop ] || ( nsNormalizeDict[ prop ] = $.camelCase( $.mobile.ns + prop ) ); + }, + + getInheritedTheme: function( el, defaultTheme ) { + + // Find the closest parent with a theme class on it. Note that + // we are not using $.fn.closest() on purpose here because this + // method gets called quite a bit and we need it to be as fast + // as possible. + + var e = el[ 0 ], + ltr = "", + re = /ui-(bar|body|overlay)-([a-z])\b/, + c, m; + + while ( e ) { + var c = e.className || ""; + if ( ( m = re.exec( c ) ) && ( ltr = m[ 2 ] ) ) { + // We found a parent with a theme class + // on it so bail from this loop. + break; + } + e = e.parentNode; + } + + // Return the theme letter we found, if none, return the + // specified default. + + return ltr || defaultTheme || "a"; + }, + + // TODO the following $ and $.fn extensions can/probably should be moved into jquery.mobile.core.helpers + // + // Find the closest javascript page element to gather settings data jsperf test + // http://jsperf.com/single-complex-selector-vs-many-complex-selectors/edit + // possibly naive, but it shows that the parsing overhead for *just* the page selector vs + // the page and dialog selector is negligable. This could probably be speed up by + // doing a similar parent node traversal to the one found in the inherited theme code above + closestPageData: function( $target ) { + return $target + .closest(':jqmData(role="page"), :jqmData(role="dialog")') + .data("page"); + }, + + enhanceable: function( $set ) { + return this.haveParents( $set, "enhance" ); + }, + + hijackable: function( $set ) { + return this.haveParents( $set, "ajax" ); + }, + + haveParents: function( $set, attr ) { + if( !$.mobile.ignoreContentEnabled ){ + return $set; + } + + var count = $set.length, + $newSet = $(), + e, $element, excluded; + + for ( var i = 0; i < count; i++ ) { + $element = $set.eq( i ); + excluded = false; + e = $set[ i ]; + + while ( e ) { + var c = e.getAttribute ? e.getAttribute( "data-" + $.mobile.ns + attr ) : ""; + + if ( c === "false" ) { + excluded = true; + break; + } + + e = e.parentNode; + } + + if ( !excluded ) { + $newSet = $newSet.add( $element ); + } + } + + return $newSet; + } + }, $.mobile ); + + // Mobile version of data and removeData and hasData methods + // ensures all data is set and retrieved using jQuery Mobile's data namespace + $.fn.jqmData = function( prop, value ) { + var result; + if ( typeof prop != "undefined" ) { + if ( prop ) { + prop = $.mobile.nsNormalize( prop ); + } + result = this.data.apply( this, arguments.length < 2 ? [ prop ] : [ prop, value ] ); + } + return result; + }; + + $.jqmData = function( elem, prop, value ) { + var result; + if ( typeof prop != "undefined" ) { + result = $.data( elem, prop ? $.mobile.nsNormalize( prop ) : prop, value ); + } + return result; + }; + + $.fn.jqmRemoveData = function( prop ) { + return this.removeData( $.mobile.nsNormalize( prop ) ); + }; + + $.jqmRemoveData = function( elem, prop ) { + return $.removeData( elem, $.mobile.nsNormalize( prop ) ); + }; + + $.fn.removeWithDependents = function() { + $.removeWithDependents( this ); + }; + + $.removeWithDependents = function( elem ) { + var $elem = $( elem ); + + ( $elem.jqmData('dependents') || $() ).remove(); + $elem.remove(); + }; + + $.fn.addDependents = function( newDependents ) { + $.addDependents( $(this), newDependents ); + }; + + $.addDependents = function( elem, newDependents ) { + var dependents = $(elem).jqmData( 'dependents' ) || $(); + + $(elem).jqmData( 'dependents', $.merge(dependents, newDependents) ); + }; + + // note that this helper doesn't attempt to handle the callback + // or setting of an html elements text, its only purpose is + // to return the html encoded version of the text in all cases. (thus the name) + $.fn.getEncodedText = function() { + return $( "
" ).text( $(this).text() ).html(); + }; + + // fluent helper function for the mobile namespaced equivalent + $.fn.jqmEnhanceable = function() { + return $.mobile.enhanceable( this ); + }; + + $.fn.jqmHijackable = function() { + return $.mobile.hijackable( this ); + }; + + // Monkey-patching Sizzle to filter the :jqmData selector + var oldFind = $.find, + jqmDataRE = /:jqmData\(([^)]*)\)/g; + + $.find = function( selector, context, ret, extra ) { + selector = selector.replace( jqmDataRE, "[data-" + ( $.mobile.ns || "" ) + "$1]" ); + + return oldFind.call( this, selector, context, ret, extra ); + }; + + $.extend( $.find, oldFind ); + + $.find.matches = function( expr, set ) { + return $.find( expr, null, null, set ); + }; + + $.find.matchesSelector = function( node, expr ) { + return $.find( expr, null, null, [ node ] ).length > 0; + }; +})( jQuery, this ); + + +(function( $, undefined ) { + +var $window = $( window ), + $html = $( "html" ); + +/* $.mobile.media method: pass a CSS media type or query and get a bool return + note: this feature relies on actual media query support for media queries, though types will work most anywhere + examples: + $.mobile.media('screen') // tests for screen media type + $.mobile.media('screen and (min-width: 480px)') // tests for screen media type with window width > 480px + $.mobile.media('@media screen and (-webkit-min-device-pixel-ratio: 2)') // tests for webkit 2x pixel ratio (iPhone 4) +*/ +$.mobile.media = (function() { + // TODO: use window.matchMedia once at least one UA implements it + var cache = {}, + testDiv = $( "
" ), + fakeBody = $( "" ).append( testDiv ); + + return function( query ) { + if ( !( query in cache ) ) { + var styleBlock = document.createElement( "style" ), + cssrule = "@media " + query + " { #jquery-mediatest { position:absolute; } }"; + + //must set type for IE! + styleBlock.type = "text/css"; + + if ( styleBlock.styleSheet ){ + styleBlock.styleSheet.cssText = cssrule; + } else { + styleBlock.appendChild( document.createTextNode(cssrule) ); + } + + $html.prepend( fakeBody ).prepend( styleBlock ); + cache[ query ] = testDiv.css( "position" ) === "absolute"; + fakeBody.add( styleBlock ).remove(); + } + return cache[ query ]; + }; +})(); + +})(jQuery); + +(function( $, undefined ) { + +var fakeBody = $( "" ).prependTo( "html" ), + fbCSS = fakeBody[ 0 ].style, + vendors = [ "Webkit", "Moz", "O" ], + webos = "palmGetResource" in window, //only used to rule out scrollTop + operamini = window.operamini && ({}).toString.call( window.operamini ) === "[object OperaMini]", + bb = window.blackberry; //only used to rule out box shadow, as it's filled opaque on BB + +// thx Modernizr +function propExists( prop ) { + var uc_prop = prop.charAt( 0 ).toUpperCase() + prop.substr( 1 ), + props = ( prop + " " + vendors.join( uc_prop + " " ) + uc_prop ).split( " " ); + + for ( var v in props ){ + if ( fbCSS[ props[ v ] ] !== undefined ) { + return true; + } + } +} + +function validStyle( prop, value, check_vend ) { + var div = document.createElement('div'), + uc = function( txt ) { + return txt.charAt( 0 ).toUpperCase() + txt.substr( 1 ) + }, + vend_pref = function( vend ) { + return "-" + vend.charAt( 0 ).toLowerCase() + vend.substr( 1 ) + "-"; + }, + check_style = function( vend ) { + var vend_prop = vend_pref( vend ) + prop + ": " + value + ";", + uc_vend = uc( vend ), + propStyle = uc_vend + uc( prop ); + + div.setAttribute( "style", vend_prop ); + + if( !!div.style[ propStyle ] ) { + ret = true; + } + }, + check_vends = check_vend ? [ check_vend ] : vendors, + ret; + + for( i = 0; i < check_vends.length; i++ ) { + check_style( check_vends[i] ); + } + return !!ret; +} + +// Thanks to Modernizr src for this test idea. `perspective` check is limited to Moz to prevent a false positive for 3D transforms on Android. +function transform3dTest() { + var prop = "transform-3d"; + return validStyle( 'perspective', '10px', 'moz' ) || $.mobile.media( "(-" + vendors.join( "-" + prop + "),(-" ) + "-" + prop + "),(" + prop + ")" ); +} + +// Test for dynamic-updating base tag support ( allows us to avoid href,src attr rewriting ) +function baseTagTest() { + var fauxBase = location.protocol + "//" + location.host + location.pathname + "ui-dir/", + base = $( "head base" ), + fauxEle = null, + href = "", + link, rebase; + + if ( !base.length ) { + base = fauxEle = $( "", { "href": fauxBase }).appendTo( "head" ); + } else { + href = base.attr( "href" ); + } + + link = $( "" ).prependTo( fakeBody ); + rebase = link[ 0 ].href; + base[ 0 ].href = href || location.pathname; + + if ( fauxEle ) { + fauxEle.remove(); + } + return rebase.indexOf( fauxBase ) === 0; +} + + +// non-UA-based IE version check by James Padolsey, modified by jdalton - from http://gist.github.com/527683 +// allows for inclusion of IE 6+, including Windows Mobile 7 +$.extend( $.mobile, { browser: {} } ); +$.mobile.browser.ie = (function() { + var v = 3, + div = document.createElement( "div" ), + a = div.all || []; + + // added {} to silence closure compiler warnings. registering my dislike of all things + // overly clever here for future reference + while ( div.innerHTML = "", a[ 0 ] ){}; + + return v > 4 ? v : !v; +})(); + + +$.extend( $.support, { + orientation: "orientation" in window && "onorientationchange" in window, + touch: "ontouchend" in document, + cssTransitions: "WebKitTransitionEvent" in window || validStyle( 'transition', 'height 100ms linear' ), + pushState: "pushState" in history && "replaceState" in history, + mediaquery: $.mobile.media( "only all" ), + cssPseudoElement: !!propExists( "content" ), + touchOverflow: !!propExists( "overflowScrolling" ), + cssTransform3d: transform3dTest(), + boxShadow: !!propExists( "boxShadow" ) && !bb, + scrollTop: ( "pageXOffset" in window || "scrollTop" in document.documentElement || "scrollTop" in fakeBody[ 0 ] ) && !webos && !operamini, + dynamicBaseTag: baseTagTest() +}); + +fakeBody.remove(); + + +// $.mobile.ajaxBlacklist is used to override ajaxEnabled on platforms that have known conflicts with hash history updates (BB5, Symbian) +// or that generally work better browsing in regular http for full page refreshes (Opera Mini) +// Note: This detection below is used as a last resort. +// We recommend only using these detection methods when all other more reliable/forward-looking approaches are not possible +var nokiaLTE7_3 = (function(){ + + var ua = window.navigator.userAgent; + + //The following is an attempt to match Nokia browsers that are running Symbian/s60, with webkit, version 7.3 or older + return ua.indexOf( "Nokia" ) > -1 && + ( ua.indexOf( "Symbian/3" ) > -1 || ua.indexOf( "Series60/5" ) > -1 ) && + ua.indexOf( "AppleWebKit" ) > -1 && + ua.match( /(BrowserNG|NokiaBrowser)\/7\.[0-3]/ ); +})(); + +// Support conditions that must be met in order to proceed +// default enhanced qualifications are media query support OR IE 7+ +$.mobile.gradeA = function(){ + return $.support.mediaquery || $.mobile.browser.ie && $.mobile.browser.ie >= 7; +}; + +$.mobile.ajaxBlacklist = + // BlackBerry browsers, pre-webkit + window.blackberry && !window.WebKitPoint || + // Opera Mini + operamini || + // Symbian webkits pre 7.3 + nokiaLTE7_3; + +// Lastly, this workaround is the only way we've found so far to get pre 7.3 Symbian webkit devices +// to render the stylesheets when they're referenced before this script, as we'd recommend doing. +// This simply reappends the CSS in place, which for some reason makes it apply +if ( nokiaLTE7_3 ) { + $(function() { + $( "head link[rel='stylesheet']" ).attr( "rel", "alternate stylesheet" ).attr( "rel", "stylesheet" ); + }); +} + +// For ruling out shadows via css +if ( !$.support.boxShadow ) { + $( "html" ).addClass( "ui-mobile-nosupport-boxshadow" ); +} + +})( jQuery ); + +(function( $, window, undefined ) { + +// add new event shortcuts +$.each( ( "touchstart touchmove touchend orientationchange throttledresize " + + "tap taphold swipe swipeleft swiperight scrollstart scrollstop" ).split( " " ), function( i, name ) { + + $.fn[ name ] = function( fn ) { + return fn ? this.bind( name, fn ) : this.trigger( name ); + }; + + $.attrFn[ name ] = true; +}); + +var supportTouch = $.support.touch, + scrollEvent = "touchmove scroll", + touchStartEvent = supportTouch ? "touchstart" : "mousedown", + touchStopEvent = supportTouch ? "touchend" : "mouseup", + touchMoveEvent = supportTouch ? "touchmove" : "mousemove"; + +function triggerCustomEvent( obj, eventType, event ) { + var originalType = event.type; + event.type = eventType; + $.event.handle.call( obj, event ); + event.type = originalType; +} + +// also handles scrollstop +$.event.special.scrollstart = { + + enabled: true, + + setup: function() { + + var thisObject = this, + $this = $( thisObject ), + scrolling, + timer; + + function trigger( event, state ) { + scrolling = state; + triggerCustomEvent( thisObject, scrolling ? "scrollstart" : "scrollstop", event ); + } + + // iPhone triggers scroll after a small delay; use touchmove instead + $this.bind( scrollEvent, function( event ) { + + if ( !$.event.special.scrollstart.enabled ) { + return; + } + + if ( !scrolling ) { + trigger( event, true ); + } + + clearTimeout( timer ); + timer = setTimeout(function() { + trigger( event, false ); + }, 50 ); + }); + } +}; + +// also handles taphold +$.event.special.tap = { + setup: function() { + var thisObject = this, + $this = $( thisObject ); + + $this.bind( "vmousedown", function( event ) { + + if ( event.which && event.which !== 1 ) { + return false; + } + + var origTarget = event.target, + origEvent = event.originalEvent, + timer; + + function clearTapTimer() { + clearTimeout( timer ); + } + + function clearTapHandlers() { + clearTapTimer(); + + $this.unbind( "vclick", clickHandler ) + .unbind( "vmouseup", clearTapTimer ); + $( document ).unbind( "vmousecancel", clearTapHandlers ); + } + + function clickHandler(event) { + clearTapHandlers(); + + // ONLY trigger a 'tap' event if the start target is + // the same as the stop target. + if ( origTarget == event.target ) { + triggerCustomEvent( thisObject, "tap", event ); + } + } + + $this.bind( "vmouseup", clearTapTimer ) + .bind( "vclick", clickHandler ); + $( document ).bind( "vmousecancel", clearTapHandlers ); + + timer = setTimeout(function() { + triggerCustomEvent( thisObject, "taphold", $.Event( "taphold", { target: origTarget } ) ); + }, 750 ); + }); + } +}; + +// also handles swipeleft, swiperight +$.event.special.swipe = { + scrollSupressionThreshold: 10, // More than this horizontal displacement, and we will suppress scrolling. + + durationThreshold: 1000, // More time than this, and it isn't a swipe. + + horizontalDistanceThreshold: 30, // Swipe horizontal displacement must be more than this. + + verticalDistanceThreshold: 75, // Swipe vertical displacement must be less than this. + + setup: function() { + var thisObject = this, + $this = $( thisObject ); + + $this.bind( touchStartEvent, function( event ) { + var data = event.originalEvent.touches ? + event.originalEvent.touches[ 0 ] : event, + start = { + time: ( new Date() ).getTime(), + coords: [ data.pageX, data.pageY ], + origin: $( event.target ) + }, + stop; + + function moveHandler( event ) { + + if ( !start ) { + return; + } + + var data = event.originalEvent.touches ? + event.originalEvent.touches[ 0 ] : event; + + stop = { + time: ( new Date() ).getTime(), + coords: [ data.pageX, data.pageY ] + }; + + // prevent scrolling + if ( Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.scrollSupressionThreshold ) { + event.preventDefault(); + } + } + + $this.bind( touchMoveEvent, moveHandler ) + .one( touchStopEvent, function( event ) { + $this.unbind( touchMoveEvent, moveHandler ); + + if ( start && stop ) { + if ( stop.time - start.time < $.event.special.swipe.durationThreshold && + Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.horizontalDistanceThreshold && + Math.abs( start.coords[ 1 ] - stop.coords[ 1 ] ) < $.event.special.swipe.verticalDistanceThreshold ) { + + start.origin.trigger( "swipe" ) + .trigger( start.coords[0] > stop.coords[ 0 ] ? "swipeleft" : "swiperight" ); + } + } + start = stop = undefined; + }); + }); + } +}; + +(function( $, window ) { + // "Cowboy" Ben Alman + + var win = $( window ), + special_event, + get_orientation, + last_orientation, + initial_orientation_is_landscape, + initial_orientation_is_default, + portrait_map = { "0": true, "180": true }; + + // It seems that some device/browser vendors use window.orientation values 0 and 180 to + // denote the "default" orientation. For iOS devices, and most other smart-phones tested, + // the default orientation is always "portrait", but in some Android and RIM based tablets, + // the default orientation is "landscape". The following code attempts to use the window + // dimensions to figure out what the current orientation is, and then makes adjustments + // to the to the portrait_map if necessary, so that we can properly decode the + // window.orientation value whenever get_orientation() is called. + // + // Note that we used to use a media query to figure out what the orientation the browser + // thinks it is in: + // + // initial_orientation_is_landscape = $.mobile.media("all and (orientation: landscape)"); + // + // but there was an iPhone/iPod Touch bug beginning with iOS 4.2, up through iOS 5.1, + // where the browser *ALWAYS* applied the landscape media query. This bug does not + // happen on iPad. + + if ( $.support.orientation ) { + + // Check the window width and height to figure out what the current orientation + // of the device is at this moment. Note that we've initialized the portrait map + // values to 0 and 180, *AND* we purposely check for landscape so that if we guess + // wrong, , we default to the assumption that portrait is the default orientation. + // We use a threshold check below because on some platforms like iOS, the iPhone + // form-factor can report a larger width than height if the user turns on the + // developer console. The actual threshold value is somewhat arbitrary, we just + // need to make sure it is large enough to exclude the developer console case. + + var ww = window.innerWidth || $( window ).width(), + wh = window.innerHeight || $( window ).height(), + landscape_threshold = 50; + + initial_orientation_is_landscape = ww > wh && ( ww - wh ) > landscape_threshold; + + + // Now check to see if the current window.orientation is 0 or 180. + initial_orientation_is_default = portrait_map[ window.orientation ]; + + // If the initial orientation is landscape, but window.orientation reports 0 or 180, *OR* + // if the initial orientation is portrait, but window.orientation reports 90 or -90, we + // need to flip our portrait_map values because landscape is the default orientation for + // this device/browser. + if ( ( initial_orientation_is_landscape && initial_orientation_is_default ) || ( !initial_orientation_is_landscape && !initial_orientation_is_default ) ) { + portrait_map = { "-90": true, "90": true }; + } + } + + $.event.special.orientationchange = special_event = { + setup: function() { + // If the event is supported natively, return false so that jQuery + // will bind to the event using DOM methods. + if ( $.support.orientation && $.mobile.orientationChangeEnabled ) { + return false; + } + + // Get the current orientation to avoid initial double-triggering. + last_orientation = get_orientation(); + + // Because the orientationchange event doesn't exist, simulate the + // event by testing window dimensions on resize. + win.bind( "throttledresize", handler ); + }, + teardown: function(){ + // If the event is not supported natively, return false so that + // jQuery will unbind the event using DOM methods. + if ( $.support.orientation && $.mobile.orientationChangeEnabled ) { + return false; + } + + // Because the orientationchange event doesn't exist, unbind the + // resize event handler. + win.unbind( "throttledresize", handler ); + }, + add: function( handleObj ) { + // Save a reference to the bound event handler. + var old_handler = handleObj.handler; + + + handleObj.handler = function( event ) { + // Modify event object, adding the .orientation property. + event.orientation = get_orientation(); + + // Call the originally-bound event handler and return its result. + return old_handler.apply( this, arguments ); + }; + } + }; + + // If the event is not supported natively, this handler will be bound to + // the window resize event to simulate the orientationchange event. + function handler() { + // Get the current orientation. + var orientation = get_orientation(); + + if ( orientation !== last_orientation ) { + // The orientation has changed, so trigger the orientationchange event. + last_orientation = orientation; + win.trigger( "orientationchange" ); + } + } + + // Get the current page orientation. This method is exposed publicly, should it + // be needed, as jQuery.event.special.orientationchange.orientation() + $.event.special.orientationchange.orientation = get_orientation = function() { + var isPortrait = true, elem = document.documentElement; + + // prefer window orientation to the calculation based on screensize as + // the actual screen resize takes place before or after the orientation change event + // has been fired depending on implementation (eg android 2.3 is before, iphone after). + // More testing is required to determine if a more reliable method of determining the new screensize + // is possible when orientationchange is fired. (eg, use media queries + element + opacity) + if ( $.support.orientation ) { + // if the window orientation registers as 0 or 180 degrees report + // portrait, otherwise landscape + isPortrait = portrait_map[ window.orientation ]; + } else { + isPortrait = elem && elem.clientWidth / elem.clientHeight < 1.1; + } + + return isPortrait ? "portrait" : "landscape"; + }; + +})( jQuery, window ); + + +// throttled resize event +(function() { + + $.event.special.throttledresize = { + setup: function() { + $( this ).bind( "resize", handler ); + }, + teardown: function(){ + $( this ).unbind( "resize", handler ); + } + }; + + var throttle = 250, + handler = function() { + curr = ( new Date() ).getTime(); + diff = curr - lastCall; + + if ( diff >= throttle ) { + + lastCall = curr; + $( this ).trigger( "throttledresize" ); + + } else { + + if ( heldCall ) { + clearTimeout( heldCall ); + } + + // Promise a held call will still execute + heldCall = setTimeout( handler, throttle - diff ); + } + }, + lastCall = 0, + heldCall, + curr, + diff; +})(); + + +$.each({ + scrollstop: "scrollstart", + taphold: "tap", + swipeleft: "swipe", + swiperight: "swipe" +}, function( event, sourceEvent ) { + + $.event.special[ event ] = { + setup: function() { + $( this ).bind( sourceEvent, $.noop ); + } + }; +}); + +})( jQuery, this ); + +(function( $, undefined ) { + +$.widget( "mobile.page", $.mobile.widget, { + options: { + theme: "c", + domCache: false, + keepNativeDefault: ":jqmData(role='none'), :jqmData(role='nojs')" + }, + + _create: function() { + + var self = this; + + // if false is returned by the callbacks do not create the page + if( self._trigger( "beforecreate" ) === false ){ + return false; + } + + self.element + .attr( "tabindex", "0" ) + .addClass( "ui-page ui-body-" + self.options.theme ) + .bind( "pagebeforehide", function(){ + self.removeContainerBackground(); + } ) + .bind( "pagebeforeshow", function(){ + self.setContainerBackground(); + } ); + + }, + + removeContainerBackground: function(){ + $.mobile.pageContainer.removeClass( "ui-overlay-" + $.mobile.getInheritedTheme( this.element.parent() ) ); + }, + + // set the page container background to the page theme + setContainerBackground: function( theme ){ + if( this.options.theme ){ + $.mobile.pageContainer.addClass( "ui-overlay-" + ( theme || this.options.theme ) ); + } + }, + + keepNativeSelector: function() { + var options = this.options, + keepNativeDefined = options.keepNative && $.trim(options.keepNative); + + if( keepNativeDefined && options.keepNative !== options.keepNativeDefault ){ + return [options.keepNative, options.keepNativeDefault].join(", "); + } + + return options.keepNativeDefault; + } +}); +})( jQuery ); + + +(function( $, window, undefined ) { + +var createHandler = function( sequential ){ + + // Default to sequential + if( sequential === undefined ){ + sequential = true; + } + + return function( name, reverse, $to, $from ) { + + var deferred = new $.Deferred(), + reverseClass = reverse ? " reverse" : "", + active = $.mobile.urlHistory.getActive(), + toScroll = active.lastScroll || $.mobile.defaultHomeScroll, + screenHeight = $.mobile.getScreenHeight(), + maxTransitionOverride = $.mobile.maxTransitionWidth !== false && $( window ).width() > $.mobile.maxTransitionWidth, + none = !$.support.cssTransitions || maxTransitionOverride || !name || name === "none", + toggleViewportClass = function(){ + $.mobile.pageContainer.toggleClass( "ui-mobile-viewport-transitioning viewport-" + name ); + }, + scrollPage = function(){ + // By using scrollTo instead of silentScroll, we can keep things better in order + // Just to be precautios, disable scrollstart listening like silentScroll would + $.event.special.scrollstart.enabled = false; + + window.scrollTo( 0, toScroll ); + + // reenable scrollstart listening like silentScroll would + setTimeout(function() { + $.event.special.scrollstart.enabled = true; + }, 150 ); + }, + cleanFrom = function(){ + $from + .removeClass( $.mobile.activePageClass + " out in reverse " + name ) + .height( "" ); + }, + startOut = function(){ + // if it's not sequential, call the doneOut transition to start the TO page animating in simultaneously + if( !sequential ){ + doneOut(); + } + else { + $from.animationComplete( doneOut ); + } + + // Set the from page's height and start it transitioning out + // Note: setting an explicit height helps eliminate tiling in the transitions + $from + .height( screenHeight + $(window ).scrollTop() ) + .addClass( name + " out" + reverseClass ); + }, + + doneOut = function() { + + if ( $from && sequential ) { + cleanFrom(); + } + + startIn(); + }, + + startIn = function(){ + + $to.addClass( $.mobile.activePageClass ); + + // Send focus to page as it is now display: block + $.mobile.focusPage( $to ); + + // Set to page height + $to.height( screenHeight + toScroll ); + + scrollPage(); + + if( !none ){ + $to.animationComplete( doneIn ); + } + + $to.addClass( name + " in" + reverseClass ); + + if( none ){ + doneIn(); + } + + }, + + doneIn = function() { + + if ( !sequential ) { + + if( $from ){ + cleanFrom(); + } + } + + $to + .removeClass( "out in reverse " + name ) + .height( "" ); + + toggleViewportClass(); + + // In some browsers (iOS5), 3D transitions block the ability to scroll to the desired location during transition + // This ensures we jump to that spot after the fact, if we aren't there already. + if( $( window ).scrollTop() !== toScroll ){ + scrollPage(); + } + + deferred.resolve( name, reverse, $to, $from, true ); + }; + + toggleViewportClass(); + + if ( $from && !none ) { + startOut(); + } + else { + doneOut(); + } + + return deferred.promise(); + }; +} + +// generate the handlers from the above +var sequentialHandler = createHandler(), + simultaneousHandler = createHandler( false ); + +// Make our transition handler the public default. +$.mobile.defaultTransitionHandler = sequentialHandler; + +//transition handler dictionary for 3rd party transitions +$.mobile.transitionHandlers = { + "default": $.mobile.defaultTransitionHandler, + "sequential": sequentialHandler, + "simultaneous": simultaneousHandler +}; + +$.mobile.transitionFallbacks = {}; + +})( jQuery, this ); + +( function( $, undefined ) { + + //define vars for interal use + var $window = $( window ), + $html = $( 'html' ), + $head = $( 'head' ), + + //url path helpers for use in relative url management + path = { + + // This scary looking regular expression parses an absolute URL or its relative + // variants (protocol, site, document, query, and hash), into the various + // components (protocol, host, path, query, fragment, etc that make up the + // URL as well as some other commonly used sub-parts. When used with RegExp.exec() + // or String.match, it parses the URL into a results array that looks like this: + // + // [0]: http://jblas:password@mycompany.com:8080/mail/inbox?msg=1234&type=unread#msg-content + // [1]: http://jblas:password@mycompany.com:8080/mail/inbox?msg=1234&type=unread + // [2]: http://jblas:password@mycompany.com:8080/mail/inbox + // [3]: http://jblas:password@mycompany.com:8080 + // [4]: http: + // [5]: // + // [6]: jblas:password@mycompany.com:8080 + // [7]: jblas:password + // [8]: jblas + // [9]: password + // [10]: mycompany.com:8080 + // [11]: mycompany.com + // [12]: 8080 + // [13]: /mail/inbox + // [14]: /mail/ + // [15]: inbox + // [16]: ?msg=1234&type=unread + // [17]: #msg-content + // + urlParseRE: /^(((([^:\/#\?]+:)?(?:(\/\/)((?:(([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/, + + //Parse a URL into a structure that allows easy access to + //all of the URL components by name. + parseUrl: function( url ) { + // If we're passed an object, we'll assume that it is + // a parsed url object and just return it back to the caller. + if ( $.type( url ) === "object" ) { + return url; + } + + var matches = path.urlParseRE.exec( url || "" ) || []; + + // Create an object that allows the caller to access the sub-matches + // by name. Note that IE returns an empty string instead of undefined, + // like all other browsers do, so we normalize everything so its consistent + // no matter what browser we're running on. + return { + href: matches[ 0 ] || "", + hrefNoHash: matches[ 1 ] || "", + hrefNoSearch: matches[ 2 ] || "", + domain: matches[ 3 ] || "", + protocol: matches[ 4 ] || "", + doubleSlash: matches[ 5 ] || "", + authority: matches[ 6 ] || "", + username: matches[ 8 ] || "", + password: matches[ 9 ] || "", + host: matches[ 10 ] || "", + hostname: matches[ 11 ] || "", + port: matches[ 12 ] || "", + pathname: matches[ 13 ] || "", + directory: matches[ 14 ] || "", + filename: matches[ 15 ] || "", + search: matches[ 16 ] || "", + hash: matches[ 17 ] || "" + }; + }, + + //Turn relPath into an asbolute path. absPath is + //an optional absolute path which describes what + //relPath is relative to. + makePathAbsolute: function( relPath, absPath ) { + if ( relPath && relPath.charAt( 0 ) === "/" ) { + return relPath; + } + + relPath = relPath || ""; + absPath = absPath ? absPath.replace( /^\/|(\/[^\/]*|[^\/]+)$/g, "" ) : ""; + + var absStack = absPath ? absPath.split( "/" ) : [], + relStack = relPath.split( "/" ); + for ( var i = 0; i < relStack.length; i++ ) { + var d = relStack[ i ]; + switch ( d ) { + case ".": + break; + case "..": + if ( absStack.length ) { + absStack.pop(); + } + break; + default: + absStack.push( d ); + break; + } + } + return "/" + absStack.join( "/" ); + }, + + //Returns true if both urls have the same domain. + isSameDomain: function( absUrl1, absUrl2 ) { + return path.parseUrl( absUrl1 ).domain === path.parseUrl( absUrl2 ).domain; + }, + + //Returns true for any relative variant. + isRelativeUrl: function( url ) { + // All relative Url variants have one thing in common, no protocol. + return path.parseUrl( url ).protocol === ""; + }, + + //Returns true for an absolute url. + isAbsoluteUrl: function( url ) { + return path.parseUrl( url ).protocol !== ""; + }, + + //Turn the specified realtive URL into an absolute one. This function + //can handle all relative variants (protocol, site, document, query, fragment). + makeUrlAbsolute: function( relUrl, absUrl ) { + if ( !path.isRelativeUrl( relUrl ) ) { + return relUrl; + } + + var relObj = path.parseUrl( relUrl ), + absObj = path.parseUrl( absUrl ), + protocol = relObj.protocol || absObj.protocol, + doubleSlash = relObj.protocol ? relObj.doubleSlash : ( relObj.doubleSlash || absObj.doubleSlash ), + authority = relObj.authority || absObj.authority, + hasPath = relObj.pathname !== "", + pathname = path.makePathAbsolute( relObj.pathname || absObj.filename, absObj.pathname ), + search = relObj.search || ( !hasPath && absObj.search ) || "", + hash = relObj.hash; + + return protocol + doubleSlash + authority + pathname + search + hash; + }, + + //Add search (aka query) params to the specified url. + addSearchParams: function( url, params ) { + var u = path.parseUrl( url ), + p = ( typeof params === "object" ) ? $.param( params ) : params, + s = u.search || "?"; + return u.hrefNoSearch + s + ( s.charAt( s.length - 1 ) !== "?" ? "&" : "" ) + p + ( u.hash || "" ); + }, + + convertUrlToDataUrl: function( absUrl ) { + var u = path.parseUrl( absUrl ); + if ( path.isEmbeddedPage( u ) ) { + // For embedded pages, remove the dialog hash key as in getFilePath(), + // otherwise the Data Url won't match the id of the embedded Page. + return u.hash.split( dialogHashKey )[0].replace( /^#/, "" ); + } else if ( path.isSameDomain( u, documentBase ) ) { + return u.hrefNoHash.replace( documentBase.domain, "" ); + } + return absUrl; + }, + + //get path from current hash, or from a file path + get: function( newPath ) { + if( newPath === undefined ) { + newPath = location.hash; + } + return path.stripHash( newPath ).replace( /[^\/]*\.[^\/*]+$/, '' ); + }, + + //return the substring of a filepath before the sub-page key, for making a server request + getFilePath: function( path ) { + var splitkey = '&' + $.mobile.subPageUrlKey; + return path && path.split( splitkey )[0].split( dialogHashKey )[0]; + }, + + //set location hash to path + set: function( path ) { + location.hash = path; + }, + + //test if a given url (string) is a path + //NOTE might be exceptionally naive + isPath: function( url ) { + return ( /\// ).test( url ); + }, + + //return a url path with the window's location protocol/hostname/pathname removed + clean: function( url ) { + return url.replace( documentBase.domain, "" ); + }, + + //just return the url without an initial # + stripHash: function( url ) { + return url.replace( /^#/, "" ); + }, + + //remove the preceding hash, any query params, and dialog notations + cleanHash: function( hash ) { + return path.stripHash( hash.replace( /\?.*$/, "" ).replace( dialogHashKey, "" ) ); + }, + + //check whether a url is referencing the same domain, or an external domain or different protocol + //could be mailto, etc + isExternal: function( url ) { + var u = path.parseUrl( url ); + return u.protocol && u.domain !== documentUrl.domain ? true : false; + }, + + hasProtocol: function( url ) { + return ( /^(:?\w+:)/ ).test( url ); + }, + + //check if the specified url refers to the first page in the main application document. + isFirstPageUrl: function( url ) { + // We only deal with absolute paths. + var u = path.parseUrl( path.makeUrlAbsolute( url, documentBase ) ), + + // Does the url have the same path as the document? + samePath = u.hrefNoHash === documentUrl.hrefNoHash || ( documentBaseDiffers && u.hrefNoHash === documentBase.hrefNoHash ), + + // Get the first page element. + fp = $.mobile.firstPage, + + // Get the id of the first page element if it has one. + fpId = fp && fp[0] ? fp[0].id : undefined; + + // The url refers to the first page if the path matches the document and + // it either has no hash value, or the hash is exactly equal to the id of the + // first page element. + return samePath && ( !u.hash || u.hash === "#" || ( fpId && u.hash.replace( /^#/, "" ) === fpId ) ); + }, + + isEmbeddedPage: function( url ) { + var u = path.parseUrl( url ); + + //if the path is absolute, then we need to compare the url against + //both the documentUrl and the documentBase. The main reason for this + //is that links embedded within external documents will refer to the + //application document, whereas links embedded within the application + //document will be resolved against the document base. + if ( u.protocol !== "" ) { + return ( u.hash && ( u.hrefNoHash === documentUrl.hrefNoHash || ( documentBaseDiffers && u.hrefNoHash === documentBase.hrefNoHash ) ) ); + } + return (/^#/).test( u.href ); + } + }, + + //will be defined when a link is clicked and given an active class + $activeClickedLink = null, + + //urlHistory is purely here to make guesses at whether the back or forward button was clicked + //and provide an appropriate transition + urlHistory = { + // Array of pages that are visited during a single page load. + // Each has a url and optional transition, title, and pageUrl (which represents the file path, in cases where URL is obscured, such as dialogs) + stack: [], + + //maintain an index number for the active page in the stack + activeIndex: 0, + + //get active + getActive: function() { + return urlHistory.stack[ urlHistory.activeIndex ]; + }, + + getPrev: function() { + return urlHistory.stack[ urlHistory.activeIndex - 1 ]; + }, + + getNext: function() { + return urlHistory.stack[ urlHistory.activeIndex + 1 ]; + }, + + // addNew is used whenever a new page is added + addNew: function( url, transition, title, pageUrl, role ) { + //if there's forward history, wipe it + if( urlHistory.getNext() ) { + urlHistory.clearForward(); + } + + urlHistory.stack.push( {url : url, transition: transition, title: title, pageUrl: pageUrl, role: role } ); + + urlHistory.activeIndex = urlHistory.stack.length - 1; + }, + + //wipe urls ahead of active index + clearForward: function() { + urlHistory.stack = urlHistory.stack.slice( 0, urlHistory.activeIndex + 1 ); + }, + + directHashChange: function( opts ) { + var back , forward, newActiveIndex, prev = this.getActive(); + + // check if url isp in history and if it's ahead or behind current page + $.each( urlHistory.stack, function( i, historyEntry ) { + + //if the url is in the stack, it's a forward or a back + if( opts.currentUrl === historyEntry.url ) { + //define back and forward by whether url is older or newer than current page + back = i < urlHistory.activeIndex; + forward = !back; + newActiveIndex = i; + } + }); + + // save new page index, null check to prevent falsey 0 result + this.activeIndex = newActiveIndex !== undefined ? newActiveIndex : this.activeIndex; + + if( back ) { + ( opts.either || opts.isBack )( true ); + } else if( forward ) { + ( opts.either || opts.isForward )( false ); + } + }, + + //disable hashchange event listener internally to ignore one change + //toggled internally when location.hash is updated to match the url of a successful page load + ignoreNextHashChange: false + }, + + //define first selector to receive focus when a page is shown + focusable = "[tabindex],a,button:visible,select:visible,input", + + //queue to hold simultanious page transitions + pageTransitionQueue = [], + + //indicates whether or not page is in process of transitioning + isPageTransitioning = false, + + //nonsense hash change key for dialogs, so they create a history entry + dialogHashKey = "&ui-state=dialog", + + //existing base tag? + $base = $head.children( "base" ), + + //tuck away the original document URL minus any fragment. + documentUrl = path.parseUrl( location.href ), + + //if the document has an embedded base tag, documentBase is set to its + //initial value. If a base tag does not exist, then we default to the documentUrl. + documentBase = $base.length ? path.parseUrl( path.makeUrlAbsolute( $base.attr( "href" ), documentUrl.href ) ) : documentUrl, + + //cache the comparison once. + documentBaseDiffers = ( documentUrl.hrefNoHash !== documentBase.hrefNoHash ); + + //base element management, defined depending on dynamic base tag support + var base = $.support.dynamicBaseTag ? { + + //define base element, for use in routing asset urls that are referenced in Ajax-requested markup + element: ( $base.length ? $base : $( "", { href: documentBase.hrefNoHash } ).prependTo( $head ) ), + + //set the generated BASE element's href attribute to a new page's base path + set: function( href ) { + base.element.attr( "href", path.makeUrlAbsolute( href, documentBase ) ); + }, + + //set the generated BASE element's href attribute to a new page's base path + reset: function() { + base.element.attr( "href", documentBase.hrefNoHash ); + } + + } : undefined; + +/* + internal utility functions +--------------------------------------*/ + + + //direct focus to the page title, or otherwise first focusable element + $.mobile.focusPage = function ( page ) { + var autofocus = page.find("[autofocus]"), + pageTitle = page.find( ".ui-title:eq(0)" ); + + if( autofocus.length ) { + autofocus.focus(); + return; + } + + if( pageTitle.length ) { + pageTitle.focus(); + } + else{ + page.focus(); + } + } + + //remove active classes after page transition or error + function removeActiveLinkClass( forceRemoval ) { + if( !!$activeClickedLink && ( !$activeClickedLink.closest( '.ui-page-active' ).length || forceRemoval ) ) { + $activeClickedLink.removeClass( $.mobile.activeBtnClass ); + } + $activeClickedLink = null; + } + + function releasePageTransitionLock() { + isPageTransitioning = false; + if( pageTransitionQueue.length > 0 ) { + $.mobile.changePage.apply( null, pageTransitionQueue.pop() ); + } + } + + // Save the last scroll distance per page, before it is hidden + var setLastScrollEnabled = true, + setLastScroll, delayedSetLastScroll; + + setLastScroll = function() { + // this barrier prevents setting the scroll value based on the browser + // scrolling the window based on a hashchange + if( !setLastScrollEnabled ) { + return; + } + + var active = $.mobile.urlHistory.getActive(); + + if( active ) { + var lastScroll = $window.scrollTop(); + + // Set active page's lastScroll prop. + // If the location we're scrolling to is less than minScrollBack, let it go. + active.lastScroll = lastScroll < $.mobile.minScrollBack ? $.mobile.defaultHomeScroll : lastScroll; + } + }; + + // bind to scrollstop to gather scroll position. The delay allows for the hashchange + // event to fire and disable scroll recording in the case where the browser scrolls + // to the hash targets location (sometimes the top of the page). once pagechange fires + // getLastScroll is again permitted to operate + delayedSetLastScroll = function() { + setTimeout( setLastScroll, 100 ); + }; + + // disable an scroll setting when a hashchange has been fired, this only works + // because the recording of the scroll position is delayed for 100ms after + // the browser might have changed the position because of the hashchange + $window.bind( $.support.pushState ? "popstate" : "hashchange", function() { + setLastScrollEnabled = false; + }); + + // handle initial hashchange from chrome :( + $window.one( $.support.pushState ? "popstate" : "hashchange", function() { + setLastScrollEnabled = true; + }); + + // wait until the mobile page container has been determined to bind to pagechange + $window.one( "pagecontainercreate", function(){ + // once the page has changed, re-enable the scroll recording + $.mobile.pageContainer.bind( "pagechange", function() { + + setLastScrollEnabled = true; + + // remove any binding that previously existed on the get scroll + // which may or may not be different than the scroll element determined for + // this page previously + $window.unbind( "scrollstop", delayedSetLastScroll ); + + // determine and bind to the current scoll element which may be the window + // or in the case of touch overflow the element with touch overflow + $window.bind( "scrollstop", delayedSetLastScroll ); + }); + }); + + // bind to scrollstop for the first page as "pagechange" won't be fired in that case + $window.bind( "scrollstop", delayedSetLastScroll ); + + //function for transitioning between two existing pages + function transitionPages( toPage, fromPage, transition, reverse ) { + + if( fromPage ) { + //trigger before show/hide events + fromPage.data( "page" )._trigger( "beforehide", null, { nextPage: toPage } ); + } + + toPage.data( "page" )._trigger( "beforeshow", null, { prevPage: fromPage || $( "" ) } ); + + //clear page loader + $.mobile.hidePageLoadingMsg(); + + // If transition is defined, check if css 3D transforms are supported, and if not, if a fallback is specified + if( transition && !$.support.cssTransform3d && $.mobile.transitionFallbacks[ transition ] ){ + transition = $.mobile.transitionFallbacks[ transition ]; + } + + //find the transition handler for the specified transition. If there + //isn't one in our transitionHandlers dictionary, use the default one. + //call the handler immediately to kick-off the transition. + var th = $.mobile.transitionHandlers[ transition || "default" ] || $.mobile.defaultTransitionHandler, + promise = th( transition, reverse, toPage, fromPage ); + + promise.done(function() { + + //trigger show/hide events + if( fromPage ) { + fromPage.data( "page" )._trigger( "hide", null, { nextPage: toPage } ); + } + + //trigger pageshow, define prevPage as either fromPage or empty jQuery obj + toPage.data( "page" )._trigger( "show", null, { prevPage: fromPage || $( "" ) } ); + }); + + return promise; + } + + //simply set the active page's minimum height to screen height, depending on orientation + function getScreenHeight(){ + // Native innerHeight returns more accurate value for this across platforms, + // jQuery version is here as a normalized fallback for platforms like Symbian + return window.innerHeight || $( window ).height(); + } + + $.mobile.getScreenHeight = getScreenHeight; + + //simply set the active page's minimum height to screen height, depending on orientation + function resetActivePageHeight(){ + var aPage = $( "." + $.mobile.activePageClass ), + aPagePadT = parseFloat( aPage.css( "padding-top" ) ), + aPagePadB = parseFloat( aPage.css( "padding-bottom" ) ); + + aPage.css( "min-height", getScreenHeight() - aPagePadT - aPagePadB ); + } + + //shared page enhancements + function enhancePage( $page, role ) { + // If a role was specified, make sure the data-role attribute + // on the page element is in sync. + if( role ) { + $page.attr( "data-" + $.mobile.ns + "role", role ); + } + + //run page plugin + $page.page(); + } + +/* exposed $.mobile methods */ + + //animation complete callback + $.fn.animationComplete = function( callback ) { + if( $.support.cssTransitions ) { + return $( this ).one( 'webkitAnimationEnd animationend', callback ); + } + else{ + // defer execution for consistency between webkit/non webkit + setTimeout( callback, 0 ); + return $( this ); + } + }; + + //expose path object on $.mobile + $.mobile.path = path; + + //expose base object on $.mobile + $.mobile.base = base; + + //history stack + $.mobile.urlHistory = urlHistory; + + $.mobile.dialogHashKey = dialogHashKey; + + + + //enable cross-domain page support + $.mobile.allowCrossDomainPages = false; + + //return the original document url + $.mobile.getDocumentUrl = function(asParsedObject) { + return asParsedObject ? $.extend( {}, documentUrl ) : documentUrl.href; + }; + + //return the original document base url + $.mobile.getDocumentBase = function(asParsedObject) { + return asParsedObject ? $.extend( {}, documentBase ) : documentBase.href; + }; + + $.mobile._bindPageRemove = function() { + var page = $(this); + + // when dom caching is not enabled or the page is embedded bind to remove the page on hide + if( !page.data("page").options.domCache + && page.is(":jqmData(external-page='true')") ) { + + page.bind( 'pagehide.remove', function() { + var $this = $( this ), + prEvent = new $.Event( "pageremove" ); + + $this.trigger( prEvent ); + + if( !prEvent.isDefaultPrevented() ){ + $this.removeWithDependents(); + } + }); + } + }; + + // Load a page into the DOM. + $.mobile.loadPage = function( url, options ) { + // This function uses deferred notifications to let callers + // know when the page is done loading, or if an error has occurred. + var deferred = $.Deferred(), + + // The default loadPage options with overrides specified by + // the caller. + settings = $.extend( {}, $.mobile.loadPage.defaults, options ), + + // The DOM element for the page after it has been loaded. + page = null, + + // If the reloadPage option is true, and the page is already + // in the DOM, dupCachedPage will be set to the page element + // so that it can be removed after the new version of the + // page is loaded off the network. + dupCachedPage = null, + + // determine the current base url + findBaseWithDefault = function(){ + var closestBase = ( $.mobile.activePage && getClosestBaseUrl( $.mobile.activePage ) ); + return closestBase || documentBase.hrefNoHash; + }, + + // The absolute version of the URL passed into the function. This + // version of the URL may contain dialog/subpage params in it. + absUrl = path.makeUrlAbsolute( url, findBaseWithDefault() ); + + + // If the caller provided data, and we're using "get" request, + // append the data to the URL. + if ( settings.data && settings.type === "get" ) { + absUrl = path.addSearchParams( absUrl, settings.data ); + settings.data = undefined; + } + + // If the caller is using a "post" request, reloadPage must be true + if( settings.data && settings.type === "post" ){ + settings.reloadPage = true; + } + + // The absolute version of the URL minus any dialog/subpage params. + // In otherwords the real URL of the page to be loaded. + var fileUrl = path.getFilePath( absUrl ), + + // The version of the Url actually stored in the data-url attribute of + // the page. For embedded pages, it is just the id of the page. For pages + // within the same domain as the document base, it is the site relative + // path. For cross-domain pages (Phone Gap only) the entire absolute Url + // used to load the page. + dataUrl = path.convertUrlToDataUrl( absUrl ); + + // Make sure we have a pageContainer to work with. + settings.pageContainer = settings.pageContainer || $.mobile.pageContainer; + + // Check to see if the page already exists in the DOM. + page = settings.pageContainer.children( ":jqmData(url='" + dataUrl + "')" ); + + // If we failed to find the page, check to see if the url is a + // reference to an embedded page. If so, it may have been dynamically + // injected by a developer, in which case it would be lacking a data-url + // attribute and in need of enhancement. + if ( page.length === 0 && dataUrl && !path.isPath( dataUrl ) ) { + page = settings.pageContainer.children( "#" + dataUrl ) + .attr( "data-" + $.mobile.ns + "url", dataUrl ); + } + + // If we failed to find a page in the DOM, check the URL to see if it + // refers to the first page in the application. If it isn't a reference + // to the first page and refers to non-existent embedded page, error out. + if ( page.length === 0 ) { + if ( $.mobile.firstPage && path.isFirstPageUrl( fileUrl ) ) { + // Check to make sure our cached-first-page is actually + // in the DOM. Some user deployed apps are pruning the first + // page from the DOM for various reasons, we check for this + // case here because we don't want a first-page with an id + // falling through to the non-existent embedded page error + // case. If the first-page is not in the DOM, then we let + // things fall through to the ajax loading code below so + // that it gets reloaded. + if ( $.mobile.firstPage.parent().length ) { + page = $( $.mobile.firstPage ); + } + } else if ( path.isEmbeddedPage( fileUrl ) ) { + deferred.reject( absUrl, options ); + return deferred.promise(); + } + } + + // Reset base to the default document base. + if ( base ) { + base.reset(); + } + + // If the page we are interested in is already in the DOM, + // and the caller did not indicate that we should force a + // reload of the file, we are done. Otherwise, track the + // existing page as a duplicated. + if ( page.length ) { + if ( !settings.reloadPage ) { + enhancePage( page, settings.role ); + deferred.resolve( absUrl, options, page ); + return deferred.promise(); + } + dupCachedPage = page; + } + + var mpc = settings.pageContainer, + pblEvent = new $.Event( "pagebeforeload" ), + triggerData = { url: url, absUrl: absUrl, dataUrl: dataUrl, deferred: deferred, options: settings }; + + // Let listeners know we're about to load a page. + mpc.trigger( pblEvent, triggerData ); + + // If the default behavior is prevented, stop here! + if( pblEvent.isDefaultPrevented() ){ + return deferred.promise(); + } + + if ( settings.showLoadMsg ) { + + // This configurable timeout allows cached pages a brief delay to load without showing a message + var loadMsgDelay = setTimeout(function(){ + $.mobile.showPageLoadingMsg(); + }, settings.loadMsgDelay ), + + // Shared logic for clearing timeout and removing message. + hideMsg = function(){ + + // Stop message show timer + clearTimeout( loadMsgDelay ); + + // Hide loading message + $.mobile.hidePageLoadingMsg(); + }; + } + + if ( !( $.mobile.allowCrossDomainPages || path.isSameDomain( documentUrl, absUrl ) ) ) { + deferred.reject( absUrl, options ); + } else { + // Load the new page. + $.ajax({ + url: fileUrl, + type: settings.type, + data: settings.data, + dataType: "html", + success: function( html, textStatus, xhr ) { + //pre-parse html to check for a data-url, + //use it as the new fileUrl, base path, etc + var all = $( "
" ), + + //page title regexp + newPageTitle = html.match( /]*>([^<]*)/ ) && RegExp.$1, + + // TODO handle dialogs again + pageElemRegex = new RegExp( "(<[^>]+\\bdata-" + $.mobile.ns + "role=[\"']?page[\"']?[^>]*>)" ), + dataUrlRegex = new RegExp( "\\bdata-" + $.mobile.ns + "url=[\"']?([^\"'>]*)[\"']?" ); + + + // data-url must be provided for the base tag so resource requests can be directed to the + // correct url. loading into a temprorary element makes these requests immediately + if( pageElemRegex.test( html ) + && RegExp.$1 + && dataUrlRegex.test( RegExp.$1 ) + && RegExp.$1 ) { + url = fileUrl = path.getFilePath( RegExp.$1 ); + } + + if ( base ) { + base.set( fileUrl ); + } + + //workaround to allow scripts to execute when included in page divs + all.get( 0 ).innerHTML = html; + page = all.find( ":jqmData(role='page'), :jqmData(role='dialog')" ).first(); + + //if page elem couldn't be found, create one and insert the body element's contents + if( !page.length ){ + page = $( "
" + html.split( /<\/?body[^>]*>/gmi )[1] + "
" ); + } + + if ( newPageTitle && !page.jqmData( "title" ) ) { + if ( ~newPageTitle.indexOf( "&" ) ) { + newPageTitle = $( "
" + newPageTitle + "
" ).text(); + } + page.jqmData( "title", newPageTitle ); + } + + //rewrite src and href attrs to use a base url + if( !$.support.dynamicBaseTag ) { + var newPath = path.get( fileUrl ); + page.find( "[src], link[href], a[rel='external'], :jqmData(ajax='false'), a[target]" ).each(function() { + var thisAttr = $( this ).is( '[href]' ) ? 'href' : + $(this).is('[src]') ? 'src' : 'action', + thisUrl = $( this ).attr( thisAttr ); + + // XXX_jblas: We need to fix this so that it removes the document + // base URL, and then prepends with the new page URL. + //if full path exists and is same, chop it - helps IE out + thisUrl = thisUrl.replace( location.protocol + '//' + location.host + location.pathname, '' ); + + if( !/^(\w+:|#|\/)/.test( thisUrl ) ) { + $( this ).attr( thisAttr, newPath + thisUrl ); + } + }); + } + + //append to page and enhance + // TODO taging a page with external to make sure that embedded pages aren't removed + // by the various page handling code is bad. Having page handling code in many + // places is bad. Solutions post 1.0 + page + .attr( "data-" + $.mobile.ns + "url", path.convertUrlToDataUrl( fileUrl ) ) + .attr( "data-" + $.mobile.ns + "external-page", true ) + .appendTo( settings.pageContainer ); + + // wait for page creation to leverage options defined on widget + page.one( 'pagecreate', $.mobile._bindPageRemove ); + + enhancePage( page, settings.role ); + + // Enhancing the page may result in new dialogs/sub pages being inserted + // into the DOM. If the original absUrl refers to a sub-page, that is the + // real page we are interested in. + if ( absUrl.indexOf( "&" + $.mobile.subPageUrlKey ) > -1 ) { + page = settings.pageContainer.children( ":jqmData(url='" + dataUrl + "')" ); + } + + //bind pageHide to removePage after it's hidden, if the page options specify to do so + + // Remove loading message. + if ( settings.showLoadMsg ) { + hideMsg(); + } + + // Add the page reference and xhr to our triggerData. + triggerData.xhr = xhr; + triggerData.textStatus = textStatus; + triggerData.page = page; + + // Let listeners know the page loaded successfully. + settings.pageContainer.trigger( "pageload", triggerData ); + + deferred.resolve( absUrl, options, page, dupCachedPage ); + }, + error: function( xhr, textStatus, errorThrown ) { + //set base back to current path + if( base ) { + base.set( path.get() ); + } + + // Add error info to our triggerData. + triggerData.xhr = xhr; + triggerData.textStatus = textStatus; + triggerData.errorThrown = errorThrown; + + var plfEvent = new $.Event( "pageloadfailed" ); + + // Let listeners know the page load failed. + settings.pageContainer.trigger( plfEvent, triggerData ); + + // If the default behavior is prevented, stop here! + // Note that it is the responsibility of the listener/handler + // that called preventDefault(), to resolve/reject the + // deferred object within the triggerData. + if( plfEvent.isDefaultPrevented() ){ + return; + } + + // Remove loading message. + if ( settings.showLoadMsg ) { + + // Remove loading message. + hideMsg(); + + // show error message + $.mobile.showPageLoadingMsg( $.mobile.pageLoadErrorMessageTheme, $.mobile.pageLoadErrorMessage, true ); + + // hide after delay + setTimeout( $.mobile.hidePageLoadingMsg, 1500 ); + } + + deferred.reject( absUrl, options ); + } + }); + } + + return deferred.promise(); + }; + + $.mobile.loadPage.defaults = { + type: "get", + data: undefined, + reloadPage: false, + role: undefined, // By default we rely on the role defined by the @data-role attribute. + showLoadMsg: false, + pageContainer: undefined, + loadMsgDelay: 50 // This delay allows loads that pull from browser cache to occur without showing the loading message. + }; + + // Show a specific page in the page container. + $.mobile.changePage = function( toPage, options ) { + // If we are in the midst of a transition, queue the current request. + // We'll call changePage() once we're done with the current transition to + // service the request. + if( isPageTransitioning ) { + pageTransitionQueue.unshift( arguments ); + return; + } + + var settings = $.extend( {}, $.mobile.changePage.defaults, options ); + + // Make sure we have a pageContainer to work with. + settings.pageContainer = settings.pageContainer || $.mobile.pageContainer; + + // Make sure we have a fromPage. + settings.fromPage = settings.fromPage || $.mobile.activePage; + + var mpc = settings.pageContainer, + pbcEvent = new $.Event( "pagebeforechange" ), + triggerData = { toPage: toPage, options: settings }; + + // Let listeners know we're about to change the current page. + mpc.trigger( pbcEvent, triggerData ); + + // If the default behavior is prevented, stop here! + if( pbcEvent.isDefaultPrevented() ){ + return; + } + + // We allow "pagebeforechange" observers to modify the toPage in the trigger + // data to allow for redirects. Make sure our toPage is updated. + + toPage = triggerData.toPage; + + // Set the isPageTransitioning flag to prevent any requests from + // entering this method while we are in the midst of loading a page + // or transitioning. + + isPageTransitioning = true; + + // If the caller passed us a url, call loadPage() + // to make sure it is loaded into the DOM. We'll listen + // to the promise object it returns so we know when + // it is done loading or if an error ocurred. + if ( typeof toPage == "string" ) { + $.mobile.loadPage( toPage, settings ) + .done(function( url, options, newPage, dupCachedPage ) { + isPageTransitioning = false; + options.duplicateCachedPage = dupCachedPage; + $.mobile.changePage( newPage, options ); + }) + .fail(function( url, options ) { + isPageTransitioning = false; + + //clear out the active button state + removeActiveLinkClass( true ); + + //release transition lock so navigation is free again + releasePageTransitionLock(); + settings.pageContainer.trigger( "pagechangefailed", triggerData ); + }); + return; + } + + // If we are going to the first-page of the application, we need to make + // sure settings.dataUrl is set to the application document url. This allows + // us to avoid generating a document url with an id hash in the case where the + // first-page of the document has an id attribute specified. + if ( toPage[ 0 ] === $.mobile.firstPage[ 0 ] && !settings.dataUrl ) { + settings.dataUrl = documentUrl.hrefNoHash; + } + + // The caller passed us a real page DOM element. Update our + // internal state and then trigger a transition to the page. + var fromPage = settings.fromPage, + url = ( settings.dataUrl && path.convertUrlToDataUrl( settings.dataUrl ) ) || toPage.jqmData( "url" ), + // The pageUrl var is usually the same as url, except when url is obscured as a dialog url. pageUrl always contains the file path + pageUrl = url, + fileUrl = path.getFilePath( url ), + active = urlHistory.getActive(), + activeIsInitialPage = urlHistory.activeIndex === 0, + historyDir = 0, + pageTitle = document.title, + isDialog = settings.role === "dialog" || toPage.jqmData( "role" ) === "dialog"; + + // By default, we prevent changePage requests when the fromPage and toPage + // are the same element, but folks that generate content manually/dynamically + // and reuse pages want to be able to transition to the same page. To allow + // this, they will need to change the default value of allowSamePageTransition + // to true, *OR*, pass it in as an option when they manually call changePage(). + // It should be noted that our default transition animations assume that the + // formPage and toPage are different elements, so they may behave unexpectedly. + // It is up to the developer that turns on the allowSamePageTransitiona option + // to either turn off transition animations, or make sure that an appropriate + // animation transition is used. + if( fromPage && fromPage[0] === toPage[0] && !settings.allowSamePageTransition ) { + isPageTransitioning = false; + mpc.trigger( "pagechange", triggerData ); + return; + } + + // We need to make sure the page we are given has already been enhanced. + enhancePage( toPage, settings.role ); + + // If the changePage request was sent from a hashChange event, check to see if the + // page is already within the urlHistory stack. If so, we'll assume the user hit + // the forward/back button and will try to match the transition accordingly. + if( settings.fromHashChange ) { + urlHistory.directHashChange({ + currentUrl: url, + isBack: function() { historyDir = -1; }, + isForward: function() { historyDir = 1; } + }); + } + + // Kill the keyboard. + // XXX_jblas: We need to stop crawling the entire document to kill focus. Instead, + // we should be tracking focus with a delegate() handler so we already have + // the element in hand at this point. + // Wrap this in a try/catch block since IE9 throw "Unspecified error" if document.activeElement + // is undefined when we are in an IFrame. + try { + if(document.activeElement && document.activeElement.nodeName.toLowerCase() != 'body') { + $(document.activeElement).blur(); + } else { + $( "input:focus, textarea:focus, select:focus" ).blur(); + } + } catch(e) {} + + // If we're displaying the page as a dialog, we don't want the url + // for the dialog content to be used in the hash. Instead, we want + // to append the dialogHashKey to the url of the current page. + if ( isDialog && active ) { + // on the initial page load active.url is undefined and in that case should + // be an empty string. Moving the undefined -> empty string back into + // urlHistory.addNew seemed imprudent given undefined better represents + // the url state + url = ( active.url || "" ) + dialogHashKey; + } + + // Set the location hash. + if( settings.changeHash !== false && url ) { + //disable hash listening temporarily + urlHistory.ignoreNextHashChange = true; + //update hash and history + path.set( url ); + } + + // if title element wasn't found, try the page div data attr too + // If this is a deep-link or a reload ( active === undefined ) then just use pageTitle + var newPageTitle = ( !active )? pageTitle : toPage.jqmData( "title" ) || toPage.children(":jqmData(role='header')").find(".ui-title" ).getEncodedText(); + if( !!newPageTitle && pageTitle == document.title ) { + pageTitle = newPageTitle; + } + if ( !toPage.jqmData( "title" ) ) { + toPage.jqmData( "title", pageTitle ); + } + + // Make sure we have a transition defined. + settings.transition = settings.transition + || ( ( historyDir && !activeIsInitialPage ) ? active.transition : undefined ) + || ( isDialog ? $.mobile.defaultDialogTransition : $.mobile.defaultPageTransition ); + + //add page to history stack if it's not back or forward + if( !historyDir ) { + urlHistory.addNew( url, settings.transition, pageTitle, pageUrl, settings.role ); + } + + //set page title + document.title = urlHistory.getActive().title; + + //set "toPage" as activePage + $.mobile.activePage = toPage; + + // If we're navigating back in the URL history, set reverse accordingly. + settings.reverse = settings.reverse || historyDir < 0; + + transitionPages( toPage, fromPage, settings.transition, settings.reverse ) + .done(function( name, reverse, $to, $from, alreadyFocused ) { + removeActiveLinkClass(); + + //if there's a duplicateCachedPage, remove it from the DOM now that it's hidden + if ( settings.duplicateCachedPage ) { + settings.duplicateCachedPage.remove(); + } + + // Send focus to the newly shown page. Moved from promise .done binding in transitionPages + // itself to avoid ie bug that reports offsetWidth as > 0 (core check for visibility) + // despite visibility: hidden addresses issue #2965 + // https://github.com/jquery/jquery-mobile/issues/2965 + if( !alreadyFocused ){ + $.mobile.focusPage( toPage ); + } + + releasePageTransitionLock(); + + // Let listeners know we're all done changing the current page. + mpc.trigger( "pagechange", triggerData ); + }); + }; + + $.mobile.changePage.defaults = { + transition: undefined, + reverse: false, + changeHash: true, + fromHashChange: false, + role: undefined, // By default we rely on the role defined by the @data-role attribute. + duplicateCachedPage: undefined, + pageContainer: undefined, + showLoadMsg: true, //loading message shows by default when pages are being fetched during changePage + dataUrl: undefined, + fromPage: undefined, + allowSamePageTransition: false + }; + +/* 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; + } + + // The base URL for any given element depends on the page it resides in. + function getClosestBaseUrl( ele ) + { + // Find the closest page and extract out its url. + var url = $( ele ).closest( ".ui-page" ).jqmData( "url" ), + base = documentBase.hrefNoHash; + + if ( !url || !path.isPath( url ) ) { + url = base; + } + + return path.makeUrlAbsolute( url, base); + } + + + //The following event bindings should be bound after mobileinit has been triggered + //the following function is called in the init file + $.mobile._registerInternalEvents = function(){ + + //bind to form submit events, handle with Ajax + $( document ).delegate( "form", "submit", function( event ) { + var $this = $( this ); + + if( !$.mobile.ajaxEnabled || + // test that the form is, itself, ajax false + $this.is(":jqmData(ajax='false')") || + // test that $.mobile.ignoreContentEnabled is set and + // the form or one of it's parents is ajax=false + !$this.jqmHijackable().length ) { + return; + } + + var type = $this.attr( "method" ), + target = $this.attr( "target" ), + url = $this.attr( "action" ); + + // 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 = getClosestBaseUrl( $this ); + if ( url === 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 = path.makeUrlAbsolute( url, getClosestBaseUrl($this) ); + + //external submits use regular HTTP + if( path.isExternal( url ) || target ) { + return; + } + + $.mobile.changePage( + url, + { + type: type && type.length && type.toLowerCase() || "get", + data: $this.serialize(), + transition: $this.jqmData( "transition" ), + direction: $this.jqmData( "direction" ), + reloadPage: true + } + ); + event.preventDefault(); + }); + + //add active state on vclick + $( document ).bind( "vclick", function( event ) { + // 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; + } + + var link = findClosestLink( event.target ); + + // split from the previous return logic to avoid find closest where possible + // TODO teach $.mobile.hijackable to operate on raw dom elements so the link wrapping + // can be avoided + if ( !$(link).jqmHijackable().length ) { + return; + } + + if ( link ) { + if ( path.parseUrl( link.getAttribute( "href" ) || "#" ).hash !== "#" ) { + removeActiveLinkClass( true ); + $activeClickedLink = $( link ).closest( ".ui-btn" ).not( ".ui-disabled" ); + $activeClickedLink.addClass( $.mobile.activeBtnClass ); + $( "." + $.mobile.activePageClass + " .ui-btn" ).not( link ).blur(); + + // By caching the href value to data and switching the href to a #, we can avoid address bar showing in iOS. The click handler resets the href during its initial steps if this data is present + $( link ) + .jqmData( "href", $( link ).attr( "href" ) ) + .attr( "href", "#" ); + } + } + }); + + // click routing - direct to HTTP or Ajax, accordingly + $( document ).bind( "click", function( event ) { + if( !$.mobile.linkBindingEnabled ){ + return; + } + + var link = findClosestLink( event.target ), $link = $( link ), 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 ) { + return; + } + + //remove active link class if external (then it won't be there if you come back) + httpCleanup = function(){ + window.setTimeout( function() { removeActiveLinkClass( true ); }, 200 ); + }; + + // If there's data cached for the real href value, set the link's href back to it again. This pairs with an address bar workaround from the vclick handler + if( $link.jqmData( "href" ) ){ + $link.attr( "href", $link.jqmData( "href" ) ); + } + + //if there's a data-rel=back attr, go back in history + if( $link.is( ":jqmData(rel='back')" ) ) { + window.history.back(); + return false; + } + + var baseUrl = getClosestBaseUrl( $link ), + + //get href, if defined, otherwise default to empty hash + href = path.makeUrlAbsolute( $link.attr( "href" ) || "#", baseUrl ); + + //if ajax is disabled, exit early + if( !$.mobile.ajaxEnabled && !path.isEmbeddedPage( href ) ){ + httpCleanup(); + //use default click handling + return; + } + + // XXX_jblas: Ideally links to application pages should be specified as + // an url to the application document with a hash that is either + // the site relative path or id to the page. But some of the + // internal code that dynamically generates sub-pages for nested + // lists and select dialogs, just write a hash in the link they + // create. This means the actual URL path is based on whatever + // the current value of the base tag is at the time this code + // is called. For now we are just assuming that any url with a + // hash in it is an application page reference. + if ( href.search( "#" ) != -1 ) { + href = href.replace( /[^#]*#/, "" ); + if ( !href ) { + //link was an empty hash meant purely + //for interaction, so we ignore it. + event.preventDefault(); + return; + } else if ( path.isPath( href ) ) { + //we have apath so make it the href we want to load. + href = path.makeUrlAbsolute( href, baseUrl ); + } else { + //we have a simple id so use the documentUrl as its base. + href = path.makeUrlAbsolute( "#" + href, documentUrl.hrefNoHash ); + } + } + + // Should we handle this link, or let the browser deal with it? + var useDefaultUrlHandling = $link.is( "[rel='external']" ) || $link.is( ":jqmData(ajax='false')" ) || $link.is( "[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. + // This is usually to allow the application to "phone home" and fetch app specific + // data. We normally let the browser handle external/cross-domain urls, but if the + // allowCrossDomainPages option is true, we will allow cross-domain http/https + // requests to go through our page loading logic. + isCrossDomainPageLoad = ( $.mobile.allowCrossDomainPages && documentUrl.protocol === "file:" && href.search( /^https?:/ ) != -1 ), + + //check for protocol or rel and its not an embedded page + //TODO overlap in logic from isExternal, rel=external check should be + // moved into more comprehensive isExternalLink + isExternal = useDefaultUrlHandling || ( path.isExternal( href ) && !isCrossDomainPageLoad ); + + if( isExternal ) { + httpCleanup(); + //use default click handling + return; + } + + //use ajax + var transition = $link.jqmData( "transition" ), + direction = $link.jqmData( "direction" ), + reverse = ( direction && direction === "reverse" ) || + // deprecated - remove by 1.0 + $link.jqmData( "back" ), + + //this may need to be more specific as we use data-rel more + role = $link.attr( "data-" + $.mobile.ns + "rel" ) || undefined; + + $.mobile.changePage( href, { transition: transition, reverse: reverse, role: role } ); + event.preventDefault(); + }); + + //prefetch pages when anchors with data-prefetch are encountered + $( document ).delegate( ".ui-page", "pageshow.prefetch", function() { + var urls = []; + $( this ).find( "a:jqmData(prefetch)" ).each(function(){ + var $link = $(this), + url = $link.attr( "href" ); + + if ( url && $.inArray( url, urls ) === -1 ) { + urls.push( url ); + + $.mobile.loadPage( url, {role: $link.attr("data-" + $.mobile.ns + "rel")} ); + } + }); + }); + + $.mobile._handleHashChange = function( hash ) { + //find first page via hash + var to = path.stripHash( hash ), + //transition is false if it's the first page, undefined otherwise (and may be overridden by default) + transition = $.mobile.urlHistory.stack.length === 0 ? "none" : undefined, + + // default options for the changPage calls made after examining the current state + // of the page and the hash + changePageOptions = { + transition: transition, + changeHash: false, + fromHashChange: true + }; + + //if listening is disabled (either globally or temporarily), or it's a dialog hash + if( !$.mobile.hashListeningEnabled || urlHistory.ignoreNextHashChange ) { + urlHistory.ignoreNextHashChange = false; + return; + } + + // special case for dialogs + if( urlHistory.stack.length > 1 && to.indexOf( dialogHashKey ) > -1 ) { + + // If current active page is not a dialog skip the dialog and continue + // in the same direction + if(!$.mobile.activePage.is( ".ui-dialog" )) { + //determine if we're heading forward or backward and continue accordingly past + //the current dialog + urlHistory.directHashChange({ + currentUrl: to, + isBack: function() { window.history.back(); }, + isForward: function() { window.history.forward(); } + }); + + // prevent changePage() + return; + } else { + // if the current active page is a dialog and we're navigating + // to a dialog use the dialog objected saved in the stack + urlHistory.directHashChange({ + currentUrl: to, + + // regardless of the direction of the history change + // do the following + either: function( isBack ) { + var active = $.mobile.urlHistory.getActive(); + + to = active.pageUrl; + + // make sure to set the role, transition and reversal + // as most of this is lost by the domCache cleaning + $.extend( changePageOptions, { + role: active.role, + transition: active.transition, + reverse: isBack + }); + } + }); + } + } + + //if to is defined, load it + if ( to ) { + // At this point, 'to' can be one of 3 things, a cached page element from + // a history stack entry, an id, or site-relative/absolute URL. If 'to' is + // an id, we need to resolve it against the documentBase, not the location.href, + // since the hashchange could've been the result of a forward/backward navigation + // that crosses from an external page/dialog to an internal page/dialog. + to = ( typeof to === "string" && !path.isPath( to ) ) ? ( path.makeUrlAbsolute( '#' + to, documentBase ) ) : to; + $.mobile.changePage( to, changePageOptions ); + } else { + //there's no hash, go to the first page in the dom + $.mobile.changePage( $.mobile.firstPage, changePageOptions ); + } + }; + + //hashchange event handler + $window.bind( "hashchange", function( e, triggered ) { + $.mobile._handleHashChange( location.hash ); + }); + + //set page min-heights to be device specific + $( document ).bind( "pageshow", resetActivePageHeight ); + $( window ).bind( "throttledresize", resetActivePageHeight ); + + };//_registerInternalEvents callback + +})( jQuery ); + +( function( $, window ) { + // For now, let's Monkeypatch this onto the end of $.mobile._registerInternalEvents + // Scope self to pushStateHandler so we can reference it sanely within the + // methods handed off as event handlers + var pushStateHandler = {}, + self = pushStateHandler, + $win = $( window ), + url = $.mobile.path.parseUrl( location.href ); + + $.extend( pushStateHandler, { + // TODO move to a path helper, this is rather common functionality + initialFilePath: (function() { + return url.pathname + url.search; + })(), + + initialHref: url.hrefNoHash, + + state: function() { + return { + hash: location.hash || "#" + self.initialFilePath, + title: document.title, + + // persist across refresh + initialHref: self.initialHref + }; + }, + + resetUIKeys: function( url ) { + var dialog = $.mobile.dialogHashKey, + subkey = "&" + $.mobile.subPageUrlKey, + dialogIndex = url.indexOf( dialog ); + + if( dialogIndex > -1 ) { + url = url.slice( 0, dialogIndex ) + "#" + url.slice( dialogIndex ); + } else if( url.indexOf( subkey ) > -1 ) { + url = url.split( subkey ).join( "#" + subkey ); + } + + return url; + }, + + hashValueAfterReset: function( url ) { + var resetUrl = self.resetUIKeys( url ); + return $.mobile.path.parseUrl( resetUrl ).hash; + }, + + // TODO sort out a single barrier to hashchange functionality + nextHashChangePrevented: function( value ) { + $.mobile.urlHistory.ignoreNextHashChange = value; + self.onHashChangeDisabled = value; + }, + + // on hash change we want to clean up the url + // NOTE this takes place *after* the vanilla navigation hash change + // handling has taken place and set the state of the DOM + onHashChange: function( e ) { + // disable this hash change + if( self.onHashChangeDisabled ){ + return; + } + + var href, state, + hash = location.hash, + isPath = $.mobile.path.isPath( hash ), + resolutionUrl = isPath ? location.href : $.mobile.getDocumentUrl(); + + hash = isPath ? hash.replace( "#", "" ) : hash; + + + // propulate the hash when its not available + state = self.state(); + + // make the hash abolute with the current href + href = $.mobile.path.makeUrlAbsolute( hash, resolutionUrl ); + + if ( isPath ) { + href = self.resetUIKeys( href ); + } + + // replace the current url with the new href and store the state + // Note that in some cases we might be replacing an url with the + // same url. We do this anyways because we need to make sure that + // all of our history entries have a state object associated with + // them. This allows us to work around the case where window.history.back() + // is called to transition from an external page to an embedded page. + // In that particular case, a hashchange event is *NOT* generated by the browser. + // Ensuring each history entry has a state object means that onPopState() + // will always trigger our hashchange callback even when a hashchange event + // is not fired. + history.replaceState( state, document.title, href ); + }, + + // on popstate (ie back or forward) we need to replace the hash that was there previously + // cleaned up by the additional hash handling + onPopState: function( e ) { + var poppedState = e.originalEvent.state, + timeout, fromHash, toHash, hashChanged; + + // if there's no state its not a popstate we care about, eg chrome's initial popstate + if( poppedState ) { + // the active url in the history stack will still be from the previous state + // so we can use it to verify if a hashchange will be fired from the popstate + fromHash = self.hashValueAfterReset( $.mobile.urlHistory.getActive().url ); + + // the hash stored in the state popped off the stack will be our currenturl or + // the url to which we wish to navigate + toHash = self.hashValueAfterReset( poppedState.hash.replace("#", "") ); + + // if the hashes of the urls are different we must assume that the browser + // will fire a hashchange + hashChanged = fromHash !== toHash; + + // unlock hash handling once the hashchange caused be the popstate has fired + if( hashChanged ) { + $win.one( "hashchange.pushstate", function() { + self.nextHashChangePrevented( false ); + }); + } + + // enable hash handling for the the _handleHashChange call + self.nextHashChangePrevented( false ); + + // change the page based on the hash + $.mobile._handleHashChange( poppedState.hash ); + + // only prevent another hash change handling if a hash change will be fired + // by the browser + if( hashChanged ) { + // disable hash handling until one of the above timers fires + self.nextHashChangePrevented( true ); + } + } + }, + + init: function() { + $win.bind( "hashchange", self.onHashChange ); + + // Handle popstate events the occur through history changes + $win.bind( "popstate", self.onPopState ); + + // if there's no hash, we need to replacestate for returning to home + if ( location.hash === "" ) { + history.replaceState( self.state(), document.title, location.href ); + } + } + }); + + $( function() { + if( $.mobile.pushStateEnabled && $.support.pushState ){ + pushStateHandler.init(); + } + }); +})( jQuery, this ); + +/* +* fallback transition for pop in non-3D supporting browsers (which tend to handle complex transitions poorly in general +*/ + +(function( $, window, undefined ) { + +$.mobile.transitionFallbacks.pop = "fade"; + +})( jQuery, this ); + +/* +* fallback transition for slide in non-3D supporting browsers (which tend to handle complex transitions poorly in general +*/ + +(function( $, window, undefined ) { + +// Use the simultaneous transition handler for slide transitions +$.mobile.transitionHandlers.slide = $.mobile.transitionHandlers.simultaneous; + +// Set the slide transition's fallback to "fade" +$.mobile.transitionFallbacks.slide = "fade"; + +})( jQuery, this ); + +/* +* fallback transition for slidedown in non-3D supporting browsers (which tend to handle complex transitions poorly in general +*/ + +(function( $, window, undefined ) { + +$.mobile.transitionFallbacks.slidedown = "fade"; + +})( jQuery, this ); + +/* +* fallback transition for slideup in non-3D supporting browsers (which tend to handle complex transitions poorly in general +*/ + +(function( $, window, undefined ) { + +$.mobile.transitionFallbacks.slideup = "fade"; + +})( jQuery, this ); + +/* +* fallback transition for flip in non-3D supporting browsers (which tend to handle complex transitions poorly in general +*/ + +(function( $, window, undefined ) { + +$.mobile.transitionFallbacks.flip = "fade"; + +})( jQuery, this ); + +/* +* fallback transition for flow in non-3D supporting browsers (which tend to handle complex transitions poorly in general +*/ + +(function( $, window, undefined ) { + +$.mobile.transitionFallbacks.flow = "fade"; + +})( jQuery, this ); + +/* +* fallback transition for turn in non-3D supporting browsers (which tend to handle complex transitions poorly in general +*/ + +(function( $, window, undefined ) { + +$.mobile.transitionFallbacks.turn = "fade"; + +})( jQuery, this ); + +(function( $, undefined ) { + +$.mobile.page.prototype.options.degradeInputs = { + color: false, + date: false, + datetime: false, + "datetime-local": false, + email: false, + month: false, + number: false, + range: "number", + search: "text", + tel: false, + time: false, + url: false, + week: false +}; + + +//auto self-init widgets +$( document ).bind( "pagecreate create", function( e ){ + + var page = $.mobile.closestPageData($(e.target)), options; + + if( !page ) { + return; + } + + options = page.options; + + // degrade inputs to avoid poorly implemented native functionality + $( e.target ).find( "input" ).not( page.keepNativeSelector() ).each(function() { + var $this = $( this ), + type = this.getAttribute( "type" ), + optType = options.degradeInputs[ type ] || "text"; + + if ( options.degradeInputs[ type ] ) { + var html = $( "
" ).html( $this.clone() ).html(), + // In IE browsers, the type sometimes doesn't exist in the cloned markup, so we replace the closing tag instead + hasType = html.indexOf( " type=" ) > -1, + findstr = hasType ? /\s+type=["']?\w+['"]?/ : /\/?>/, + repstr = " type=\"" + optType + "\" data-" + $.mobile.ns + "type=\"" + type + "\"" + ( hasType ? "" : ">" ); + + $this.replaceWith( html.replace( findstr, repstr ) ); + } + }); + +}); + +})( jQuery ); + +(function( $, window, undefined ) { + +$.widget( "mobile.dialog", $.mobile.widget, { + options: { + closeBtnText : "Close", + overlayTheme : "a", + initSelector : ":jqmData(role='dialog')" + }, + _create: function() { + var self = this, + $el = this.element, + headerCloseButton = $( ""+ this.options.closeBtnText + "" ), + dialogWrap = $("
", { + "role" : "dialog", + "class" : "ui-dialog-contain ui-corner-all ui-overlay-shadow" + }); + + $el.addClass( "ui-dialog ui-overlay-" + this.options.overlayTheme ); + + // Class the markup for dialog styling + // Set aria role + $el + .wrapInner( dialogWrap ) + .children() + .find( ":jqmData(role='header')" ) + .prepend( headerCloseButton ) + .end() + .children( ':first-child') + .addClass( "ui-corner-top" ) + .end() + .children( ":last-child" ) + .addClass( "ui-corner-bottom" ); + + // this must be an anonymous function so that select menu dialogs can replace + // the close method. This is a change from previously just defining data-rel=back + // on the button and letting nav handle it + // + // Use click rather than vclick in order to prevent the possibility of unintentionally + // reopening the dialog if the dialog opening item was directly under the close button. + headerCloseButton.bind( "click", function() { + self.close(); + }); + + /* bind events + - clicks and submits should use the closing transition that the dialog opened with + unless a data-transition is specified on the link/form + - if the click was on the close button, or the link has a data-rel="back" it'll go back in history naturally + */ + $el.bind( "vclick submit", function( event ) { + var $target = $( event.target ).closest( event.type === "vclick" ? "a" : "form" ), + active; + + if ( $target.length && !$target.jqmData( "transition" ) ) { + + active = $.mobile.urlHistory.getActive() || {}; + + $target.attr( "data-" + $.mobile.ns + "transition", ( active.transition || $.mobile.defaultDialogTransition ) ) + .attr( "data-" + $.mobile.ns + "direction", "reverse" ); + } + }) + .bind( "pagehide", function( e, ui ) { + $( this ).find( "." + $.mobile.activeBtnClass ).removeClass( $.mobile.activeBtnClass ); + }) + // Override the theme set by the page plugin on pageshow + .bind( "pagebeforeshow", function(){ + if( self.options.overlayTheme ){ + self.element + .page( "removeContainerBackground" ) + .page( "setContainerBackground", self.options.overlayTheme ); + } + }); + }, + + // Close method goes back in history + close: function() { + window.history.back(); + } +}); + +//auto self-init widgets +$( document ).delegate( $.mobile.dialog.prototype.options.initSelector, "pagecreate", function(){ + $.mobile.dialog.prototype.enhance( this ); +}); + +})( jQuery, this ); + +(function( $, undefined ) { + +$.fn.fieldcontain = function( options ) { + return this.addClass( "ui-field-contain ui-body ui-br" ); +}; + +//auto self-init widgets +$( document ).bind( "pagecreate create", function( e ){ + $( ":jqmData(role='fieldcontain')", e.target ).jqmEnhanceable().fieldcontain(); +}); + +})( 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; + + if ( !grid ) { + if ( $kids.length <= 5 ) { + for ( var letter in gridCols ) { + if ( gridCols[ letter ] === $kids.length ) { + grid = letter; + } + } + } else { + grid = "a"; + } + } + 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(3n+3)" ).addClass( "ui-block-c" ); + } + if ( iterator > 3 ) { + $kids.filter( ":nth-child(4n+4)" ).addClass( "ui-block-d" ); + } + if ( iterator > 4 ) { + $kids.filter( ":nth-child(5n+5)" ).addClass( "ui-block-e" ); + } + }); +}; +})( jQuery ); + +(function( $, undefined ) { + +$( document ).bind( "pagecreate create", function( e ){ + $( ":jqmData(role='nojs')", e.target ).addClass( "ui-nojs" ); + +}); + +})( jQuery ); + +( function( $, undefined ) { + +$.fn.buttonMarkup = function( options ) { + var $workingSet = this; + + // Enforce options to be of type string + options = ( options && ( $.type( options ) == "object" ) )? options : {}; + for ( var i = 0; i < $workingSet.length; i++ ) { + var el = $workingSet.eq( i ), + e = el[ 0 ], + o = $.extend( {}, $.fn.buttonMarkup.defaults, { + icon: options.icon !== undefined ? options.icon : el.jqmData( "icon" ), + iconpos: options.iconpos !== undefined ? options.iconpos : el.jqmData( "iconpos" ), + theme: options.theme !== undefined ? options.theme : el.jqmData( "theme" ) || $.mobile.getInheritedTheme( el, "c" ), + inline: options.inline !== undefined ? options.inline : el.jqmData( "inline" ), + shadow: options.shadow !== undefined ? options.shadow : el.jqmData( "shadow" ), + corners: options.corners !== undefined ? options.corners : el.jqmData( "corners" ), + iconshadow: options.iconshadow !== undefined ? options.iconshadow : el.jqmData( "iconshadow" ), + mini: options.mini !== undefined ? options.mini : el.jqmData( "mini" ) + }, options ), + + // Classes Defined + innerClass = "ui-btn-inner", + textClass = "ui-btn-text", + buttonClass, iconClass, + // Button inner markup + buttonInner, + buttonText, + buttonIcon, + buttonElements; + + $.each(o, function(key, value) { + e.setAttribute( "data-" + $.mobile.ns + key, value ); + el.jqmData(key, value); + }); + + // Check if this element is already enhanced + buttonElements = $.data(((e.tagName === "INPUT" || e.tagName === "BUTTON") ? e.parentNode : e), "buttonElements"); + + if (buttonElements) { + e = buttonElements.outer; + el = $(e); + buttonInner = buttonElements.inner; + buttonText = buttonElements.text; + // We will recreate this icon below + $(buttonElements.icon).remove(); + buttonElements.icon = null; + } + else { + buttonInner = document.createElement( o.wrapperEls ); + buttonText = document.createElement( o.wrapperEls ); + } + buttonIcon = o.icon ? document.createElement( "span" ) : null; + + if ( attachEvents && !buttonElements) { + attachEvents(); + } + + // if not, try to find closest theme container + if ( !o.theme ) { + o.theme = $.mobile.getInheritedTheme( el, "c" ); + } + + buttonClass = "ui-btn ui-btn-up-" + o.theme; + buttonClass += o.inline ? " ui-btn-inline" : ""; + buttonClass += o.shadow ? " ui-shadow" : ""; + buttonClass += o.corners ? " ui-btn-corner-all" : ""; + + if ( o.mini !== undefined ) { + // Used to control styling in headers/footers, where buttons default to `mini` style. + buttonClass += o.mini ? " ui-mini" : " ui-fullsize"; + } + + if ( o.inline !== undefined ) { + // Used to control styling in headers/footers, where buttons default to `mini` style. + buttonClass += o.inline === false ? " ui-btn-block" : " ui-btn-inline"; + } + + + if ( o.icon ) { + o.icon = "ui-icon-" + o.icon; + o.iconpos = o.iconpos || "left"; + + iconClass = "ui-icon " + o.icon; + + if ( o.iconshadow ) { + iconClass += " ui-icon-shadow"; + } + } + + if ( o.iconpos ) { + buttonClass += " ui-btn-icon-" + o.iconpos; + + if ( o.iconpos == "notext" && !el.attr( "title" ) ) { + el.attr( "title", el.getEncodedText() ); + } + } + + innerClass += o.corners ? " ui-btn-corner-all" : ""; + + if ( o.iconpos && o.iconpos === "notext" && !el.attr( "title" ) ) { + el.attr( "title", el.getEncodedText() ); + } + + if ( buttonElements ) { + el.removeClass( buttonElements.bcls || "" ); + } + el.removeClass( "ui-link" ).addClass( buttonClass ); + + buttonInner.className = innerClass; + + buttonText.className = textClass; + if ( !buttonElements ) { + buttonInner.appendChild( buttonText ); + } + if ( buttonIcon ) { + buttonIcon.className = iconClass; + if ( !(buttonElements && buttonElements.icon) ) { + buttonIcon.appendChild( document.createTextNode("\u00a0") ); + buttonInner.appendChild( buttonIcon ); + } + } + + while ( e.firstChild && !buttonElements) { + buttonText.appendChild( e.firstChild ); + } + + if ( !buttonElements ) { + e.appendChild( buttonInner ); + } + + // Assign a structure containing the elements of this button to the elements of this button. This + // will allow us to recognize this as an already-enhanced button in future calls to buttonMarkup(). + buttonElements = { + bcls : buttonClass, + outer : e, + inner : buttonInner, + text : buttonText, + icon : buttonIcon + }; + + $.data(e, 'buttonElements', buttonElements); + $.data(buttonInner, 'buttonElements', buttonElements); + $.data(buttonText, 'buttonElements', buttonElements); + if (buttonIcon) { + $.data(buttonIcon, 'buttonElements', buttonElements); + } + } + + return this; +}; + +$.fn.buttonMarkup.defaults = { + corners: true, + shadow: true, + iconshadow: true, + wrapperEls: "span" +}; + +function closestEnabledButton( element ) { + var cname; + + while ( element ) { + // Note that we check for typeof className below because the element we + // handed could be in an SVG DOM where className on SVG elements is defined to + // be of a different type (SVGAnimatedString). We only operate on HTML DOM + // elements, so we look for plain "string". + cname = ( typeof element.className === 'string' ) && (element.className + ' '); + if ( cname && cname.indexOf("ui-btn ") > -1 && cname.indexOf("ui-disabled ") < 0 ) { + break; + } + + element = element.parentNode; + } + + return element; +} + +var attachEvents = function() { + var hoverDelay = $.mobile.buttonMarkup.hoverDelay, hov, foc; + + $( document ).bind( { + "vmousedown vmousecancel vmouseup vmouseover vmouseout focus blur scrollstart": function( event ) { + var theme, + $btn = $( closestEnabledButton( event.target ) ), + evt = event.type; + + if ( $btn.length ) { + theme = $btn.attr( "data-" + $.mobile.ns + "theme" ); + + if ( evt === "vmousedown" ) { + if ( $.support.touch ) { + hov = setTimeout(function() { + $btn.removeClass( "ui-btn-up-" + theme ).addClass( "ui-btn-down-" + theme ); + }, hoverDelay ); + } else { + $btn.removeClass( "ui-btn-up-" + theme ).addClass( "ui-btn-down-" + theme ); + } + } else if ( evt === "vmousecancel" || evt === "vmouseup" ) { + $btn.removeClass( "ui-btn-down-" + theme ).addClass( "ui-btn-up-" + theme ); + } else if ( evt === "vmouseover" || evt === "focus" ) { + if ( $.support.touch ) { + foc = setTimeout(function() { + $btn.removeClass( "ui-btn-up-" + theme ).addClass( "ui-btn-hover-" + theme ); + }, hoverDelay ); + } else { + $btn.removeClass( "ui-btn-up-" + theme ).addClass( "ui-btn-hover-" + theme ); + } + } else if ( evt === "vmouseout" || evt === "blur" || evt === "scrollstart" ) { + $btn.removeClass( "ui-btn-hover-" + theme + " ui-btn-down-" + theme ).addClass( "ui-btn-up-" + theme ); + if ( hov ) { + clearTimeout( hov ); + } + if ( foc ) { + clearTimeout( foc ); + } + } + } + }, + "focusin focus": function( event ){ + $( closestEnabledButton( event.target ) ).addClass( $.mobile.focusClass ); + }, + "focusout blur": function( event ){ + $( closestEnabledButton( event.target ) ).removeClass( $.mobile.focusClass ); + } + }); + + attachEvents = null; +}; + +//links in bars, or those with data-role become buttons +//auto self-init widgets +$( document ).bind( "pagecreate create", function( e ){ + + $( ":jqmData(role='button'), .ui-bar > a, .ui-header > a, .ui-footer > a, .ui-bar > :jqmData(role='controlgroup') > a", e.target ) + .not( ".ui-btn, :jqmData(role='none'), :jqmData(role='nojs')" ) + .buttonMarkup(); +}); + +})( jQuery ); + + +(function( $, undefined ) { + +$.mobile.page.prototype.options.backBtnText = "Back"; +$.mobile.page.prototype.options.addBackBtn = false; +$.mobile.page.prototype.options.backBtnTheme = null; +$.mobile.page.prototype.options.headerTheme = "a"; +$.mobile.page.prototype.options.footerTheme = "a"; +$.mobile.page.prototype.options.contentTheme = null; + +$( document ).delegate( ":jqmData(role='page'), :jqmData(role='dialog')", "pagecreate", function( e ) { + + var $page = $( this ), + o = $page.data( "page" ).options, + pageRole = $page.jqmData( "role" ), + pageTheme = o.theme; + + $( ":jqmData(role='header'), :jqmData(role='footer'), :jqmData(role='content')", this ) + .jqmEnhanceable() + .each(function() { + + var $this = $( this ), + role = $this.jqmData( "role" ), + theme = $this.jqmData( "theme" ), + contentTheme = theme || o.contentTheme || ( pageRole === "dialog" && pageTheme ), + $headeranchors, + leftbtn, + rightbtn, + backBtn; + + $this.addClass( "ui-" + role ); + + //apply theming and markup modifications to page,header,content,footer + if ( role === "header" || role === "footer" ) { + + var thisTheme = theme || ( role === "header" ? o.headerTheme : o.footerTheme ) || pageTheme; + + $this + //add theme class + .addClass( "ui-bar-" + thisTheme ) + // Add ARIA role + .attr( "role", role === "header" ? "banner" : "contentinfo" ); + + if( role === "header") { + // Right,left buttons + $headeranchors = $this.children( "a" ); + leftbtn = $headeranchors.hasClass( "ui-btn-left" ); + rightbtn = $headeranchors.hasClass( "ui-btn-right" ); + + leftbtn = leftbtn || $headeranchors.eq( 0 ).not( ".ui-btn-right" ).addClass( "ui-btn-left" ).length; + + rightbtn = rightbtn || $headeranchors.eq( 1 ).addClass( "ui-btn-right" ).length; + } + + // Auto-add back btn on pages beyond first view + if ( o.addBackBtn && + role === "header" && + $( ".ui-page" ).length > 1 && + $page.jqmData( "url" ) !== $.mobile.path.stripHash( location.hash ) && + !leftbtn ) { + + backBtn = $( ""+ o.backBtnText +"" ) + // If theme is provided, override default inheritance + .attr( "data-"+ $.mobile.ns +"theme", o.backBtnTheme || thisTheme ) + .prependTo( $this ); + } + + // Page title + $this.children( "h1, h2, h3, h4, h5, h6" ) + .addClass( "ui-title" ) + // Regardless of h element number in src, it becomes h1 for the enhanced page + .attr({ + "role": "heading", + "aria-level": "1" + }); + + } else if ( role === "content" ) { + if ( contentTheme ) { + $this.addClass( "ui-body-" + ( contentTheme ) ); + } + + // Add ARIA role + $this.attr( "role", "main" ); + } + }); +}); + +})( jQuery ); + +(function( $, undefined ) { + +$.widget( "mobile.collapsible", $.mobile.widget, { + options: { + expandCueText: " click to expand contents", + collapseCueText: " click to collapse contents", + collapsed: true, + heading: "h1,h2,h3,h4,h5,h6,legend", + theme: null, + contentTheme: null, + iconTheme: "d", + mini: false, + initSelector: ":jqmData(role='collapsible')" + }, + _create: function() { + + var $el = this.element, + o = this.options, + collapsible = $el.addClass( "ui-collapsible" ), + collapsibleHeading = $el.children( o.heading ).first(), + collapsibleContent = collapsible.wrapInner( "
" ).find( ".ui-collapsible-content" ), + collapsibleSet = $el.closest( ":jqmData(role='collapsible-set')" ).addClass( "ui-collapsible-set" ); + + // Replace collapsibleHeading if it's a legend + if ( collapsibleHeading.is( "legend" ) ) { + collapsibleHeading = $( "
"+ collapsibleHeading.html() +"
" ).insertBefore( collapsibleHeading ); + collapsibleHeading.next().remove(); + } + + // If we are in a collapsible set + if ( collapsibleSet.length ) { + // Inherit the theme from collapsible-set + if ( !o.theme ) { + o.theme = collapsibleSet.jqmData("theme") || $.mobile.getInheritedTheme( collapsibleSet, "c" ); + } + // Inherit the content-theme from collapsible-set + if ( !o.contentTheme ) { + o.contentTheme = collapsibleSet.jqmData( "content-theme" ); + } + + // Gets the preference icon position in the set + if ( !o.iconPos ) { + o.iconPos = collapsibleSet.jqmData( "iconpos" ); + } + + if( !o.mini ) { + o.mini = collapsibleSet.jqmData( "mini" ); + } + } + collapsibleContent.addClass( ( o.contentTheme ) ? ( "ui-body-" + o.contentTheme ) : ""); + + collapsibleHeading + //drop heading in before content + .insertBefore( collapsibleContent ) + //modify markup & attributes + .addClass( "ui-collapsible-heading" ) + .append( "" ) + .wrapInner( "" ) + .find( "a" ) + .first() + .buttonMarkup({ + shadow: false, + corners: false, + iconpos: $el.jqmData( "iconpos" ) || o.iconPos || "left", + icon: "plus", + mini: o.mini, + theme: o.theme + }) + .add( ".ui-btn-inner", $el ) + .addClass( "ui-corner-top ui-corner-bottom" ); + + //events + collapsible + .bind( "expand collapse", function( event ) { + if ( !event.isDefaultPrevented() ) { + + event.preventDefault(); + + var $this = $( this ), + isCollapse = ( event.type === "collapse" ), + contentTheme = o.contentTheme; + + collapsibleHeading + .toggleClass( "ui-collapsible-heading-collapsed", isCollapse) + .find( ".ui-collapsible-heading-status" ) + .text( isCollapse ? o.expandCueText : o.collapseCueText ) + .end() + .find( ".ui-icon" ) + .toggleClass( "ui-icon-minus", !isCollapse ) + .toggleClass( "ui-icon-plus", isCollapse ); + + $this.toggleClass( "ui-collapsible-collapsed", isCollapse ); + collapsibleContent.toggleClass( "ui-collapsible-content-collapsed", isCollapse ).attr( "aria-hidden", isCollapse ); + + if ( contentTheme && ( !collapsibleSet.length || collapsible.jqmData( "collapsible-last" ) ) ) { + collapsibleHeading + .find( "a" ).first().add( collapsibleHeading.find( ".ui-btn-inner" ) ) + .toggleClass( "ui-corner-bottom", isCollapse ); + collapsibleContent.toggleClass( "ui-corner-bottom", !isCollapse ); + } + collapsibleContent.trigger( "updatelayout" ); + } + }) + .trigger( o.collapsed ? "collapse" : "expand" ); + + collapsibleHeading + .bind( "click", function( event ) { + + var type = collapsibleHeading.is( ".ui-collapsible-heading-collapsed" ) ? + "expand" : "collapse"; + + collapsible.trigger( type ); + + event.preventDefault(); + }); + } +}); + +//auto self-init widgets +$( document ).bind( "pagecreate create", function( e ){ + $.mobile.collapsible.prototype.enhanceWithin( e.target ); +}); + +})( jQuery ); + +(function( $, undefined ) { + +$.widget( "mobile.collapsibleset", $.mobile.widget, { + options: { + initSelector: ":jqmData(role='collapsible-set')" + }, + _create: function() { + var $el = this.element.addClass( "ui-collapsible-set" ), + o = this.options; + + // Inherit the theme from collapsible-set + if ( !o.theme ) { + o.theme = $.mobile.getInheritedTheme( $el, "c" ); + } + // Inherit the content-theme from collapsible-set + if ( !o.contentTheme ) { + o.contentTheme = $el.jqmData( "content-theme" ); + } + + if ( !o.corners ) { + o.corners = $el.jqmData( "corners" ) === undefined ? true : false; + } + + // Initialize the collapsible set if it's not already initialized + if ( !$el.jqmData( "collapsiblebound" ) ) { + $el + .jqmData( "collapsiblebound", true ) + .bind( "expand collapse", function( event ) { + var isCollapse = ( event.type === "collapse" ), + collapsible = $( event.target ).closest( ".ui-collapsible" ), + widget = collapsible.data( "collapsible" ), + contentTheme = widget.options.contentTheme; + if ( contentTheme && collapsible.jqmData( "collapsible-last" ) ) { + collapsible.find( widget.options.heading ).first() + .find( "a" ).first() + .add( ".ui-btn-inner" ) + .toggleClass( "ui-corner-bottom", isCollapse ); + collapsible.find( ".ui-collapsible-content" ).toggleClass( "ui-corner-bottom", !isCollapse ); + } + }) + .bind( "expand", function( event ) { + $( event.target ) + .closest( ".ui-collapsible" ) + .siblings( ".ui-collapsible" ) + .trigger( "collapse" ); + }); + } + }, + + _init: function() { + this.refresh(); + }, + + refresh: function() { + var $el = this.element, + o = this.options, + collapsiblesInSet = $el.children( ":jqmData(role='collapsible')" ); + + $.mobile.collapsible.prototype.enhance( collapsiblesInSet.not( ".ui-collapsible" ) ); + + // clean up borders + collapsiblesInSet.each( function() { + $( this ).find( $.mobile.collapsible.prototype.options.heading ) + .find( "a" ).first() + .add( ".ui-btn-inner" ) + .removeClass( "ui-corner-top ui-corner-bottom" ); + }); + + collapsiblesInSet.first() + .find( "a" ) + .first() + .addClass( o.corners ? "ui-corner-top" : "" ) + .find( ".ui-btn-inner" ) + .addClass( "ui-corner-top" ); + + collapsiblesInSet.last() + .jqmData( "collapsible-last", true ) + .find( "a" ) + .first() + .addClass( o.corners ? "ui-corner-bottom" : "" ) + .find( ".ui-btn-inner" ) + .addClass( "ui-corner-bottom" ); + } +}); + +//auto self-init widgets +$( document ).bind( "pagecreate create", function( e ){ + $.mobile.collapsibleset.prototype.enhanceWithin( e.target ); +}); + +})( jQuery ); + +(function( $, undefined ) { + +$.widget( "mobile.navbar", $.mobile.widget, { + options: { + iconpos: "top", + grid: null, + initSelector: ":jqmData(role='navbar')" + }, + + _create: function(){ + + var $navbar = this.element, + $navbtns = $navbar.find( "a" ), + iconpos = $navbtns.filter( ":jqmData(icon)" ).length ? + this.options.iconpos : undefined; + + $navbar.addClass( "ui-navbar" ) + .attr( "role","navigation" ) + .find( "ul" ) + .jqmEnhanceable() + .grid({ grid: this.options.grid }); + + if ( !iconpos ) { + $navbar.addClass( "ui-navbar-noicons" ); + } + + $navbtns.buttonMarkup({ + corners: false, + shadow: false, + inline: true, + iconpos: iconpos + }); + + $navbar.delegate( "a", "vclick", function( event ) { + if( !$(event.target).hasClass("ui-disabled") ) { + $navbtns.removeClass( $.mobile.activeBtnClass ); + $( this ).addClass( $.mobile.activeBtnClass ); + } + }); + + // Buttons in the navbar with ui-state-persist class should regain their active state before page show + $navbar.closest( ".ui-page" ).bind( "pagebeforeshow", function() { + $navbtns.filter( ".ui-state-persist" ).addClass( $.mobile.activeBtnClass ); + }); + } +}); + +//auto self-init widgets +$( document ).bind( "pagecreate create", function( e ){ + $.mobile.navbar.prototype.enhanceWithin( e.target ); +}); + +})( jQuery ); + +(function( $, undefined ) { + +//Keeps track of the number of lists per page UID +//This allows support for multiple nested list in the same page +//https://github.com/jquery/jquery-mobile/issues/1617 +var listCountPerPage = {}; + +$.widget( "mobile.listview", $.mobile.widget, { + + options: { + theme: null, + countTheme: "c", + headerTheme: "b", + dividerTheme: "b", + splitIcon: "arrow-r", + splitTheme: "b", + mini: false, + inset: false, + initSelector: ":jqmData(role='listview')" + }, + + _create: function() { + var t = this, + listviewClasses = ""; + + listviewClasses += t.options.inset ? " ui-listview-inset ui-corner-all ui-shadow " : ""; + listviewClasses += t.element.jqmData( "mini" ) || t.options.mini === true ? " ui-mini" : ""; + + // create listview markup + t.element.addClass(function( i, orig ) { + return orig + " ui-listview " + listviewClasses; + }); + + t.refresh( true ); + }, + + _removeCorners: function( li, which ) { + var top = "ui-corner-top ui-corner-tr ui-corner-tl", + bot = "ui-corner-bottom ui-corner-br ui-corner-bl"; + + li = li.add( li.find( ".ui-btn-inner, .ui-li-link-alt, .ui-li-thumb" ) ); + + if ( which === "top" ) { + li.removeClass( top ); + } else if ( which === "bottom" ) { + li.removeClass( bot ); + } else { + li.removeClass( top + " " + bot ); + } + }, + + _refreshCorners: function( create ) { + var $li, + $visibleli, + $topli, + $bottomli; + + if ( this.options.inset ) { + $li = this.element.children( "li" ); + // at create time the li are not visible yet so we need to rely on .ui-screen-hidden + $visibleli = create?$li.not( ".ui-screen-hidden" ):$li.filter( ":visible" ); + + this._removeCorners( $li ); + + // Select the first visible li element + $topli = $visibleli.first() + .addClass( "ui-corner-top" ); + + $topli.add( $topli.find( ".ui-btn-inner" ) + .not( ".ui-li-link-alt span:first-child" ) ) + .addClass( "ui-corner-top" ) + .end() + .find( ".ui-li-link-alt, .ui-li-link-alt span:first-child" ) + .addClass( "ui-corner-tr" ) + .end() + .find( ".ui-li-thumb" ) + .not(".ui-li-icon") + .addClass( "ui-corner-tl" ); + + // Select the last visible li element + $bottomli = $visibleli.last() + .addClass( "ui-corner-bottom" ); + + $bottomli.add( $bottomli.find( ".ui-btn-inner" ) ) + .find( ".ui-li-link-alt" ) + .addClass( "ui-corner-br" ) + .end() + .find( ".ui-li-thumb" ) + .not(".ui-li-icon") + .addClass( "ui-corner-bl" ); + } + if ( !create ) { + this.element.trigger( "updatelayout" ); + } + }, + + // This is a generic utility method for finding the first + // node with a given nodeName. It uses basic DOM traversal + // to be fast and is meant to be a substitute for simple + // $.fn.closest() and $.fn.children() calls on a single + // element. Note that callers must pass both the lowerCase + // and upperCase version of the nodeName they are looking for. + // The main reason for this is that this function will be + // called many times and we want to avoid having to lowercase + // the nodeName from the element every time to ensure we have + // a match. Note that this function lives here for now, but may + // be moved into $.mobile if other components need a similar method. + _findFirstElementByTagName: function( ele, nextProp, lcName, ucName ) + { + var dict = {}; + dict[ lcName ] = dict[ ucName ] = true; + while ( ele ) { + if ( dict[ ele.nodeName ] ) { + return ele; + } + ele = ele[ nextProp ]; + } + return null; + }, + _getChildrenByTagName: function( ele, lcName, ucName ) + { + var results = [], + dict = {}; + dict[ lcName ] = dict[ ucName ] = true; + ele = ele.firstChild; + while ( ele ) { + if ( dict[ ele.nodeName ] ) { + results.push( ele ); + } + ele = ele.nextSibling; + } + return $( results ); + }, + + _addThumbClasses: function( containers ) + { + var i, img, len = containers.length; + for ( i = 0; i < len; i++ ) { + img = $( this._findFirstElementByTagName( containers[ i ].firstChild, "nextSibling", "img", "IMG" ) ); + if ( img.length ) { + img.addClass( "ui-li-thumb" ); + $( this._findFirstElementByTagName( img[ 0 ].parentNode, "parentNode", "li", "LI" ) ).addClass( img.is( ".ui-li-icon" ) ? "ui-li-has-icon" : "ui-li-has-thumb" ); + } + } + }, + + refresh: function( create ) { + this.parentPage = this.element.closest( ".ui-page" ); + this._createSubPages(); + + var o = this.options, + $list = this.element, + self = this, + dividertheme = $list.jqmData( "dividertheme" ) || o.dividerTheme, + listsplittheme = $list.jqmData( "splittheme" ), + listspliticon = $list.jqmData( "spliticon" ), + li = this._getChildrenByTagName( $list[ 0 ], "li", "LI" ), + counter = $.support.cssPseudoElement || !$.nodeName( $list[ 0 ], "ol" ) ? 0 : 1, + itemClassDict = {}, + item, itemClass, itemTheme, + a, last, splittheme, countParent, icon, imgParents, img, linkIcon; + + if ( counter ) { + $list.find( ".ui-li-dec" ).remove(); + } + + if ( !o.theme ) { + o.theme = $.mobile.getInheritedTheme( this.element, "c" ); + } + + for ( var pos = 0, numli = li.length; pos < numli; pos++ ) { + item = li.eq( pos ); + itemClass = "ui-li"; + + // If we're creating the element, we update it regardless + if ( create || !item.hasClass( "ui-li" ) ) { + itemTheme = item.jqmData("theme") || o.theme; + a = this._getChildrenByTagName( item[ 0 ], "a", "A" ); + + if ( a.length ) { + icon = item.jqmData("icon"); + + item.buttonMarkup({ + wrapperEls: "div", + shadow: false, + corners: false, + iconpos: "right", + icon: a.length > 1 || icon === false ? false : icon || "arrow-r", + theme: itemTheme + }); + + if ( ( icon != false ) && ( a.length == 1 ) ) { + item.addClass( "ui-li-has-arrow" ); + } + + a.first().removeClass( "ui-link" ).addClass( "ui-link-inherit" ); + + if ( a.length > 1 ) { + itemClass += " ui-li-has-alt"; + + last = a.last(); + splittheme = listsplittheme || last.jqmData( "theme" ) || o.splitTheme; + linkIcon = last.jqmData("icon"); + + last.appendTo(item) + .attr( "title", last.getEncodedText() ) + .addClass( "ui-li-link-alt" ) + .empty() + .buttonMarkup({ + shadow: false, + corners: false, + theme: itemTheme, + icon: false, + iconpos: false + }) + .find( ".ui-btn-inner" ) + .append( + $( document.createElement( "span" ) ).buttonMarkup({ + shadow: true, + corners: true, + theme: splittheme, + iconpos: "notext", + // link icon overrides list item icon overrides ul element overrides options + icon: linkIcon || icon || listspliticon || o.splitIcon + }) + ); + } + } else if ( item.jqmData( "role" ) === "list-divider" ) { + + itemClass += " ui-li-divider ui-bar-" + dividertheme; + item.attr( "role", "heading" ); + + //reset counter when a divider heading is encountered + if ( counter ) { + counter = 1; + } + + } else { + itemClass += " ui-li-static ui-body-" + itemTheme; + } + } + + if ( counter && itemClass.indexOf( "ui-li-divider" ) < 0 ) { + countParent = item.is( ".ui-li-static:first" ) ? item : item.find( ".ui-link-inherit" ); + + countParent.addClass( "ui-li-jsnumbering" ) + .prepend( "" + (counter++) + ". " ); + } + + // Instead of setting item class directly on the list item and its + // btn-inner at this point in time, push the item into a dictionary + // that tells us what class to set on it so we can do this after this + // processing loop is finished. + + if ( !itemClassDict[ itemClass ] ) { + itemClassDict[ itemClass ] = []; + } + + itemClassDict[ itemClass ].push( item[ 0 ] ); + } + + // Set the appropriate listview item classes on each list item + // and their btn-inner elements. The main reason we didn't do this + // in the for-loop above is because we can eliminate per-item function overhead + // by calling addClass() and children() once or twice afterwards. This + // can give us a significant boost on platforms like WP7.5. + + for ( itemClass in itemClassDict ) { + $( itemClassDict[ itemClass ] ).addClass( itemClass ).children( ".ui-btn-inner" ).addClass( itemClass ); + } + + $list.find( "h1, h2, h3, h4, h5, h6" ).addClass( "ui-li-heading" ) + .end() + + .find( "p, dl" ).addClass( "ui-li-desc" ) + .end() + + .find( ".ui-li-aside" ).each(function() { + var $this = $(this); + $this.prependTo( $this.parent() ); //shift aside to front for css float + }) + .end() + + .find( ".ui-li-count" ).each( function() { + $( this ).closest( "li" ).addClass( "ui-li-has-count" ); + }).addClass( "ui-btn-up-" + ( $list.jqmData( "counttheme" ) || this.options.countTheme) + " ui-btn-corner-all" ); + + // The idea here is to look at the first image in the list item + // itself, and any .ui-link-inherit element it may contain, so we + // can place the appropriate classes on the image and list item. + // Note that we used to use something like: + // + // li.find(">img:eq(0), .ui-link-inherit>img:eq(0)").each( ... ); + // + // But executing a find() like that on Windows Phone 7.5 took a + // really long time. Walking things manually with the code below + // allows the 400 listview item page to load in about 3 seconds as + // opposed to 30 seconds. + + this._addThumbClasses( li ); + this._addThumbClasses( $list.find( ".ui-link-inherit" ) ); + + this._refreshCorners( create ); + }, + + //create a string for ID/subpage url creation + _idStringEscape: function( str ) { + return str.replace(/[^a-zA-Z0-9]/g, '-'); + }, + + _createSubPages: function() { + var parentList = this.element, + parentPage = parentList.closest( ".ui-page" ), + parentUrl = parentPage.jqmData( "url" ), + parentId = parentUrl || parentPage[ 0 ][ $.expando ], + parentListId = parentList.attr( "id" ), + o = this.options, + dns = "data-" + $.mobile.ns, + self = this, + persistentFooterID = parentPage.find( ":jqmData(role='footer')" ).jqmData( "id" ), + hasSubPages; + + if ( typeof listCountPerPage[ parentId ] === "undefined" ) { + listCountPerPage[ parentId ] = -1; + } + + parentListId = parentListId || ++listCountPerPage[ parentId ]; + + $( parentList.find( "li>ul, li>ol" ).toArray().reverse() ).each(function( i ) { + var self = this, + list = $( this ), + listId = list.attr( "id" ) || parentListId + "-" + i, + parent = list.parent(), + nodeEls = $( list.prevAll().toArray().reverse() ), + nodeEls = nodeEls.length ? nodeEls : $( "" + $.trim(parent.contents()[ 0 ].nodeValue) + "" ), + title = nodeEls.first().getEncodedText(),//url limits to first 30 chars of text + id = ( parentUrl || "" ) + "&" + $.mobile.subPageUrlKey + "=" + listId, + theme = list.jqmData( "theme" ) || o.theme, + countTheme = list.jqmData( "counttheme" ) || parentList.jqmData( "counttheme" ) || o.countTheme, + newPage, anchor; + + //define hasSubPages for use in later removal + hasSubPages = true; + + newPage = list.detach() + .wrap( "
" ) + .parent() + .before( "
" + title + "
" ) + .after( persistentFooterID ? $( "
") : "" ) + .parent() + .appendTo( $.mobile.pageContainer ); + + newPage.page(); + + anchor = parent.find('a:first'); + + if ( !anchor.length ) { + anchor = $( "" ).html( nodeEls || title ).prependTo( parent.empty() ); + } + + anchor.attr( "href", "#" + id ); + + }).listview(); + + // on pagehide, remove any nested pages along with the parent page, as long as they aren't active + // and aren't embedded + if( hasSubPages && + parentPage.is( ":jqmData(external-page='true')" ) && + parentPage.data("page").options.domCache === false ) { + + var newRemove = function( e, ui ){ + var nextPage = ui.nextPage, npURL; + + if( ui.nextPage ){ + npURL = nextPage.jqmData( "url" ); + if( npURL.indexOf( parentUrl + "&" + $.mobile.subPageUrlKey ) !== 0 ){ + self.childPages().remove(); + parentPage.remove(); + } + } + }; + + // unbind the original page remove and replace with our specialized version + parentPage + .unbind( "pagehide.remove" ) + .bind( "pagehide.remove", newRemove); + } + }, + + // TODO sort out a better way to track sub pages of the listview this is brittle + childPages: function(){ + var parentUrl = this.parentPage.jqmData( "url" ); + + return $( ":jqmData(url^='"+ parentUrl + "&" + $.mobile.subPageUrlKey +"')"); + } +}); + +//auto self-init widgets +$( document ).bind( "pagecreate create", function( e ){ + $.mobile.listview.prototype.enhanceWithin( e.target ); +}); + +})( jQuery ); + +/* +* "checkboxradio" plugin +*/ + +(function( $, undefined ) { + +$.widget( "mobile.checkboxradio", $.mobile.widget, { + options: { + theme: null, + initSelector: "input[type='checkbox'],input[type='radio']" + }, + _create: function() { + var self = this, + input = this.element, + inheritAttr = function( input, dataAttr ) { + return input.jqmData( dataAttr ) || input.closest( "form,fieldset" ).jqmData( dataAttr ) + }, + // NOTE: Windows Phone could not find the label through a selector + // filter works though. + parentLabel = $( input ).closest( "label" ), + label = parentLabel.length ? parentLabel : $( input ).closest( "form,fieldset,:jqmData(role='page'),:jqmData(role='dialog')" ).find( "label" ).filter( "[for='" + input[0].id + "']" ), + inputtype = input[0].type, + mini = inheritAttr( input, "mini" ), + checkedState = inputtype + "-on", + uncheckedState = inputtype + "-off", + icon = input.parents( ":jqmData(type='horizontal')" ).length ? undefined : uncheckedState, + iconpos = inheritAttr( input, "iconpos" ), + activeBtn = icon ? "" : " " + $.mobile.activeBtnClass, + checkedClass = "ui-" + checkedState + activeBtn, + uncheckedClass = "ui-" + uncheckedState, + checkedicon = "ui-icon-" + checkedState, + uncheckedicon = "ui-icon-" + uncheckedState; + + if ( inputtype !== "checkbox" && inputtype !== "radio" ) { + return; + } + + // Expose for other methods + $.extend( this, { + label: label, + inputtype: inputtype, + checkedClass: checkedClass, + uncheckedClass: uncheckedClass, + checkedicon: checkedicon, + uncheckedicon: uncheckedicon + }); + + // If there's no selected theme check the data attr + if( !this.options.theme ) { + this.options.theme = $.mobile.getInheritedTheme( this.element, "c" ); + } + + label.buttonMarkup({ + theme: this.options.theme, + icon: icon, + shadow: false, + mini: mini, + iconpos: iconpos + }); + + // Wrap the input + label in a div + var wrapper = document.createElement('div'); + wrapper.className = 'ui-' + inputtype; + + input.add( label ).wrapAll( wrapper ); + + label.bind({ + vmouseover: function( event ) { + if ( $( this ).parent().is( ".ui-disabled" ) ) { + event.stopPropagation(); + } + }, + + vclick: function( event ) { + if ( input.is( ":disabled" ) ) { + event.preventDefault(); + return; + } + + self._cacheVals(); + + input.prop( "checked", inputtype === "radio" && true || !input.prop( "checked" ) ); + + // trigger click handler's bound directly to the input as a substitute for + // how label clicks behave normally in the browsers + // TODO: it would be nice to let the browser's handle the clicks and pass them + // through to the associate input. we can swallow that click at the parent + // wrapper element level + input.triggerHandler( 'click' ); + + // Input set for common radio buttons will contain all the radio + // buttons, but will not for checkboxes. clearing the checked status + // of other radios ensures the active button state is applied properly + self._getInputSet().not( input ).prop( "checked", false ); + + self._updateAll(); + return false; + } + }); + + input + .bind({ + vmousedown: function() { + self._cacheVals(); + }, + + vclick: function() { + var $this = $(this); + + // Adds checked attribute to checked input when keyboard is used + if ( $this.is( ":checked" ) ) { + + $this.prop( "checked", true); + self._getInputSet().not($this).prop( "checked", false ); + } else { + + $this.prop( "checked", false ); + } + + self._updateAll(); + }, + + focus: function() { + label.addClass( $.mobile.focusClass ); + }, + + blur: function() { + label.removeClass( $.mobile.focusClass ); + } + }); + + this.refresh(); + }, + + _cacheVals: function() { + this._getInputSet().each(function() { + $(this).jqmData( "cacheVal", this.checked ); + }); + }, + + //returns either a set of radios with the same name attribute, or a single checkbox + _getInputSet: function(){ + if(this.inputtype === "checkbox") { + return this.element; + } + + return this.element.closest( "form,fieldset,:jqmData(role='page')" ) + .find( "input[name='"+ this.element[0].name +"'][type='"+ this.inputtype +"']" ); + }, + + _updateAll: function() { + var self = this; + + this._getInputSet().each(function() { + var $this = $(this); + + if ( this.checked || self.inputtype === "checkbox" ) { + $this.trigger( "change" ); + } + }) + .checkboxradio( "refresh" ); + }, + + refresh: function() { + var input = this.element[0], + label = this.label, + icon = label.find( ".ui-icon" ); + + if ( input.checked ) { + label.addClass( this.checkedClass ).removeClass( this.uncheckedClass ); + icon.addClass( this.checkedicon ).removeClass( this.uncheckedicon ); + } else { + label.removeClass( this.checkedClass ).addClass( this.uncheckedClass ); + icon.removeClass( this.checkedicon ).addClass( this.uncheckedicon ); + } + + if ( input.disabled ) { + this.disable(); + } else { + this.enable(); + } + }, + + disable: function() { + this.element.prop( "disabled", true ).parent().addClass( "ui-disabled" ); + }, + + enable: function() { + this.element.prop( "disabled", false ).parent().removeClass( "ui-disabled" ); + } +}); + +//auto self-init widgets +$( document ).bind( "pagecreate create", function( e ){ + $.mobile.checkboxradio.prototype.enhanceWithin( e.target, true ); +}); + +})( jQuery ); + +(function( $, undefined ) { + +$.widget( "mobile.button", $.mobile.widget, { + options: { + theme: null, + icon: null, + iconpos: null, + inline: false, + corners: true, + shadow: true, + iconshadow: true, + initSelector: "button, [type='button'], [type='submit'], [type='reset'], [type='image']", + mini: false + }, + _create: function() { + var $el = this.element, + $button, + o = this.options, + type, + name, + classes = "", + $buttonPlaceholder; + + // if this is a link, check if it's been enhanced and, if not, use the right function + if( $el[ 0 ].tagName === "A" ) { + !$el.hasClass( "ui-btn" ) && $el.buttonMarkup(); + return; + } + + // get the inherited theme + // TODO centralize for all widgets + if ( !this.options.theme ) { + this.options.theme = $.mobile.getInheritedTheme( this.element, "c" ); + } + + // TODO: Post 1.1--once we have time to test thoroughly--any classes manually applied to the original element should be carried over to the enhanced element, with an `-enhanced` suffix. See https://github.com/jquery/jquery-mobile/issues/3577 + /* if( $el[0].className.length ) { + classes = $el[0].className; + } */ + if( !!~$el[0].className.indexOf( "ui-btn-left" ) ) { + classes = "ui-btn-left"; + } + + if( !!~$el[0].className.indexOf( "ui-btn-right" ) ) { + classes = "ui-btn-right"; + } + + // Add ARIA role + this.button = $( "
" ) + .text( $el.text() || $el.val() ) + .insertBefore( $el ) + .buttonMarkup({ + theme: o.theme, + icon: o.icon, + iconpos: o.iconpos, + inline: o.inline, + corners: o.corners, + shadow: o.shadow, + iconshadow: o.iconshadow, + mini: o.mini + }) + .addClass( classes ) + .append( $el.addClass( "ui-btn-hidden" ) ); + + $button = this.button; + type = $el.attr( "type" ); + name = $el.attr( "name" ); + + // Add hidden input during submit if input type="submit" has a name. + if ( type !== "button" && type !== "reset" && name ) { + $el.bind( "vclick", function() { + // Add hidden input if it doesn’t already exist. + if( $buttonPlaceholder === undefined ) { + $buttonPlaceholder = $( "", { + type: "hidden", + name: $el.attr( "name" ), + value: $el.attr( "value" ) + }).insertBefore( $el ); + + // Bind to doc to remove after submit handling + $( document ).one("submit", function(){ + $buttonPlaceholder.remove(); + + // reset the local var so that the hidden input + // will be re-added on subsequent clicks + $buttonPlaceholder = undefined; + }); + } + }); + } + + $el.bind({ + focus: function() { + $button.addClass( $.mobile.focusClass ); + }, + + blur: function() { + $button.removeClass( $.mobile.focusClass ); + } + }); + + this.refresh(); + }, + + enable: function() { + this.element.attr( "disabled", false ); + this.button.removeClass( "ui-disabled" ).attr( "aria-disabled", false ); + return this._setOption( "disabled", false ); + }, + + disable: function() { + this.element.attr( "disabled", true ); + this.button.addClass( "ui-disabled" ).attr( "aria-disabled", true ); + return this._setOption( "disabled", true ); + }, + + refresh: function() { + var $el = this.element; + + if ( $el.prop("disabled") ) { + this.disable(); + } else { + this.enable(); + } + + // Grab the button's text element from its implementation-independent data item + $( this.button.data( 'buttonElements' ).text ).text( $el.text() || $el.val() ); + } +}); + +//auto self-init widgets +$( document ).bind( "pagecreate create", function( e ){ + $.mobile.button.prototype.enhanceWithin( e.target, true ); +}); + +})( jQuery ); + +(function( $, undefined ) { + +$.fn.controlgroup = function( options ) { + function flipClasses( els, flCorners ) { + els.removeClass( "ui-btn-corner-all ui-shadow" ) + .eq( 0 ).addClass( flCorners[ 0 ] ) + .end() + .last().addClass( flCorners[ 1 ] ).addClass( "ui-controlgroup-last" ); + } + + return this.each(function() { + var $el = $( this ), + o = $.extend({ + direction: $el.jqmData( "type" ) || "vertical", + shadow: false, + excludeInvisible: true, + mini: $el.jqmData( "mini" ) + }, options ), + groupheading = $el.children( "legend" ), + flCorners = o.direction == "horizontal" ? [ "ui-corner-left", "ui-corner-right" ] : [ "ui-corner-top", "ui-corner-bottom" ], + type = $el.find( "input" ).first().attr( "type" ); + + // Replace legend with more stylable replacement div + if ( groupheading.length ) { + $el.wrapInner( "
" ); + $( "
" + groupheading.html() + "
" ).insertBefore( $el.children(0) ); + groupheading.remove(); + } + + $el.addClass( "ui-corner-all ui-controlgroup ui-controlgroup-" + o.direction ); + + flipClasses( $el.find( ".ui-btn" + ( o.excludeInvisible ? ":visible" : "" ) ).not('.ui-slider-handle'), flCorners ); + flipClasses( $el.find( ".ui-btn-inner" ), flCorners ); + + if ( o.shadow ) { + $el.addClass( "ui-shadow" ); + } + + if ( o.mini ) { + $el.addClass( "ui-mini" ); + } + + }); +}; + +// The pagecreate handler for controlgroup is in jquery.mobile.init because of the soft-dependency on the wrapped widgets + +})(jQuery); + +(function( $, undefined ) { + +$( document ).bind( "pagecreate create", function( e ){ + + //links within content areas, tests included with page + $( e.target ) + .find( "a" ) + .jqmEnhanceable() + .not( ".ui-btn, .ui-link-inherit, :jqmData(role='none'), :jqmData(role='nojs')" ) + .addClass( "ui-link" ); + +}); + +})( jQuery ); + + +( function( $ ) { + var meta = $( "meta[name=viewport]" ), + initialContent = meta.attr( "content" ), + disabledZoom = initialContent + ",maximum-scale=1, user-scalable=no", + enabledZoom = initialContent + ",maximum-scale=10, user-scalable=yes", + disabledInitially = /(user-scalable[\s]*=[\s]*no)|(maximum-scale[\s]*=[\s]*1)[$,\s]/.test( initialContent ); + + $.mobile.zoom = $.extend( {}, { + enabled: !disabledInitially, + locked: false, + disable: function( lock ) { + if( !disabledInitially && !$.mobile.zoom.locked ){ + meta.attr( "content", disabledZoom ); + $.mobile.zoom.enabled = false; + $.mobile.zoom.locked = lock || false; + } + }, + enable: function( unlock ) { + if( !disabledInitially && ( !$.mobile.zoom.locked || unlock === true ) ){ + meta.attr( "content", enabledZoom ); + $.mobile.zoom.enabled = true; + $.mobile.zoom.locked = false; + } + }, + restore: function() { + if( !disabledInitially ){ + meta.attr( "content", initialContent ); + $.mobile.zoom.enabled = true; + } + } + }); + +}( jQuery )); + +(function( $, undefined ) { + +$.widget( "mobile.textinput", $.mobile.widget, { + options: { + theme: null, + // This option defaults to true on iOS devices. + preventFocusZoom: /iPhone|iPad|iPod/.test( navigator.platform ) && navigator.userAgent.indexOf( "AppleWebKit" ) > -1, + initSelector: "input[type='text'], input[type='search'], :jqmData(type='search'), input[type='number'], :jqmData(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])", + clearSearchButtonText: "clear text" + }, + + _create: function() { + + var input = this.element, + o = this.options, + theme = o.theme || $.mobile.getInheritedTheme( this.element, "c" ), + themeclass = " ui-body-" + theme, + mini = input.jqmData("mini") == true, + miniclass = mini ? " ui-mini" : "", + focusedEl, clearbtn; + + $( "label[for='" + input.attr( "id" ) + "']" ).addClass( "ui-input-text" ); + + focusedEl = input.addClass("ui-input-text ui-body-"+ theme ); + + // 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 input[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. + input[0].setAttribute( "autocorrect", "off" ); + input[0].setAttribute( "autocomplete", "off" ); + } + + + //"search" input widget + if ( input.is( "[type='search'],:jqmData(type='search')" ) ) { + + focusedEl = input.wrap( "" ).parent(); + clearbtn = $( "
" + o.clearSearchButtonText + "" ) + .bind('click', function( event ) { + input + .val( "" ) + .focus() + .trigger( "change" ); + clearbtn.addClass( "ui-input-clear-hidden" ); + event.preventDefault(); + }) + .appendTo( focusedEl ) + .buttonMarkup({ + icon: "delete", + iconpos: "notext", + corners: true, + shadow: true, + mini: mini + }); + + function toggleClear() { + setTimeout(function() { + clearbtn.toggleClass( "ui-input-clear-hidden", !input.val() ); + }, 0); + } + + toggleClear(); + + input.bind('paste cut keyup focus change blur', toggleClear); + + } else { + input.addClass( "ui-corner-all ui-shadow-inset" + themeclass + miniclass ); + } + + input.focus(function() { + focusedEl.addClass( $.mobile.focusClass ); + }) + .blur(function(){ + focusedEl.removeClass( $.mobile.focusClass ); + }) + // In many situations, iOS will zoom into the select upon tap, this prevents that from happening + .bind( "focus", function() { + if( o.preventFocusZoom ){ + $.mobile.zoom.disable( true ); + } + }) + .bind( "blur", function() { + if( o.preventFocusZoom ){ + $.mobile.zoom.enable( true ); + } + }); + + // Autogrow + if ( input.is( "textarea" ) ) { + var extraLineHeight = 15, + keyupTimeoutBuffer = 100, + keyup = function() { + var scrollHeight = input[ 0 ].scrollHeight, + clientHeight = input[ 0 ].clientHeight; + + if ( clientHeight < scrollHeight ) { + input.height(scrollHeight + extraLineHeight); + } + }, + keyupTimeout; + + input.keyup(function() { + clearTimeout( keyupTimeout ); + keyupTimeout = setTimeout( keyup, keyupTimeoutBuffer ); + }); + + // binding to pagechange here ensures that for pages loaded via + // ajax the height is recalculated without user input + $( document ).one( "pagechange", keyup ); + + // Issue 509: the browser is not providing scrollHeight properly until the styles load + if ( $.trim( input.val() ) ) { + // bind to the window load to make sure the height is calculated based on BOTH + // the DOM and CSS + $( window ).load( keyup ); + } + } + }, + + disable: function(){ + ( this.element.attr( "disabled", true ).is( "[type='search'],:jqmData(type='search')" ) ? + this.element.parent() : this.element ).addClass( "ui-disabled" ); + }, + + enable: function(){ + ( this.element.attr( "disabled", false).is( "[type='search'],:jqmData(type='search')" ) ? + this.element.parent() : this.element ).removeClass( "ui-disabled" ); + } +}); + +//auto self-init widgets +$( document ).bind( "pagecreate create", function( e ){ + $.mobile.textinput.prototype.enhanceWithin( e.target, true ); +}); + +})( jQuery ); + +(function( $, undefined ) { + +$.mobile.listview.prototype.options.filter = false; +$.mobile.listview.prototype.options.filterPlaceholder = "Filter items..."; +$.mobile.listview.prototype.options.filterTheme = "c"; +$.mobile.listview.prototype.options.filterCallback = function( text, searchValue ){ + return text.toLowerCase().indexOf( searchValue ) === -1; +}; + +$( document ).delegate( ":jqmData(role='listview')", "listviewcreate", function() { + + var list = $( this ), + listview = list.data( "listview" ); + + if ( !listview.options.filter ) { + return; + } + + var wrapper = $( "
", { + "class": "ui-listview-filter ui-bar-" + listview.options.filterTheme, + "role": "search" + }), + search = $( "", { + placeholder: listview.options.filterPlaceholder + }) + .attr( "data-" + $.mobile.ns + "type", "search" ) + .jqmData( "lastval", "" ) + .bind( "keyup change", function() { + + var $this = $(this), + val = this.value.toLowerCase(), + listItems = null, + lastval = $this.jqmData( "lastval" ) + "", + childItems = false, + itemtext = "", + item; + + // Change val as lastval for next execution + $this.jqmData( "lastval" , val ); + if ( val.length < lastval.length || val.indexOf(lastval) !== 0 ) { + + // Removed chars or pasted something totally different, check all items + listItems = list.children(); + } else { + + // Only chars added, not removed, only use visible subset + listItems = list.children( ":not(.ui-screen-hidden)" ); + } + + if ( val ) { + + // This handles hiding regular rows without the text we search for + // and any list dividers without regular rows shown under it + + for ( var i = listItems.length - 1; i >= 0; i-- ) { + item = $( listItems[ i ] ); + itemtext = item.jqmData( "filtertext" ) || item.text(); + + if ( item.is( "li:jqmData(role=list-divider)" ) ) { + + item.toggleClass( "ui-filter-hidequeue" , !childItems ); + + // New bucket! + childItems = false; + + } else if ( listview.options.filterCallback( itemtext, val ) ) { + + //mark to be hidden + item.toggleClass( "ui-filter-hidequeue" , true ); + } else { + + // There's a shown item in the bucket + childItems = true; + } + } + + // Show items, not marked to be hidden + listItems + .filter( ":not(.ui-filter-hidequeue)" ) + .toggleClass( "ui-screen-hidden", false ); + + // Hide items, marked to be hidden + listItems + .filter( ".ui-filter-hidequeue" ) + .toggleClass( "ui-screen-hidden", true ) + .toggleClass( "ui-filter-hidequeue", false ); + + } else { + + //filtervalue is empty => show all + listItems.toggleClass( "ui-screen-hidden", false ); + } + listview._refreshCorners(); + }) + .appendTo( wrapper ) + .textinput(); + + if ( listview.options.inset ) { + wrapper.addClass( "ui-listview-filter-inset" ); + } + + wrapper.bind( "submit", function() { + return false; + }) + .insertBefore( list ); +}); + +})( jQuery ); + +( function( $, undefined ) { + +$.widget( "mobile.slider", $.mobile.widget, { + options: { + theme: null, + trackTheme: null, + disabled: false, + initSelector: "input[type='range'], :jqmData(type='range'), :jqmData(role='slider')", + mini: false + }, + + _create: function() { + + // TODO: Each of these should have comments explain what they're for + var self = this, + + control = this.element, + + parentTheme = $.mobile.getInheritedTheme( control, "c" ), + + theme = this.options.theme || parentTheme, + + trackTheme = this.options.trackTheme || parentTheme, + + cType = control[ 0 ].nodeName.toLowerCase(), + + selectClass = ( cType == "select" ) ? "ui-slider-switch" : "", + + controlID = control.attr( "id" ), + + labelID = controlID + "-label", + + label = $( "[for='"+ controlID +"']" ).attr( "id", labelID ), + + val = function() { + return cType == "input" ? parseFloat( control.val() ) : control[0].selectedIndex; + }, + + min = cType == "input" ? parseFloat( control.attr( "min" ) ) : 0, + + max = cType == "input" ? parseFloat( control.attr( "max" ) ) : control.find( "option" ).length-1, + + step = window.parseFloat( control.attr( "step" ) || 1 ), + + inlineClass = ( this.options.inline || control.jqmData("inline") == true ) ? " ui-slider-inline" : "", + + miniClass = ( this.options.mini || control.jqmData("mini") ) ? " ui-slider-mini" : "", + + + domHandle = document.createElement('a'), + handle = $( domHandle ), + domSlider = document.createElement('div'), + slider = $( domSlider ), + + valuebg = control.jqmData("highlight") && cType != "select" ? (function() { + var bg = document.createElement('div'); + bg.className = 'ui-slider-bg ui-btn-active ui-btn-corner-all'; + return $( bg ).prependTo( slider ); + })() : false, + + options; + + domHandle.setAttribute( 'href', "#" ); + domSlider.setAttribute('role','application'); + domSlider.className = ['ui-slider ',selectClass," ui-btn-down-",trackTheme,' ui-btn-corner-all', inlineClass, miniClass].join(""); + domHandle.className = 'ui-slider-handle'; + domSlider.appendChild(domHandle); + + handle.buttonMarkup({ corners: true, theme: theme, shadow: true }) + .attr({ + "role": "slider", + "aria-valuemin": min, + "aria-valuemax": max, + "aria-valuenow": val(), + "aria-valuetext": val(), + "title": val(), + "aria-labelledby": labelID + }); + + $.extend( this, { + slider: slider, + handle: handle, + valuebg: valuebg, + dragging: false, + beforeStart: null, + userModified: false, + mouseMoved: false + }); + + if ( cType == "select" ) { + var wrapper = document.createElement('div'); + wrapper.className = 'ui-slider-inneroffset'; + + for(var j = 0,length = domSlider.childNodes.length;j < length;j++){ + wrapper.appendChild(domSlider.childNodes[j]); + } + + domSlider.appendChild(wrapper); + + // slider.wrapInner( "
" ); + + // make the handle move with a smooth transition + handle.addClass( "ui-slider-handle-snapping" ); + + options = control.find( "option" ); + + for(var i = 0, optionsCount = options.length; i < optionsCount; i++){ + var side = !i ? "b":"a", + sliderTheme = !i ? " ui-btn-down-" + trackTheme :( " " + $.mobile.activeBtnClass ), + sliderLabel = document.createElement('div'), + sliderImg = document.createElement('span'); + + sliderImg.className = ['ui-slider-label ui-slider-label-',side,sliderTheme," ui-btn-corner-all"].join(""); + sliderImg.setAttribute('role','img'); + sliderImg.appendChild(document.createTextNode(options[i].innerHTML)); + $(sliderImg).prependTo( slider ); + } + + self._labels = $( ".ui-slider-label", slider ); + + } + + label.addClass( "ui-slider" ); + + // monitor the input for updated values + control.addClass( cType === "input" ? "ui-slider-input" : "ui-slider-switch" ) + .change( function() { + // if the user dragged the handle, the "change" event was triggered from inside refresh(); don't call refresh() again + if (!self.mouseMoved) { + self.refresh( val(), true ); + } + }) + .keyup( function() { // necessary? + self.refresh( val(), true, true ); + }) + .blur( function() { + self.refresh( val(), true ); + }); + + // prevent screen drag when slider activated + $( document ).bind( "vmousemove", function( event ) { + if ( self.dragging ) { + // self.mouseMoved must be updated before refresh() because it will be used in the control "change" event + self.mouseMoved = true; + + if ( cType === "select" ) { + // make the handle move in sync with the mouse + handle.removeClass( "ui-slider-handle-snapping" ); + } + + self.refresh( event ); + + // only after refresh() you can calculate self.userModified + self.userModified = self.beforeStart !== control[0].selectedIndex; + return false; + } + }); + + slider.bind( "vmousedown", function( event ) { + self.dragging = true; + self.userModified = false; + self.mouseMoved = false; + + if ( cType === "select" ) { + self.beforeStart = control[0].selectedIndex; + } + + self.refresh( event ); + return false; + }) + .bind( "vclick", false ); + + slider.add( document ) + .bind( "vmouseup", function() { + if ( self.dragging ) { + + self.dragging = false; + + if ( cType === "select") { + + // make the handle move with a smooth transition + handle.addClass( "ui-slider-handle-snapping" ); + + if ( self.mouseMoved ) { + + // this is a drag, change the value only if user dragged enough + if ( self.userModified ) { + self.refresh( self.beforeStart == 0 ? 1 : 0 ); + } + else { + self.refresh( self.beforeStart ); + } + + } + else { + // this is just a click, change the value + self.refresh( self.beforeStart == 0 ? 1 : 0 ); + } + + } + + self.mouseMoved = false; + + return false; + } + }); + + slider.insertAfter( control ); + + // Only add focus class to toggle switch, sliders get it automatically from ui-btn + if( cType == 'select' ) { + this.handle.bind({ + focus: function() { + slider.addClass( $.mobile.focusClass ); + }, + + blur: function() { + slider.removeClass( $.mobile.focusClass ); + } + }); + } + + this.handle.bind({ + // NOTE force focus on handle + vmousedown: function() { + $( this ).focus(); + }, + + vclick: false, + + keydown: function( event ) { + var index = val(); + + if ( self.options.disabled ) { + return; + } + + // In all cases prevent the default and mark the handle as active + switch ( event.keyCode ) { + case $.mobile.keyCode.HOME: + case $.mobile.keyCode.END: + case $.mobile.keyCode.PAGE_UP: + case $.mobile.keyCode.PAGE_DOWN: + case $.mobile.keyCode.UP: + case $.mobile.keyCode.RIGHT: + case $.mobile.keyCode.DOWN: + case $.mobile.keyCode.LEFT: + event.preventDefault(); + + if ( !self._keySliding ) { + self._keySliding = true; + $( this ).addClass( "ui-state-active" ); + } + break; + } + + // move the slider according to the keypress + switch ( event.keyCode ) { + case $.mobile.keyCode.HOME: + self.refresh( min ); + break; + case $.mobile.keyCode.END: + self.refresh( max ); + break; + case $.mobile.keyCode.PAGE_UP: + case $.mobile.keyCode.UP: + case $.mobile.keyCode.RIGHT: + self.refresh( index + step ); + break; + case $.mobile.keyCode.PAGE_DOWN: + case $.mobile.keyCode.DOWN: + case $.mobile.keyCode.LEFT: + self.refresh( index - step ); + break; + } + }, // remove active mark + + keyup: function( event ) { + if ( self._keySliding ) { + self._keySliding = false; + $( this ).removeClass( "ui-state-active" ); + } + } + }); + + this.refresh(undefined, undefined, true); + }, + + refresh: function( val, isfromControl, preventInputUpdate ) { + + if ( this.options.disabled || this.element.attr('disabled')) { + this.disable(); + } + + var control = this.element, percent, + cType = control[0].nodeName.toLowerCase(), + min = cType === "input" ? parseFloat( control.attr( "min" ) ) : 0, + max = cType === "input" ? parseFloat( control.attr( "max" ) ) : control.find( "option" ).length - 1, + step = (cType === "input" && parseFloat( control.attr( "step" ) ) > 0) ? parseFloat(control.attr("step")) : 1; + + if ( typeof val === "object" ) { + var data = val, + // a slight tolerance helped get to the ends of the slider + tol = 8; + if ( !this.dragging || + data.pageX < this.slider.offset().left - tol || + data.pageX > this.slider.offset().left + this.slider.width() + tol ) { + return; + } + percent = Math.round( ( ( data.pageX - this.slider.offset().left ) / this.slider.width() ) * 100 ); + } else { + if ( val == null ) { + val = cType === "input" ? parseFloat( control.val() || 0 ) : control[0].selectedIndex; + } + percent = ( parseFloat( val ) - min ) / ( max - min ) * 100; + } + + if ( isNaN( percent ) ) { + return; + } + + if ( percent < 0 ) { + percent = 0; + } + + if ( percent > 100 ) { + percent = 100; + } + + var newval = ( percent / 100 ) * ( max - min ) + min; + + //from jQuery UI slider, the following source will round to the nearest step + var valModStep = ( newval - min ) % step; + var alignValue = newval - valModStep; + + if ( Math.abs( valModStep ) * 2 >= step ) { + alignValue += ( valModStep > 0 ) ? step : ( -step ); + } + // Since JavaScript has problems with large floats, round + // the final value to 5 digits after the decimal point (see jQueryUI: #4124) + newval = parseFloat( alignValue.toFixed(5) ); + + if ( newval < min ) { + newval = min; + } + + if ( newval > max ) { + newval = max; + } + + this.handle.css( "left", percent + "%" ); + this.handle.attr( { + "aria-valuenow": cType === "input" ? newval : control.find( "option" ).eq( newval ).attr( "value" ), + "aria-valuetext": cType === "input" ? newval : control.find( "option" ).eq( newval ).getEncodedText(), + title: cType === "input" ? newval : control.find( "option" ).eq( newval ).getEncodedText() + }); + this.valuebg && this.valuebg.css( "width", percent + "%" ); + + // drag the label widths + if ( this._labels ) { + var handlePercent = this.handle.width() / this.slider.width() * 100, + aPercent = percent && handlePercent + ( 100 - handlePercent ) * percent / 100, + bPercent = percent === 100 ? 0 : Math.min( handlePercent + 100 - aPercent, 100 ); + + this._labels.each(function(){ + var ab = $(this).is( ".ui-slider-label-a" ); + $( this ).width( ( ab ? aPercent : bPercent ) + "%" ); + }); + } + + if ( !preventInputUpdate ) { + var valueChanged = false; + + // update control"s value + if ( cType === "input" ) { + valueChanged = control.val() !== newval; + control.val( newval ); + } else { + valueChanged = control[ 0 ].selectedIndex !== newval; + control[ 0 ].selectedIndex = newval; + } + if ( !isfromControl && valueChanged ) { + control.trigger( "change" ); + } + } + }, + + enable: function() { + this.element.attr( "disabled", false ); + this.slider.removeClass( "ui-disabled" ).attr( "aria-disabled", false ); + return this._setOption( "disabled", false ); + }, + + disable: function() { + this.element.attr( "disabled", true ); + this.slider.addClass( "ui-disabled" ).attr( "aria-disabled", true ); + return this._setOption( "disabled", true ); + } + +}); + +//auto self-init widgets +$( document ).bind( "pagecreate create", function( e ){ + $.mobile.slider.prototype.enhanceWithin( e.target, true ); +}); + +})( jQuery ); + +(function( $, undefined ) { + +$.widget( "mobile.selectmenu", $.mobile.widget, { + options: { + theme: null, + disabled: false, + icon: "arrow-d", + iconpos: "right", + inline: false, + corners: true, + shadow: true, + iconshadow: true, + overlayTheme: "a", + hidePlaceholderMenuItems: true, + closeText: "Close", + nativeMenu: true, + // This option defaults to true on iOS devices. + preventFocusZoom: /iPhone|iPad|iPod/.test( navigator.platform ) && navigator.userAgent.indexOf( "AppleWebKit" ) > -1, + initSelector: "select:not(:jqmData(role='slider'))", + mini: false + }, + + _button: function(){ + return $( "
" ); + }, + + _setDisabled: function( value ) { + this.element.attr( "disabled", value ); + this.button.attr( "aria-disabled", value ); + return this._setOption( "disabled", value ); + }, + + _focusButton : function() { + var self = this; + + setTimeout( function() { + self.button.focus(); + }, 40); + }, + + _selectOptions: function() { + return this.select.find( "option" ); + }, + + // setup items that are generally necessary for select menu extension + _preExtension: function(){ + var classes = ""; + // TODO: Post 1.1--once we have time to test thoroughly--any classes manually applied to the original element should be carried over to the enhanced element, with an `-enhanced` suffix. See https://github.com/jquery/jquery-mobile/issues/3577 + /* if( $el[0].className.length ) { + classes = $el[0].className; + } */ + if( !!~this.element[0].className.indexOf( "ui-btn-left" ) ) { + classes = " ui-btn-left"; + } + + if( !!~this.element[0].className.indexOf( "ui-btn-right" ) ) { + classes = " ui-btn-right"; + } + + this.select = this.element.wrap( "
" ); + this.selectID = this.select.attr( "id" ); + this.label = $( "label[for='"+ this.selectID +"']" ).addClass( "ui-select" ); + this.isMultiple = this.select[ 0 ].multiple; + if ( !this.options.theme ) { + this.options.theme = $.mobile.getInheritedTheme( this.select, "c" ); + } + }, + + _create: function() { + this._preExtension(); + + // Allows for extension of the native select for custom selects and other plugins + // see select.custom for example extension + // TODO explore plugin registration + this._trigger( "beforeCreate" ); + + this.button = this._button(); + + var self = this, + + options = this.options, + + // IE throws an exception at options.item() function when + // there is no selected item + // select first in this case + selectedIndex = this.select[ 0 ].selectedIndex == -1 ? 0 : this.select[ 0 ].selectedIndex, + + // TODO values buttonId and menuId are undefined here + button = this.button + .text( $( this.select[ 0 ].options.item( selectedIndex ) ).text() ) + .insertBefore( this.select ) + .buttonMarkup( { + theme: options.theme, + icon: options.icon, + iconpos: options.iconpos, + inline: options.inline, + corners: options.corners, + shadow: options.shadow, + iconshadow: options.iconshadow, + mini: options.mini + }); + + // Opera does not properly support opacity on select elements + // In Mini, it hides the element, but not its text + // On the desktop,it seems to do the opposite + // for these reasons, using the nativeMenu option results in a full native select in Opera + if ( options.nativeMenu && window.opera && window.opera.version ) { + this.select.addClass( "ui-select-nativeonly" ); + } + + // Add counter for multi selects + if ( this.isMultiple ) { + this.buttonCount = $( "" ) + .addClass( "ui-li-count ui-btn-up-c ui-btn-corner-all" ) + .hide() + .appendTo( button.addClass('ui-li-has-count') ); + } + + // Disable if specified + if ( options.disabled || this.element.attr('disabled')) { + this.disable(); + } + + // Events on native select + this.select.change( function() { + self.refresh(); + }); + + this.build(); + }, + + build: function() { + var self = this; + + this.select + .appendTo( self.button ) + .bind( "vmousedown", function() { + // Add active class to button + self.button.addClass( $.mobile.activeBtnClass ); + }) + .bind( "focus", function() { + self.button.addClass( $.mobile.focusClass ); + }) + .bind( "blur", function() { + self.button.removeClass( $.mobile.focusClass ); + }) + .bind( "focus vmouseover", function() { + self.button.trigger( "vmouseover" ); + }) + .bind( "vmousemove", function() { + // Remove active class on scroll/touchmove + self.button.removeClass( $.mobile.activeBtnClass ); + }) + .bind( "change blur vmouseout", function() { + self.button.trigger( "vmouseout" ) + .removeClass( $.mobile.activeBtnClass ); + }) + .bind( "change blur", function() { + self.button.removeClass( "ui-btn-down-" + self.options.theme ); + }); + + // In many situations, iOS will zoom into the select upon tap, this prevents that from happening + self.button.bind( "vmousedown", function() { + if( self.options.preventFocusZoom ){ + $.mobile.zoom.disable( true ); + } + }) + .bind( "mouseup", function() { + if( self.options.preventFocusZoom ){ + $.mobile.zoom.enable( true ); + } + }); + }, + + selected: function() { + return this._selectOptions().filter( ":selected" ); + }, + + selectedIndices: function() { + var self = this; + + return this.selected().map( function() { + return self._selectOptions().index( this ); + }).get(); + }, + + setButtonText: function() { + var self = this, selected = this.selected(); + + this.button.find( ".ui-btn-text" ).text( function() { + if ( !self.isMultiple ) { + return selected.text(); + } + + return selected.length ? selected.map( function() { + return $( this ).text(); + }).get().join( ", " ) : self.placeholder; + }); + }, + + setButtonCount: function() { + var selected = this.selected(); + + // multiple count inside button + if ( this.isMultiple ) { + this.buttonCount[ selected.length > 1 ? "show" : "hide" ]().text( selected.length ); + } + }, + + refresh: function() { + this.setButtonText(); + this.setButtonCount(); + }, + + // open and close preserved in native selects + // to simplify users code when looping over selects + open: $.noop, + close: $.noop, + + disable: function() { + this._setDisabled( true ); + this.button.addClass( "ui-disabled" ); + }, + + enable: function() { + this._setDisabled( false ); + this.button.removeClass( "ui-disabled" ); + } +}); + +//auto self-init widgets +$( document ).bind( "pagecreate create", function( e ){ + $.mobile.selectmenu.prototype.enhanceWithin( e.target, true ); +}); +})( jQuery ); + +/* +* custom "selectmenu" plugin +*/ + +(function( $, undefined ) { + var extendSelect = function( widget ){ + + var select = widget.select, + selectID = widget.selectID, + label = widget.label, + thisPage = widget.select.closest( ".ui-page" ), + screen = $( "
", {"class": "ui-selectmenu-screen ui-screen-hidden"} ).appendTo( thisPage ), + selectOptions = widget._selectOptions(), + isMultiple = widget.isMultiple = widget.select[ 0 ].multiple, + buttonId = selectID + "-button", + menuId = selectID + "-menu", + menuPage = $( "
" + + "
" + + "
" + label.getEncodedText() + "
"+ + "
"+ + "
"+ + "
" ), + + listbox = $("
", { "class": "ui-selectmenu ui-selectmenu-hidden ui-overlay-shadow ui-corner-all ui-body-" + widget.options.overlayTheme + " " + $.mobile.defaultDialogTransition } ).insertAfter(screen), + + list = $( "