diff --git a/.hgignore b/.hgignore index c663dc7..e0f3714 100644 --- a/.hgignore +++ b/.hgignore @@ -5,7 +5,6 @@ .vscode docs/.ipynb_checkpoints test/* -gnu-prolog/thun venv build Thun.egg-info diff --git a/docs/VUI-docs/Makefile b/docs/VUI-docs/Makefile deleted file mode 100644 index 69fe55e..0000000 --- a/docs/VUI-docs/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -SOURCEDIR = source -BUILDDIR = build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file diff --git a/docs/VUI-docs/build/doctrees/core.doctree b/docs/VUI-docs/build/doctrees/core.doctree deleted file mode 100644 index 555cc59..0000000 Binary files a/docs/VUI-docs/build/doctrees/core.doctree and /dev/null differ diff --git a/docs/VUI-docs/build/doctrees/display.doctree b/docs/VUI-docs/build/doctrees/display.doctree deleted file mode 100644 index 42869cb..0000000 Binary files a/docs/VUI-docs/build/doctrees/display.doctree and /dev/null differ diff --git a/docs/VUI-docs/build/doctrees/environment.pickle b/docs/VUI-docs/build/doctrees/environment.pickle deleted file mode 100644 index ae5e7e7..0000000 Binary files a/docs/VUI-docs/build/doctrees/environment.pickle and /dev/null differ diff --git a/docs/VUI-docs/build/doctrees/index.doctree b/docs/VUI-docs/build/doctrees/index.doctree deleted file mode 100644 index 32d4306..0000000 Binary files a/docs/VUI-docs/build/doctrees/index.doctree and /dev/null differ diff --git a/docs/VUI-docs/build/doctrees/main.doctree b/docs/VUI-docs/build/doctrees/main.doctree deleted file mode 100644 index efb93de..0000000 Binary files a/docs/VUI-docs/build/doctrees/main.doctree and /dev/null differ diff --git a/docs/VUI-docs/build/doctrees/persist_task.doctree b/docs/VUI-docs/build/doctrees/persist_task.doctree deleted file mode 100644 index 9f6ff13..0000000 Binary files a/docs/VUI-docs/build/doctrees/persist_task.doctree and /dev/null differ diff --git a/docs/VUI-docs/build/doctrees/stack_viewer.doctree b/docs/VUI-docs/build/doctrees/stack_viewer.doctree deleted file mode 100644 index c1b5fba..0000000 Binary files a/docs/VUI-docs/build/doctrees/stack_viewer.doctree and /dev/null differ diff --git a/docs/VUI-docs/build/doctrees/text_viewer.doctree b/docs/VUI-docs/build/doctrees/text_viewer.doctree deleted file mode 100644 index 23d65e0..0000000 Binary files a/docs/VUI-docs/build/doctrees/text_viewer.doctree and /dev/null differ diff --git a/docs/VUI-docs/build/doctrees/viewer.doctree b/docs/VUI-docs/build/doctrees/viewer.doctree deleted file mode 100644 index cdd8f21..0000000 Binary files a/docs/VUI-docs/build/doctrees/viewer.doctree and /dev/null differ diff --git a/docs/VUI-docs/build/html/.buildinfo b/docs/VUI-docs/build/html/.buildinfo deleted file mode 100644 index 9ae7c5c..0000000 --- a/docs/VUI-docs/build/html/.buildinfo +++ /dev/null @@ -1,4 +0,0 @@ -# Sphinx build info version 1 -# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: 045f1325f6d2a1aed4dff11fe7e98c72 -tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/VUI-docs/build/html/_images/Joy-VUI-screenshot.PNG b/docs/VUI-docs/build/html/_images/Joy-VUI-screenshot.PNG deleted file mode 100644 index 53f7bf1..0000000 Binary files a/docs/VUI-docs/build/html/_images/Joy-VUI-screenshot.PNG and /dev/null differ diff --git a/docs/VUI-docs/build/html/_images/packages_Vui.png b/docs/VUI-docs/build/html/_images/packages_Vui.png deleted file mode 100644 index a1e8936..0000000 Binary files a/docs/VUI-docs/build/html/_images/packages_Vui.png and /dev/null differ diff --git a/docs/VUI-docs/build/html/_modules/index.html b/docs/VUI-docs/build/html/_modules/index.html deleted file mode 100644 index 20e40d1..0000000 --- a/docs/VUI-docs/build/html/_modules/index.html +++ /dev/null @@ -1,105 +0,0 @@ - - - - -
- - -
-# -*- coding: utf-8 -*-
-#
-# Copyright © 2019 Simon Forman
-#
-# This file is part of Thun
-#
-# Thun 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 3 of the License, or
-# (at your option) any later version.
-#
-# Thun 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.
-#
-# You should have received a copy of the GNU General Public License
-# along with Thun. If not see <http://www.gnu.org/licenses/>.
-#
-'''
-
-Core
-=====================
-
-The core module defines a bunch of system-wide "constants" (some colors
-and PyGame event groups), the message classes for Oberon-style message
-passing, a "world" class that holds the main context for the system, and
-a mainloop class that manages the, uh, main loop (the PyGame event queue.)
-
-'''
-from sys import stderr
-from traceback import format_exc
-import pygame
-from joy.joy import run
-from joy.utils.stack import stack_to_string
-
-
-COMMITTER = 'Joy <auto-commit@example.com>'
-
-
-BLACK = FOREGROUND = 0, 0, 0
-GREY = 127, 127, 127
-WHITE = BACKGROUND = 255, 255, 255
-BLUE = 100, 100, 255
-GREEN = 70, 200, 70
-
-
-MOUSE_EVENTS = frozenset({
- pygame.MOUSEMOTION,
- pygame.MOUSEBUTTONDOWN,
- pygame.MOUSEBUTTONUP
- })
-'PyGame mouse events.'
-
-ARROW_KEYS = frozenset({
- pygame.K_UP,
- pygame.K_DOWN,
- pygame.K_LEFT,
- pygame.K_RIGHT
- })
-'PyGame arrow key events.'
-
-
-TASK_EVENTS = tuple(range(pygame.USEREVENT, pygame.NUMEVENTS))
-'Keep track of all possible task events.'
-
-AVAILABLE_TASK_EVENTS = set(TASK_EVENTS)
-'Task IDs that have not been assigned to a task.'
-
-ALLOWED_EVENTS = [pygame.QUIT, pygame.KEYUP, pygame.KEYDOWN]
-ALLOWED_EVENTS.extend(MOUSE_EVENTS)
-ALLOWED_EVENTS.extend(TASK_EVENTS)
-'Event "mask" for PyGame event queue, we are only interested in these event types.'
-
-
-ERROR = -1
-PENDING = 0
-SUCCESS = 1
-# 'Message status codes... dunno if this is a good idea or not...
-
-
-[docs]class Message(object):
- '''Message base class. Contains ``sender`` field.'''
- def __init__(self, sender):
- self.sender = sender
-
-
-[docs]class CommandMessage(Message):
- '''For commands, adds ``command`` field.'''
- def __init__(self, sender, command):
- Message.__init__(self, sender)
- self.command = command
-
-
-[docs]class ModifyMessage(Message):
- '''
- For when resources are modified, adds ``subject`` and ``details``
- fields.
- '''
- def __init__(self, sender, subject, **details):
- Message.__init__(self, sender)
- self.subject = subject
- self.details = details
-
-
-[docs]class OpenMessage(Message):
- '''
- For when resources are modified, adds ``name``, content_id``,
- ``status``, and ``traceback`` fields.
- '''
- def __init__(self, sender, name):
- Message.__init__(self, sender)
- self.name = name
- self.content_id = self.thing = None
- self.status = PENDING
- self.traceback = None
-
-
-[docs]class PersistMessage(Message):
- '''
- For when resources are modified, adds ``content_id`` and ``details``
- fields.
- '''
- def __init__(self, sender, content_id, **details):
- Message.__init__(self, sender)
- self.content_id = content_id
- self.details = details
-
-
-
-
-
-# Joy Interpreter & Context
-
-
-[docs]class World(object):
- '''
- This object contains the system context, the stack, dictionary, a
- reference to the display broadcast method, and the log.
- '''
-
- def __init__(self, stack_id, stack_holder, dictionary, notify, log):
- self.stack_holder = stack_holder
- self.dictionary = dictionary
- self.notify = notify
- self.stack_id = stack_id
- self.log = log.lines
- self.log_id = log.content_id
-
-[docs] def handle(self, message):
- '''
- Deal with updates to the stack and commands.
- '''
- if (isinstance(message, ModifyMessage)
- and message.subject is self.stack_holder
- ):
- self._log_lines('', '%s <-' % self.format_stack())
- if not isinstance(message, CommandMessage):
- return
- c, s, d = message.command, self.stack_holder[0], self.dictionary
- self._log_lines('', '-> %s' % (c,))
- self.stack_holder[0], _, self.dictionary = run(c, s, d)
- mm = ModifyMessage(self, self.stack_holder, content_id=self.stack_id)
- self.notify(mm)
-
- def _log_lines(self, *lines):
- self.log.extend(lines)
- self.notify(ModifyMessage(self, self.log, content_id=self.log_id))
-
- def format_stack(self):
- try:
- return stack_to_string(self.stack_holder[0])
- except:
- print >> stderr, format_exc()
- return str(self.stack_holder[0])
-
-
-[docs]def push(sender, item, notify, stack_name='stack.pickle'):
- '''
- Helper function to push an item onto the system stack with message.
- '''
- om = OpenMessage(sender, stack_name)
- notify(om)
- if om.status == SUCCESS:
- om.thing[0] = item, om.thing[0]
- notify(ModifyMessage(sender, om.thing, content_id=om.content_id))
- return om.status
-
-
-[docs]def open_viewer_on_string(sender, content, notify):
- '''
- Helper function to open a text viewer on a string.
- Typically used to show tracebacks.
- '''
- push(sender, content, notify)
- notify(CommandMessage(sender, 'good_viewer_location open_viewer'))
-
-
-# main loop
-
-
-[docs]class TheLoop(object):
- '''
- The main loop manages tasks and the PyGame event queue
- and framerate clock.
- '''
-
- FRAME_RATE = 24
-
- def __init__(self, display, clock):
- self.display = display
- self.clock = clock
- self.tasks = {}
- self.running = False
-
-[docs] def install_task(self, F, milliseconds):
- '''
- Install a task to run every so many milliseconds.
- '''
- try:
- task_event_id = AVAILABLE_TASK_EVENTS.pop()
- except KeyError:
- raise RuntimeError('out of task ids')
- self.tasks[task_event_id] = F
- pygame.time.set_timer(task_event_id, milliseconds)
- return task_event_id
-
-[docs] def remove_task(self, task_event_id):
- '''
- Remove an installed task.
- '''
- assert task_event_id in self.tasks, repr(task_event_id)
- pygame.time.set_timer(task_event_id, 0)
- del self.tasks[task_event_id]
- AVAILABLE_TASK_EVENTS.add(task_event_id)
-
- def __del__(self):
- # Best effort to cancel all running tasks.
- for task_event_id in self.tasks:
- pygame.time.set_timer(task_event_id, 0)
-
-[docs] def run_task(self, task_event_id):
- '''
- Give a task its time to shine.
- '''
- task = self.tasks[task_event_id]
- try:
- task()
- except:
- traceback = format_exc()
- self.remove_task(task_event_id)
- print >> stderr, traceback
- print >> stderr, 'TASK removed due to ERROR', task
- open_viewer_on_string(self, traceback, self.display.broadcast)
-
-[docs] def loop(self):
- '''
- The actual main loop machinery.
-
- Maintain a ``running`` flag, pump the PyGame event queue and
- handle the events (dispatching to the display), tick the clock.
-
- When the loop is exited (by clicking the window close button or
- pressing the ``escape`` key) it broadcasts a ``ShutdownMessage``.
- '''
- self.running = True
- while self.running:
- for event in pygame.event.get():
- if event.type == pygame.QUIT:
- self.running = False
- elif event.type == pygame.KEYUP and event.key == pygame.K_ESCAPE:
- self.running = False
- elif event.type in self.tasks:
- self.run_task(event.type)
- else:
- self.display.dispatch_event(event)
- pygame.display.update()
- self.clock.tick(self.FRAME_RATE)
- self.display.broadcast(ShutdownMessage(self))
-
-# -*- coding: utf-8 -*-
-#
-# Copyright © 2019 Simon Forman
-#
-# This file is part of Thun
-#
-# Thun 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 3 of the License, or
-# (at your option) any later version.
-#
-# Thun 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.
-#
-# You should have received a copy of the GNU General Public License
-# along with Thun. If not see <http://www.gnu.org/licenses/>.
-#
-'''
-
-Display
-=================
-
-This module implements a simple visual display system modeled on Oberon.
-
-Refer to Chapter 4 of the Project Oberon book for more information.
-
-There is a Display object that manages a pygame surface and N vertical
-tracks each of which manages zero or more viewers.
-'''
-from copy import copy
-from sys import stderr
-from traceback import format_exc
-import pygame
-from .core import (
- open_viewer_on_string,
- GREY,
- MOUSE_EVENTS,
- )
-from .viewer import Viewer
-from joy.vui import text_viewer
-
-
-[docs]class Display(object):
- '''
- Manage tracks and viewers on a screen (Pygame surface.)
-
- The size and number of tracks are defined by passing in at least two
- ratios, e.g. Display(screen, 1, 4, 4) would create three tracks, one
- small one on the left and two larger ones of the same size, each four
- times wider than the left one.
-
- All tracks take up the whole height of the display screen. Tracks
- manage zero or more Viewers. When you "grow" a viewer a new track is
- created that overlays or hides one or two existing tracks, and when
- the last viewer in an overlay track is closed the track closes too
- and reveals the hidden tracks (and their viewers, if any.)
-
- In order to facilitate command underlining while mouse dragging the
- lookup parameter must be a function that accepts a string and returns
- a Boolean indicating whether that string is a valid Joy function name.
- Typically you pass in the __contains__ method of the Joy dict. This
- is a case of breaking "loose coupling" to gain efficiency, as otherwise
- we would have to e.g. send some sort of lookup message to the
- World context object, going through the whole Display.broadcast()
- machinery, etc. Not something you want to do on each MOUSEMOTION
- event.
- '''
-
- def __init__(self, screen, lookup, *track_ratios):
- self.screen = screen
- self.w, self.h = screen.get_width(), screen.get_height()
- self.lookup = lookup
- self.focused_viewer = None
- self.tracks = [] # (x, track)
- self.handlers = [] # Non-viewers that should receive messages.
- # Create the tracks.
- if not track_ratios: track_ratios = 1, 4
- x, total = 0, sum(track_ratios)
- for ratio in track_ratios[:-1]:
- track_width = self.w * ratio / total
- assert track_width >= 10 # minimum width 10 pixels
- self._open_track(x, track_width)
- x += track_width
- self._open_track(x, self.w - x)
-
- def _open_track(self, x, w):
- '''Helper function to create the pygame surface and Track.'''
- track_surface = self.screen.subsurface((x, 0, w, self.h))
- self.tracks.append((x, Track(track_surface)))
-
-[docs] def open_viewer(self, x, y, class_):
- '''
- Open a viewer of class_ at the x, y location on the display,
- return the viewer.
- '''
- track = self._track_at(x)[0]
- V = track.open_viewer(y, class_)
- V.focus(self)
- return V
-
-[docs] def close_viewer(self, viewer):
- '''Close the viewer.'''
- for x, track in self.tracks:
- if track.close_viewer(viewer):
- if not track.viewers and track.hiding:
- i = self.tracks.index((x, track))
- self.tracks[i:i + 1] = track.hiding
- assert sorted(self.tracks) == self.tracks
- for _, exposed_track in track.hiding:
- exposed_track.redraw()
- if viewer is self.focused_viewer:
- self.focused_viewer = None
- break
-
-[docs] def change_viewer(self, viewer, y, relative=False):
- '''
- Adjust the top of the viewer to a new y within the boundaries of
- its neighbors.
-
- If relative is False new_y should be in screen coords, else new_y
- should be relative to the top of the viewer.
- '''
- for _, track in self.tracks:
- if track.change_viewer(viewer, y, relative):
- break
-
-[docs] def grow_viewer(self, viewer):
- '''
- Cause the viewer to take up its whole track or, if it does
- already, take up another track, up to the whole screen.
-
- This is the inverse of closing a viewer. "Growing" a viewer
- actually creates a new copy and a new track to hold it. The old
- tracks and viewers are retained, and they get restored when the
- covering track closes, which happens automatically when the last
- viewer in the covering track is closed.
- '''
- for x, track in self.tracks:
- for _, V in track.viewers:
- if V is viewer:
- return self._grow_viewer(x, track, viewer)
-
- def _grow_viewer(self, x, track, viewer):
- '''Helper function to "grow" a viewer.'''
- new_viewer = None
-
- if viewer.h < self.h:
- # replace the track with a new track that contains
- # a copy of the viewer at full height.
- new_track = Track(track.surface) # Reuse it, why not?
- new_viewer = copy(viewer)
- new_track._grow_by(new_viewer, 0, self.h - viewer.h)
- new_track.viewers.append((0, new_viewer))
- new_track.hiding = [(x, track)]
- self.tracks[self.tracks.index((x, track))] = x, new_track
-
- elif viewer.w < self.w:
- # replace two tracks
- i = self.tracks.index((x, track))
- try: # prefer the one on the right
- xx, xtrack = self.tracks[i + 1]
- except IndexError:
- i -= 1 # okay, the one on the left
- xx, xtrack = self.tracks[i]
- hiding = [(xx, xtrack), (x, track)]
- else:
- hiding = [(x, track), (xx, xtrack)]
- # We know there has to be at least one other track because it
- # there weren't then that implies that the one track takes up
- # the whole display screen (the only way you can get just one
- # track is by growing a viewer to cover the whole screen.)
- # Ergo, viewer.w == self.w, so this branch doesn't run.
- new_x = min(x, xx)
- new_w = track.w + xtrack.w
- r = new_x, 0, new_w, self.h
- new_track = Track(self.screen.subsurface(r))
- new_viewer = copy(viewer)
- r = 0, 0, new_w, self.h
- new_viewer.resurface(new_track.surface.subsurface(r))
- new_track.viewers.append((0, new_viewer))
- new_track.hiding = hiding
- self.tracks[i:i + 2] = [(new_x, new_track)]
- new_viewer.draw()
-
- return new_viewer
-
- def _move_viewer(self, to, rel_y, viewer, _x, y):
- '''
- Helper function to move (really copy) a viewer to a new location.
- '''
- h = to.split(rel_y)
- new_viewer = copy(viewer)
- if not isinstance(to, Track):
- to = next(T for _, T in self.tracks
- for _, V in T.viewers
- if V is to)
- new_viewer.resurface(to.surface.subsurface((0, y, to.w, h)))
- to.viewers.append((y, new_viewer))
- to.viewers.sort() # bisect.insort() would be overkill here.
- new_viewer.draw()
- self.close_viewer(viewer)
-
- def _track_at(self, x):
- '''
- Return the track at x along with the track-relative x coordinate,
- raise ValueError if x is off-screen.
- '''
- for track_x, track in self.tracks:
- if x < track_x + track.w:
- return track, x - track_x
- raise ValueError('x outside display: %r' % (x,))
-
-[docs] def at(self, x, y):
- '''
- Return the viewer (which can be a Track) at the x, y location,
- along with the relative-to-viewer-surface x and y coordinates.
- If there is no viewer at the location the Track will be returned
- instead.
- '''
- track, x = self._track_at(x)
- viewer, y = track.viewer_at(y)
- return viewer, x, y
-
-[docs] def iter_viewers(self):
- '''
- Iterate through all viewers yielding (viewer, x, y) three-tuples.
- The x and y coordinates are screen pixels of the top-left corner
- of the viewer.
- '''
- for x, T in self.tracks:
- for y, V in T.viewers:
- yield V, x, y
-
-[docs] def done_resizing(self):
- '''
- Helper method called directly by ``MenuViewer.mouse_up()`` to (hackily)
- update the display when done resizing a viewer.
- '''
- for _, track in self.tracks: # This should be done by a Message?
- if track.resizing_viewer:
- track.resizing_viewer.draw()
- track.resizing_viewer = None
- break
-
-[docs] def broadcast(self, message):
- '''
- Broadcast a message to all viewers (except the sender) and all
- registered handlers.
- '''
- for _, track in self.tracks:
- track.broadcast(message)
- for handler in self.handlers:
- handler(message)
-
-[docs] def redraw(self):
- '''
- Redraw all tracks (which will redraw all viewers.)
- '''
- for _, track in self.tracks:
- track.redraw()
-
-[docs] def focus(self, viewer):
- '''
- Set system focus to a given viewer (or no viewer if a track.)
- '''
- if isinstance(viewer, Track):
- if self.focused_viewer: self.focused_viewer.unfocus()
- self.focused_viewer = None
- elif viewer is not self.focused_viewer:
- if self.focused_viewer: self.focused_viewer.unfocus()
- self.focused_viewer = viewer
- viewer.focus(self)
-
-[docs] def dispatch_event(self, event):
- '''
- Display event handling.
- '''
- try:
- if event.type in {pygame.KEYUP, pygame.KEYDOWN}:
- self._keyboard_event(event)
- elif event.type in MOUSE_EVENTS:
- self._mouse_event(event)
- else:
- print >> stderr, (
- 'received event %s Use pygame.event.set_allowed().'
- % pygame.event.event_name(event.type)
- )
- # Catch all exceptions and open a viewer.
- except:
- err = format_exc()
- print >> stderr, err # To be safe just print it right away.
- open_viewer_on_string(self, err, self.broadcast)
-
- def _keyboard_event(self, event):
- if event.key == pygame.K_PAUSE and event.type == pygame.KEYUP:
- # At least on my keyboard the break/pause key sends K_PAUSE.
- # The main use of this is to open a TextViewer if you
- # accidentally close all the viewers, so you can recover.
- raise KeyboardInterrupt('break')
- if not self.focused_viewer:
- return
- if event.type == pygame.KEYUP:
- self.focused_viewer.key_up(self, event.key, event.mod)
- elif event.type == pygame.KEYDOWN:
- self.focused_viewer.key_down(
- self, event.unicode, event.key, event.mod)
-
- def _mouse_event(self, event):
- V, x, y = self.at(*event.pos)
-
- if event.type == pygame.MOUSEMOTION:
- if not isinstance(V, Track):
- V.mouse_motion(self, x, y, *(event.rel + event.buttons))
-
- elif event.type == pygame.MOUSEBUTTONDOWN:
- if event.button == 1:
- self.focus(V)
- V.mouse_down(self, x, y, event.button)
-
- else:
- assert event.type == pygame.MOUSEBUTTONUP
-
- # Check for moving viewer.
- if (event.button == 2
- and self.focused_viewer
- and V is not self.focused_viewer
- and V.MINIMUM_HEIGHT < y < V.h - self.focused_viewer.MINIMUM_HEIGHT
- ):
- self._move_viewer(V, y, self.focused_viewer, *event.pos)
-
- else:
- V.mouse_up(self, x, y, event.button)
-
-[docs] def init_text(self, pt, x, y, filename):
- '''
- Open and return a ``TextViewer`` on a given file (which must be present
- in the ``JOYHOME`` directory.)
- '''
- viewer = self.open_viewer(x, y, text_viewer.TextViewer)
- viewer.content_id, viewer.lines = pt.open(filename)
- viewer.draw()
- return viewer
-
-
-[docs]class Track(Viewer):
- '''
- Manage a vertical strip of the display, and the viewers on it.
- '''
-
- def __init__(self, surface):
- Viewer.__init__(self, surface)
- self.viewers = [] # (y, viewer)
- self.hiding = None
- self.resizing_viewer = None
- self.draw()
-
-[docs] def split(self, y):
- '''
- Split the Track at the y coordinate and return the height
- available for a new viewer. Tracks manage a vertical strip of
- the display screen so they don't resize their surface when split.
- '''
- h = self.viewers[0][0] if self.viewers else self.h
- assert h > y
- return h - y
-
-[docs] def draw(self, rect=None):
- '''Draw the track onto its surface, clearing all content.
-
- If rect is passed only draw to that area. This supports e.g.
- closing a viewer that then exposes part of the track.
- '''
- self.surface.fill(GREY, rect=rect)
-
-[docs] def viewer_at(self, y):
- '''
- Return the viewer at y along with the viewer-relative y coordinate,
- if there's no viewer at y return this track and y.
- '''
- for viewer_y, viewer in self.viewers:
- if viewer_y < y <= viewer_y + viewer.h:
- return viewer, y - viewer_y
- return self, y
-
-[docs] def open_viewer(self, y, class_):
- '''Open and return a viewer of class at y.'''
- # Todo: if y coincides with some other viewer's y replace it.
- viewer, viewer_y = self.viewer_at(y)
- h = viewer.split(viewer_y)
- new_viewer = class_(self.surface.subsurface((0, y, self.w, h)))
- new_viewer.draw()
- self.viewers.append((y, new_viewer))
- self.viewers.sort() # Could use bisect module but how many
- # viewers will you ever have?
- return new_viewer
-
-[docs] def close_viewer(self, viewer):
- '''Close the viewer, reuse the freed space.'''
- for y, V in self.viewers:
- if V is viewer:
- self._close_viewer(y, V)
- return True
- return False
-
- def _close_viewer(self, y, viewer):
- '''Helper function to do the actual closing.'''
- i = self.viewers.index((y, viewer))
- del self.viewers[i]
- if i: # The previous viewer gets the space.
- previous_y, previous_viewer = self.viewers[i - 1]
- self._grow_by(previous_viewer, previous_y, viewer.h)
- else: # This track gets the space.
- self.draw((0, y, self.w, viewer.surface.get_height()))
- viewer.close()
-
- def _grow_by(self, viewer, y, h):
- '''Grow a viewer (located at y) by height h.
-
- This might seem like it should be a method of the viewer, but
- the viewer knows nothing of its own y location on the screen nor
- the parent track's surface (to make a new subsurface) so it has
- to be a method of the track, which has both.
- '''
- h = viewer.surface.get_height() + h
- try:
- surface = self.surface.subsurface((0, y, self.w, h))
- except ValueError: # subsurface rectangle outside surface area
- pass
- else:
- viewer.resurface(surface)
- if h <= viewer.last_touch[1]: viewer.last_touch = 0, 0
- viewer.draw()
-
-[docs] def change_viewer(self, viewer, new_y, relative=False):
- '''
- Adjust the top of the viewer to a new y within the boundaries of
- its neighbors.
-
- If relative is False new_y should be in screen coords, else new_y
- should be relative to the top of the viewer.
- '''
- for old_y, V in self.viewers:
- if V is viewer:
- if relative: new_y += old_y
- if new_y != old_y: self._change_viewer(new_y, old_y, V)
- return True
- return False
-
- def _change_viewer(self, new_y, old_y, viewer):
- new_y = max(0, min(self.h, new_y))
- i = self.viewers.index((old_y, viewer))
- if new_y < old_y: # Enlarge self, shrink upper neighbor.
- if i:
- previous_y, previous_viewer = self.viewers[i - 1]
- if new_y - previous_y < self.MINIMUM_HEIGHT:
- return
- previous_viewer.resizing = 1
- h = previous_viewer.split(new_y - previous_y)
- previous_viewer.resizing = 0
- self.resizing_viewer = previous_viewer
- else:
- h = old_y - new_y
- self._grow_by(viewer, new_y, h)
-
- else: # Shink self, enlarge upper neighbor.
- # Enforce invariant.
- try:
- h, _ = self.viewers[i + 1]
- except IndexError: # No next viewer.
- h = self.h
- if h - new_y < self.MINIMUM_HEIGHT:
- return
-
- # Change the viewer and adjust the upper viewer or track.
- h = new_y - old_y
- self._grow_by(viewer, new_y, -h) # grow by negative height!
- if i:
- previous_y, previous_viewer = self.viewers[i - 1]
- previous_viewer.resizing = 1
- self._grow_by(previous_viewer, previous_y, h)
- previous_viewer.resizing = 0
- self.resizing_viewer = previous_viewer
- else:
- self.draw((0, old_y, self.w, h))
-
- self.viewers[i] = new_y, viewer
- # self.viewers.sort() # Not necessary, invariant holds.
- assert sorted(self.viewers) == self.viewers
-
-[docs] def broadcast(self, message):
- '''
- Broadcast a message to all viewers on this track (except the sender.)
- '''
- for _, viewer in self.viewers:
- if viewer is not message.sender:
- viewer.handle(message)
-
-[docs] def redraw(self):
- '''Redraw the track and all of its viewers.'''
- self.draw()
- for _, viewer in self.viewers:
- viewer.draw()
-
-# -*- coding: utf-8 -*-
-#
-# Copyright © 2019 Simon Forman
-#
-# This file is part of Thun
-#
-# Thun 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 3 of the License, or
-# (at your option) any later version.
-#
-# Thun 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.
-#
-# You should have received a copy of the GNU General Public License
-# along with Thun. If not see <http://www.gnu.org/licenses/>.
-#
-'''
-
-Main Module
-======================================
-
-Pulls everything together.
-
-'''
-import os, sys, traceback
-import pygame
-from joy.library import initialize, DefinitionWrapper, SimpleFunctionWrapper
-from joy.vui import core, display, persist_task
-
-
-FULLSCREEN = '-f' in sys.argv
-
-
-JOY_HOME = os.environ.get('JOY_HOME')
-if JOY_HOME is None:
- JOY_HOME = os.path.expanduser('~/.thun')
- if not os.path.isabs(JOY_HOME):
- raise ValueError('what directory?')
-
-
-[docs]def load_definitions(pt, dictionary):
- '''Load definitions from ``definitions.txt``.'''
- lines = pt.open('definitions.txt')[1]
- for line in lines:
- if '==' in line:
- DefinitionWrapper.add_def(line, dictionary)
-
-
-[docs]def load_primitives(home, name_space):
- '''Load primitives from ``library.py``.'''
- fn = os.path.join(home, 'library.py')
- if os.path.exists(fn):
- execfile(fn, name_space)
-
-
-[docs]def init():
- '''
- Initialize the system.
-
- * Init PyGame
- * Create main window
- * Start the PyGame clock
- * Set the event mask
- * Create the PersistTask
-
- '''
- print 'Initializing Pygame...'
- pygame.init()
- print 'Creating window...'
- if FULLSCREEN:
- screen = pygame.display.set_mode()
- else:
- screen = pygame.display.set_mode((1024, 768))
- clock = pygame.time.Clock()
- pygame.event.set_allowed(None)
- pygame.event.set_allowed(core.ALLOWED_EVENTS)
- pt = persist_task.PersistTask(JOY_HOME)
- return screen, clock, pt
-
-
-[docs]def init_context(screen, clock, pt):
- '''
- More initialization
-
- * Create the Joy dictionary
- * Create the Display
- * Open the log, menu, and scratch text viewers, and the stack pickle
- * Start the main loop
- * Create the World object
- * Register PersistTask and World message handlers with the Display
- * Load user function definitions.
-
- '''
- D = initialize()
- d = display.Display(
- screen,
- D.__contains__,
- *((144 - 89, 144, 89) if FULLSCREEN else (89, 144))
- )
- log = d.init_text(pt, 0, 0, 'log.txt')
- tho = d.init_text(pt, 0, d.h / 3, 'menu.txt')
- t = d.init_text(pt, d.w / 2, 0, 'scratch.txt')
- loop = core.TheLoop(d, clock)
- stack_id, stack_holder = pt.open('stack.pickle')
- world = core.World(stack_id, stack_holder, D, d.broadcast, log)
- loop.install_task(pt.task_run, 10000) # save files every ten seconds
- d.handlers.append(pt.handle)
- d.handlers.append(world.handle)
- load_definitions(pt, D)
- return locals()
-
-
-[docs]def error_guard(loop, n=10):
- '''
- Run a loop function, retry for ``n`` exceptions.
- Prints tracebacks on ``sys.stderr``.
- '''
- error_count = 0
- while error_count < n:
- try:
- loop()
- break
- except:
- traceback.print_exc(file=sys.stderr)
- error_count += 1
-
-
-[docs]class FileFaker(object):
- '''Pretends to be a file object but writes to log instead.'''
-
- def __init__(self, log):
- self.log = log
-
-
-
- def flush(self):
- pass
-
-
-[docs]def main(screen, clock, pt):
- '''
- Main function.
-
- * Call ``init_context()``
- * Load primitives
- * Create an ``evaluate`` function that lets you just eval some Python code
- * Redirect ``stdout`` to the log using a ``FileFaker`` object, and...
- * Start the main loop.
- '''
- name_space = init_context(screen, clock, pt)
- load_primitives(pt.home, name_space.copy())
-
- @SimpleFunctionWrapper
- def evaluate(stack):
- '''Evaluate the Python code text on the top of the stack.'''
- code, stack = stack
- exec code in name_space.copy()
- return stack
-
- name_space['D']['evaluate'] = evaluate
-
-
- sys.stdout, old_stdout = FileFaker(name_space['log']), sys.stdout
- try:
- error_guard(name_space['loop'].loop)
- finally:
- sys.stdout = old_stdout
-
- return name_space['d']
-
-# -*- coding: utf-8 -*-
-#
-# Copyright © 2019 Simon Forman
-#
-# This file is part of Thun
-#
-# Thun 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 3 of the License, or
-# (at your option) any later version.
-#
-# Thun 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.
-#
-# You should have received a copy of the GNU General Public License
-# along with Thun. If not see <http://www.gnu.org/licenses/>.
-#
-'''
-
-Persist Task
-===========================
-
-This module deals with persisting the "resources" (text files and the
-stack) to the git repo in the ``JOY_HOME`` directory.
-
-'''
-import os, pickle, traceback
-from collections import Counter
-from dulwich.errors import NotGitRepository
-from dulwich.repo import Repo
-from joy.vui import core, init_joy_home
-
-
-[docs]def open_repo(repo_dir=None, initialize=False):
- '''
- Open, or create, and return a Dulwich git repo object for the given
- directory. If the dir path doesn't exist it will be created. If it
- does exist but isn't a repo the result depends on the ``initialize``
- argument. If it is ``False`` (the default) a ``NotGitRepository``
- exception is raised, otherwise ``git init`` is effected in the dir.
- '''
- if not os.path.exists(repo_dir):
- os.makedirs(repo_dir, 0700)
- return init_repo(repo_dir)
- try:
- return Repo(repo_dir)
- except NotGitRepository:
- if initialize:
- return init_repo(repo_dir)
- raise
-
-
-[docs]def init_repo(repo_dir):
- '''
- Initialize a git repository in the directory. Stage and commit all
- files (toplevel, not those in subdirectories if any) in the dir.
- '''
- repo = Repo.init(repo_dir)
- init_joy_home.initialize(repo_dir)
- repo.stage([
- fn
- for fn in os.listdir(repo_dir)
- if os.path.isfile(os.path.join(repo_dir, fn))
- ])
- repo.do_commit('Initial commit.', committer=core.COMMITTER)
- return repo
-
-
-[docs]def make_repo_relative_path_maker(repo):
- '''
- Helper function to return a function that returns a path given a path,
- that's relative to the repository.
- '''
- c = repo.controldir()
- def repo_relative_path(path):
- return os.path.relpath(path, os.path.commonprefix((c, path)))
- return repo_relative_path
-
-
-[docs]class Resource(object):
- '''
- Handle the content of a text files as a list of lines, deal with
- saving it and staging the changes to a repo.
- '''
-
- def __init__(self, filename, repo_relative_filename, thing=None):
- self.filename = filename
- self.repo_relative_filename = repo_relative_filename
- self.thing = thing or self._from_file(open(filename))
-
- def _from_file(self, f):
- return f.read().splitlines()
-
- def _to_file(self, f):
- for line in self.thing:
- print >> f, line
-
-[docs] def persist(self, repo):
- '''
- Save the lines to the file and stage the file in the repo.
- '''
- with open(self.filename, 'w') as f:
- os.chmod(self.filename, 0600)
- self._to_file(f)
- f.flush()
- os.fsync(f.fileno())
- # For goodness's sake, write it to the disk already!
- repo.stage([self.repo_relative_filename])
-
-
-[docs]class PickledResource(Resource):
- '''
- A ``Resource`` subclass that uses ``pickle`` on its file/thing.
- '''
-
- def _from_file(self, f):
- return [pickle.load(f)]
-
- def _to_file(self, f):
- pickle.dump(self.thing[0], f)
-
-
-[docs]class PersistTask(object):
- '''
- This class deals with saving changes to the git repo.
- '''
-
- LIMIT = 10
- MAX_SAVE = 10
-
- def __init__(self, home):
- self.home = home
- self.repo = open_repo(home)
- self._r = make_repo_relative_path_maker(self.repo)
- self.counter = Counter()
- self.store = {}
-
-[docs] def open(self, name):
- '''
- Look up the named file in home and return its content_id and data.
- '''
- fn = os.path.join(self.home, name)
- content_id = name # hash(fn)
- try:
- resource = self.store[content_id]
- except KeyError:
- R = PickledResource if name.endswith('.pickle') else Resource
- resource = self.store[content_id] = R(fn, self._r(fn))
- return content_id, resource.thing
-
-[docs] def handle(self, message):
- '''
- Handle messages, dispatch to ``handle_FOO()`` methods.
- '''
- if isinstance(message, core.OpenMessage):
- self.handle_open(message)
- elif isinstance(message, core.ModifyMessage):
- self.handle_modify(message)
- elif isinstance(message, core.PersistMessage):
- self.handle_persist(message)
- elif isinstance(message, core.ShutdownMessage):
- for content_id in self.counter:
- self.store[content_id].persist(self.repo)
- self.commit('shutdown')
-
-[docs] def handle_open(self, message):
- '''
- Foo.
- '''
- try:
- message.content_id, message.thing = self.open(message.name)
- except:
- message.traceback = traceback.format_exc()
- message.status = core.ERROR
- else:
- message.status = core.SUCCESS
-
-[docs] def handle_modify(self, message):
- '''
- Foo.
- '''
- try:
- content_id = message.details['content_id']
- except KeyError:
- return
- if not content_id:
- return
- self.counter[content_id] += 1
- if self.counter[content_id] > self.LIMIT:
- self.persist(content_id)
- self.commit('due to activity')
-
-[docs] def handle_persist(self, message):
- '''
- Foo.
- '''
- try:
- resource = self.store[message.content_id]
- except KeyError:
- resource = self.handle_persist_new(message)
- resource.persist(self.repo)
- self.commit('by request from %r' % (message.sender,))
-
-[docs] def handle_persist_new(self, message):
- '''
- Foo.
- '''
- name = message.content_id
- check_filename(name)
- fn = os.path.join(self.home, name)
- thing = message.details['thing']
- R = PickledResource if name.endswith('.pickle') else Resource # !!! refactor!
- resource = self.store[name] = R(fn, self._r(fn), thing)
- return resource
-
-[docs] def persist(self, content_id):
- '''
- Persist a resource.
- '''
- del self.counter[content_id]
- self.store[content_id].persist(self.repo)
-
-[docs] def task_run(self):
- '''
- Stage any outstanding changes.
- '''
- if not self.counter:
- return
- for content_id, _ in self.counter.most_common(self.MAX_SAVE):
- self.persist(content_id)
- self.commit()
-
-[docs] def commit(self, message='auto-commit'):
- '''
- Commit.
- '''
- return self.repo.do_commit(message, committer=core.COMMITTER)
-
-[docs] def scan(self):
- '''
- Return a sorted list of all the files in the home dir.
- '''
- return sorted([
- fn
- for fn in os.listdir(self.home)
- if os.path.isfile(os.path.join(self.home, fn))
- ])
-
-
-[docs]def check_filename(name):
- '''
- Sanity checks for filename.
- '''
- # TODO: improve this...
- if len(name) > 64:
- raise ValueError('bad name %r' % (name,))
- left, dot, right = name.partition('.')
- if not left.isalnum() or dot and not right.isalnum():
- raise ValueError('bad name %r' % (name,))
-
-
-if __name__ == '__main__':
- JOY_HOME = os.path.expanduser('~/.thun')
- pt = PersistTask(JOY_HOME)
- content_id, thing = pt.open('stack.pickle')
- pt.persist(content_id)
- print pt.counter
- mm = core.ModifyMessage(None, None, content_id=content_id)
- pt.handle(mm)
- print pt.counter
-
-# -*- coding: utf-8 -*-
-#
-# Copyright © 2019 Simon Forman
-#
-# This file is part of Thun
-#
-# Thun 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 3 of the License, or
-# (at your option) any later version.
-#
-# Thun 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.
-#
-# You should have received a copy of the GNU General Public License
-# along with Thun. If not see <http://www.gnu.org/licenses/>.
-#
-'''
-
-Stack Viewer
-=================
-
-'''
-from joy.utils.stack import expression_to_string, iter_stack
-from joy.vui import core, text_viewer
-
-
-MAX_WIDTH = 64
-
-
-[docs]def fsi(item):
- '''Format Stack Item'''
- if isinstance(item, tuple):
- res = '[%s]' % expression_to_string(item)
- elif isinstance(item, str):
- res = '"%s"' % item
- else:
- assert not isinstance(item, unicode), repr(item)
- res = str(item)
- if len(res) > MAX_WIDTH:
- return res[:MAX_WIDTH - 3] + '...'
- return res
-
-
-[docs]class StackViewer(text_viewer.TextViewer):
-
- def __init__(self, surface):
- super(StackViewer, self).__init__(surface)
- self.stack_holder = None
- self.content_id = 'stack viewer'
-
- def _attach(self, display):
- if self.stack_holder:
- return
- om = core.OpenMessage(self, 'stack.pickle')
- display.broadcast(om)
- if om.status != core.SUCCESS:
- raise RuntimeError('stack unavailable')
- self.stack_holder = om.thing
-
- def _update(self):
- self.lines[:] = map(fsi, iter_stack(self.stack_holder[0])) or ['']
-
- def focus(self, display):
- self._attach(display)
- super(StackViewer, self).focus(display)
-
- def handle(self, message):
- if (isinstance(message, core.ModifyMessage)
- and message.subject is self.stack_holder
- ):
- self._update()
- self.draw_body()
-
-# -*- coding: utf-8 -*-
-#
-# Copyright © 2019 Simon Forman
-#
-# This file is part of Thun
-#
-# Thun 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 3 of the License, or
-# (at your option) any later version.
-#
-# Thun 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.
-#
-# You should have received a copy of the GNU General Public License
-# along with Thun. If not see <http://www.gnu.org/licenses/>.
-#
-'''
-
-Text Viewer
-=================
-
-'''
-import string
-import pygame
-from joy.utils.stack import expression_to_string
-from joy.vui.core import (
- ARROW_KEYS,
- BACKGROUND as BG,
- FOREGROUND as FG,
- CommandMessage,
- ModifyMessage,
- OpenMessage,
- SUCCESS,
- push,
- )
-from joy.vui import viewer, font_data
-#reload(viewer)
-
-
-MenuViewer = viewer.MenuViewer
-
-
-SELECTION_COLOR = 235, 255, 0, 32
-SELECTION_KEYS = {
- pygame.K_F1,
- pygame.K_F2,
- pygame.K_F3,
- pygame.K_F4,
- }
-STACK_CHATTER_KEYS = {
- pygame.K_F5,
- pygame.K_F6,
- pygame.K_F7,
- pygame.K_F8,
- }
-
-
-def _is_command(display, word):
- return display.lookup(word) or word.isdigit() or all(
- not s or s.isdigit() for s in word.split('.', 1)
- ) and len(word) > 1
-
-
-def format_stack_item(content):
- if isinstance(content, tuple):
- return '[%s]' % expression_to_string(content)
- return str(content)
-
-
-class Font(object):
-
- IMAGE = pygame.image.load(font_data.data, 'Iosevka12.BMP')
- LOOKUP = (string.ascii_letters +
- string.digits +
- '''@#$&_~|`'"%^=-+*/\\<>[]{}(),.;:!?''')
-
- def __init__(self, char_w=8, char_h=19, line_h=19):
- self.char_w = char_w
- self.char_h = char_h
- self.line_h = line_h
-
- def size(self, text):
- return self.char_w * len(text), self.line_h
-
- def render(self, text):
- surface = pygame.Surface(self.size(text))
- surface.fill(BG)
- x = 0
- for ch in text:
- if not ch.isspace():
- try:
- i = self.LOOKUP.index(ch)
- except ValueError:
- # render a lil box...
- r = (x + 1, self.line_h / 2 - 3,
- self.char_w - 2, self.line_h / 2)
- pygame.draw.rect(surface, FG, r, 1)
- else:
- iy, ix = divmod(i, 26)
- ix *= self.char_w
- iy *= self.char_h
- area = ix, iy, self.char_w, self.char_h
- surface.blit(self.IMAGE, (x, 0), area)
- x += self.char_w
- return surface
-
- def __contains__(self, char):
- assert len(char) == 1, repr(char)
- return char in self.LOOKUP
-
-
-FONT = Font()
-
-
-[docs]class TextViewer(MenuViewer):
-
- MINIMUM_HEIGHT = FONT.line_h + 3
- CLOSE_TEXT = FONT.render('close')
- GROW_TEXT = FONT.render('grow')
-
- class Cursor(object):
-
- def __init__(self, viewer):
- self.v = viewer
- self.x = self.y = 0
- self.w, self.h = 2, FONT.line_h
- self.mem = pygame.Surface((self.w, self.h))
- self.can_fade = False
-
- def set_to(self, x, y):
- self.fade()
- self.x, self.y = x, y
- self.draw()
-
- def draw(self):
- r = self.x * FONT.char_w, self.screen_y(), self.w, self.h
- self.mem.blit(self.v.body_surface, (0, 0), r)
- self.v.body_surface.fill(FG, r)
- self.can_fade = True
-
- def fade(self):
- if self.can_fade:
- dest = self.x * FONT.char_w, self.screen_y()
- self.v.body_surface.blit(self.mem, dest)
- self.can_fade = False
-
- def screen_y(self, row=None):
- if row is None: row = self.y
- return (row - self.v.at_line) * FONT.line_h
-
- def up(self, _mod):
- if self.y:
- self.fade()
- self.y -= 1
- self.x = min(self.x, len(self.v.lines[self.y]))
- self.draw()
-
- def down(self, _mod):
- if self.y < len(self.v.lines) - 1:
- self.fade()
- self.y += 1
- self.x = min(self.x, len(self.v.lines[self.y]))
- self.draw()
- self._check_scroll()
-
- def left(self, _mod):
- if self.x:
- self.fade()
- self.x -= 1
- self.draw()
- elif self.y:
- self.fade()
- self.y -= 1
- self.x = len(self.v.lines[self.y])
- self.draw()
- self._check_scroll()
-
- def right(self, _mod):
- if self.x < len(self.v.lines[self.y]):
- self.fade()
- self.x += 1
- self.draw()
- elif self.y < len(self.v.lines) - 1:
- self.fade()
- self.y += 1
- self.x = 0
- self.draw()
- self._check_scroll()
-
- def _check_scroll(self):
- if self.y < self.v.at_line:
- self.v.scroll_down()
- elif self.y > self.v.at_line + self.v.h_in_lines:
- self.v.scroll_up()
-
- def __init__(self, surface):
- self.cursor = self.Cursor(self)
- MenuViewer.__init__(self, surface)
- self.lines = ['']
- self.content_id = None
- self.at_line = 0
- self.bg = BG
- self.command = self.command_rect = None
- self._sel_start = self._sel_end = None
-
- def resurface(self, surface):
- self.cursor.fade()
- MenuViewer.resurface(self, surface)
-
- w, h = self.CLOSE_TEXT.get_size()
- self.close_rect = pygame.rect.Rect(self.w - 2 - w, 1, w, h)
- w, h = self.GROW_TEXT.get_size()
- self.grow_rect = pygame.rect.Rect(1, 1, w, h)
-
- self.body_surface = surface.subsurface(self.body_rect)
- self.line_w = self.body_rect.w / FONT.char_w + 1
- self.h_in_lines = self.body_rect.h / FONT.line_h - 1
- self.command_rect = self.command = None
- self._sel_start = self._sel_end = None
-
- def handle(self, message):
- if super(TextViewer, self).handle(message):
- return
- if (isinstance(message, ModifyMessage)
- and message.subject is self.lines
- ):
- # TODO: check self.at_line
- self.draw_body()
-
- # Drawing
-
- def draw_menu(self):
- #MenuViewer.draw_menu(self)
- self.surface.blit(self.GROW_TEXT, (1, 1))
- self.surface.blit(self.CLOSE_TEXT,
- (self.w - 2 - self.close_rect.w, 1))
- if self.content_id:
- self.surface.blit(FONT.render('| ' + self.content_id),
- (self.grow_rect.w + FONT.char_w + 3, 1))
- self.surface.fill( # light grey background
- (196, 196, 196),
- (0, 0, self.w - 1, self.MINIMUM_HEIGHT),
- pygame.BLEND_MULT
- )
-
- def draw_body(self):
- MenuViewer.draw_body(self)
- ys = xrange(0, self.body_rect.height, FONT.line_h)
- ls = self.lines[self.at_line:self.at_line + self.h_in_lines + 2]
- for y, line in zip(ys, ls):
- self.draw_line(y, line)
-
- def draw_line(self, y, line):
- surface = FONT.render(line[:self.line_w])
- self.body_surface.blit(surface, (0, y))
-
- def _redraw_line(self, row):
- try: line = self.lines[row]
- except IndexError: line = ' ' * self.line_w
- else:
- n = self.line_w - len(line)
- if n > 0: line = line + ' ' * n
- self.draw_line(self.cursor.screen_y(row), line)
-
- # General Functionality
-
- def focus(self, display):
- self.cursor.v = self
- self.cursor.draw()
-
- def unfocus(self):
- self.cursor.fade()
-
- def scroll_up(self):
- if self.at_line < len(self.lines) - 1:
- self._fade_command()
- self._deselect()
- self._sel_start = self._sel_end = None
- self.at_line += 1
- self.body_surface.scroll(0, -FONT.line_h)
- row = self.h_in_lines + self.at_line
- self._redraw_line(row)
- self._redraw_line(row + 1)
- self.cursor.draw()
-
- def scroll_down(self):
- if self.at_line:
- self._fade_command()
- self._deselect()
- self._sel_start = self._sel_end = None
- self.at_line -= 1
- self.body_surface.scroll(0, FONT.line_h)
- self._redraw_line(self.at_line)
- self.cursor.draw()
-
- def command_down(self, display, x, y):
- if self.command_rect and self.command_rect.collidepoint(x, y):
- return
- self._fade_command()
- line, column, _row = self.at(x, y)
- word_start = line.rfind(' ', 0, column) + 1
- word_end = line.find(' ', column)
- if word_end == -1: word_end = len(line)
- word = line[word_start:word_end]
- if not _is_command(display, word):
- return
- r = self.command_rect = pygame.Rect(
- word_start * FONT.char_w, # x
- y / FONT.line_h * FONT.line_h, # y
- len(word) * FONT.char_w, # w
- FONT.line_h # h
- )
- pygame.draw.line(self.body_surface, FG, r.bottomleft, r.bottomright)
- self.command = word
-
- def command_up(self, display):
- if self.command:
- command = self.command
- self._fade_command()
- display.broadcast(CommandMessage(self, command))
-
- def _fade_command(self):
- self.command = None
- r, self.command_rect = self.command_rect, None
- if r:
- pygame.draw.line(self.body_surface, BG, r.bottomleft, r.bottomright)
-
-[docs] def at(self, x, y):
- '''
- Given screen coordinates return the line, row, and column of the
- character there.
- '''
- row = self.at_line + y / FONT.line_h
- try:
- line = self.lines[row]
- except IndexError:
- row = len(self.lines) - 1
- line = self.lines[row]
- column = len(line)
- else:
- column = min(x / FONT.char_w, len(line))
- return line, column, row
-
- # Event Processing
-
- def body_click(self, display, x, y, button):
- if button == 1:
- _line, column, row = self.at(x, y)
- self.cursor.set_to(column, row)
- elif button == 2:
- if pygame.KMOD_SHIFT & pygame.key.get_mods():
- self.scroll_up()
- else:
- self.scroll_down()
- elif button == 3:
- self.command_down(display, x, y)
- elif button == 4: self.scroll_down()
- elif button == 5: self.scroll_up()
-
- def menu_click(self, display, x, y, button):
- if MenuViewer.menu_click(self, display, x, y, button):
- return True
-
- def mouse_up(self, display, x, y, button):
- if MenuViewer.mouse_up(self, display, x, y, button):
- return True
- elif button == 3 and self.body_rect.collidepoint(x, y):
- self.command_up(display)
-
- def mouse_motion(self, display, x, y, rel_x, rel_y, button0, button1, button2):
- if MenuViewer.mouse_motion(self, display, x, y, rel_x, rel_y,
- button0, button1, button2):
- return True
- if (button0
- and display.focused_viewer is self
- and self.body_rect.collidepoint(x, y)
- ):
- bx, by = self.body_rect.topleft
- _line, column, row = self.at(x - bx, y - by)
- self.cursor.set_to(column, row)
- elif button2 and self.body_rect.collidepoint(x, y):
- bx, by = self.body_rect.topleft
- self.command_down(display, x - bx, y - by)
-
-
-
- def key_down(self, display, uch, key, mod):
-
- if key in SELECTION_KEYS:
- self._selection_key(display, key, mod)
- return
- if key in STACK_CHATTER_KEYS:
- self._stack_chatter_key(display, key, mod)
- return
- if key in ARROW_KEYS:
- self._arrow_key(key, mod)
- return
-
- line, i = self.lines[self.cursor.y], self.cursor.x
- modified = ()
- if key == pygame.K_RETURN:
- self._return_key(mod, line, i)
- modified = True
- elif key == pygame.K_BACKSPACE:
- modified = self._backspace_key(mod, line, i)
- elif key == pygame.K_DELETE:
- modified = self._delete_key(mod, line, i)
- elif key == pygame.K_INSERT:
- modified = self._insert_key(display, mod, line, i)
- elif uch and uch in FONT or uch == ' ':
- self._printable_key(uch, mod, line, i)
- modified = True
- else:
- print '%r %i %s' % (uch, key, bin(mod))
-
- if modified:
- # The selection is fragile.
- self._deselect()
- self._sel_start = self._sel_end = None
- message = ModifyMessage(
- self, self.lines, content_id=self.content_id)
- display.broadcast(message)
-
- def _stack_chatter_key(self, display, key, mod):
- if key == pygame.K_F5:
- if mod & pygame.KMOD_SHIFT:
- command = 'roll<'
- else:
- command = 'swap'
- elif key == pygame.K_F6:
- if mod & pygame.KMOD_SHIFT:
- command = 'roll>'
- else:
- command = 'dup'
- elif key == pygame.K_F7:
- if mod & pygame.KMOD_SHIFT:
- command = 'tuck'
- else:
- command = 'over'
-## elif key == pygame.K_F8:
-## if mod & pygame.KMOD_SHIFT:
-## command = ''
-## else:
-## command = ''
- else:
- return
- display.broadcast(CommandMessage(self, command))
-
- # Selection Handling
-
- def _selection_key(self, display, key, mod):
- self.cursor.fade()
- self._deselect()
- if key == pygame.K_F1: # set sel start
- self._sel_start = self.cursor.y, self.cursor.x
- self._update_selection()
- elif key == pygame.K_F2: # set sel end
- self._sel_end = self.cursor.y, self.cursor.x
- self._update_selection()
- elif key == pygame.K_F3: # copy
- if mod & pygame.KMOD_SHIFT:
- self._parse_selection(display)
- else:
- self._copy_selection(display)
- self._update_selection()
- elif key == pygame.K_F4: # cut or delete
- if mod & pygame.KMOD_SHIFT:
- self._delete_selection(display)
- else:
- self._cut_selection(display)
- self.cursor.draw()
-
- def _deselect(self):
- if self._has_selection():
- srow, erow = self._sel_start[0], self._sel_end[0]
- # Just erase the whole selection.
- for r in range(min(srow, erow), max(srow, erow) + 1):
- self._redraw_line(r)
-
- def _copy_selection(self, display):
- if push(self, self._get_selection(), display.broadcast) == SUCCESS:
- return True
-## om = OpenMessage(self, 'stack.pickle')
-## display.broadcast(om)
-## if om.status == SUCCESS:
-## selection = self._get_selection()
-## om.thing[0] = selection, om.thing[0]
-## display.broadcast(ModifyMessage(
-## self, om.thing, content_id=om.content_id))
-
- def _parse_selection(self, display):
- if self._has_selection():
- if self._copy_selection(display):
- display.broadcast(CommandMessage(self, 'parse'))
-
- def _cut_selection(self, display):
- if self._has_selection():
- if self._copy_selection(display):
- self._delete_selection(display)
-
- def _delete_selection(self, display):
- if not self._has_selection():
- return
- self.cursor.fade()
- srow, scolumn, erow, ecolumn = self._selection_coords()
- if srow == erow:
- line = self.lines[srow]
- self.lines[srow] = line[:scolumn] + line[ecolumn:]
- else:
- left = self.lines[srow][:scolumn]
- right = self.lines[erow][ecolumn:]
- self.lines[srow:erow + 1] = [left + right]
- self.draw_body()
- self.cursor.set_to(srow, scolumn)
- display.broadcast(ModifyMessage(
- self, self.lines, content_id=self.content_id))
-
- def _has_selection(self):
- return (self._sel_start
- and self._sel_end
- and self._sel_start != self._sel_end)
-
- def _get_selection(self):
- '''Return the current selection if any as a single string.'''
- if not self._has_selection():
- return ''
- srow, scolumn, erow, ecolumn = self._selection_coords()
- if srow == erow:
- return str(self.lines[srow][scolumn:ecolumn])
- lines = []
- assert srow < erow
- while srow <= erow:
- line = self.lines[srow]
- e = ecolumn if srow == erow else len(line)
- lines.append(line[scolumn:e])
- scolumn = 0
- srow += 1
- return str('\n'.join(lines))
-
- def _selection_coords(self):
- (srow, scolumn), (erow, ecolumn) = (
- min(self._sel_start, self._sel_end),
- max(self._sel_start, self._sel_end)
- )
- return srow, scolumn, erow, ecolumn
-
- def _update_selection(self):
- if self._sel_start is None and self._sel_end:
- self._sel_start = self._sel_end
- elif self._sel_end is None and self._sel_start:
- self._sel_end = self._sel_start
- assert self._sel_start and self._sel_end
- if self._sel_start != self._sel_end:
- for rect in self._iter_selection_rectangles():
- self.body_surface.fill(
- SELECTION_COLOR,
- rect,
- pygame.BLEND_RGBA_MULT
- )
-
- def _iter_selection_rectangles(self, ):
- srow, scolumn, erow, ecolumn = self._selection_coords()
- if srow == erow:
- yield (
- scolumn * FONT.char_w,
- self.cursor.screen_y(srow),
- (ecolumn - scolumn) * FONT.char_w,
- FONT.line_h
- )
- return
- lines = self.lines[srow:erow + 1]
- assert len(lines) >= 2
- first_line = lines[0]
- yield (
- scolumn * FONT.char_w,
- self.cursor.screen_y(srow),
- (len(first_line) - scolumn) * FONT.char_w,
- FONT.line_h
- )
- yield (
- 0,
- self.cursor.screen_y(erow),
- ecolumn * FONT.char_w,
- FONT.line_h
- )
- if len(lines) > 2:
- for line in lines[1:-1]:
- srow += 1
- yield (
- 0,
- self.cursor.screen_y(srow),
- len(line) * FONT.char_w,
- FONT.line_h
- )
-
- # Key Handlers
-
- def _printable_key(self, uch, _mod, line, i):
- line = line[:i] + uch + line[i:]
- self.lines[self.cursor.y] = line
- self.cursor.fade()
- self.cursor.x += 1
- self.draw_line(self.cursor.screen_y(), line)
- self.cursor.draw()
-
- def _backspace_key(self, _mod, line, i):
- res = False
- if i:
- line = line[:i - 1] + line[i:]
- self.lines[self.cursor.y] = line
- self.cursor.fade()
- self.cursor.x -= 1
- self.draw_line(self.cursor.screen_y(), line + ' ')
- self.cursor.draw()
- res = True
- elif self.cursor.y:
- y = self.cursor.y
- left, right = self.lines[y - 1:y + 1]
- self.lines[y - 1:y + 1] = [left + right]
- self.cursor.x = len(left)
- self.cursor.y -= 1
- self.draw_body()
- self.cursor.draw()
- res = True
- return res
-
- def _delete_key(self, _mod, line, i):
- res = False
- if i < len(line):
- line = line[:i] + line[i + 1:]
- self.lines[self.cursor.y] = line
- self.cursor.fade()
- self.draw_line(self.cursor.screen_y(), line + ' ')
- self.cursor.draw()
- res = True
- elif self.cursor.y < len(self.lines) - 1:
- y = self.cursor.y
- left, right = self.lines[y:y + 2]
- self.lines[y:y + 2] = [left + right]
- self.draw_body()
- self.cursor.draw()
- res = True
- return res
-
- def _arrow_key(self, key, mod):
- if key == pygame.K_UP: self.cursor.up(mod)
- elif key == pygame.K_DOWN: self.cursor.down(mod)
- elif key == pygame.K_LEFT: self.cursor.left(mod)
- elif key == pygame.K_RIGHT: self.cursor.right(mod)
-
- def _return_key(self, _mod, line, i):
- self.cursor.fade()
- # Ignore the mods for now.
- n = self.cursor.y
- self.lines[n:n + 1] = [line[:i], line[i:]]
- self.cursor.y += 1
- self.cursor.x = 0
- if self.cursor.y > self.at_line + self.h_in_lines:
- self.scroll_up()
- else:
- self.draw_body()
- self.cursor.draw()
-
- def _insert_key(self, display, mod, _line, _i):
- om = OpenMessage(self, 'stack.pickle')
- display.broadcast(om)
- if om.status != SUCCESS:
- return
- stack = om.thing[0]
- if stack:
- content = format_stack_item(stack[0])
- if self.insert(content):
- if mod & pygame.KMOD_SHIFT:
- display.broadcast(CommandMessage(self, 'pop'))
- return True
-
- def insert(self, content):
- assert isinstance(content, basestring), repr(content)
- if content:
- self.cursor.fade()
- row, column = self.cursor.y, self.cursor.x
- line = self.lines[row]
- lines = (line[:column] + content + line[column:]).splitlines()
- self.lines[row:row + 1] = lines
- self.draw_body()
- self.cursor.y = row + len(lines) - 1
- self.cursor.x = len(lines[-1]) - len(line) + column
- self.cursor.draw()
- return True
-
- def append(self, content):
- self.cursor.fade()
- self.cursor.y = len(self.lines) - 1
- self.cursor.x = len(self.lines[self.cursor.y])
- self.insert(content)
-
-# -*- coding: utf-8 -*-
-#
-# Copyright © 2019 Simon Forman
-#
-# This file is part of joy.py
-#
-# joy.py 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 3 of the License, or
-# (at your option) any later version.
-#
-# joy.py 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.
-#
-# You should have received a copy of the GNU General Public License
-# along with joy.py. If not see <http://www.gnu.org/licenses/>.
-#
-'''
-
-Viewer
-=================
-
-'''
-import pygame
-from joy.vui.core import BACKGROUND, FOREGROUND
-
-
-[docs]class Viewer(object):
- '''
- Base Viewer class
- '''
-
- MINIMUM_HEIGHT = 11
-
- def __init__(self, surface):
- self.resurface(surface)
- self.last_touch = 0, 0
-
- def resurface(self, surface):
- self.w, self.h = surface.get_width(), surface.get_height()
- self.surface = surface
-
-[docs] def split(self, y):
- '''
- Split the viewer at the y coordinate (which is relative to the
- viewer's surface and must be inside it somewhere) and return the
- remaining height. The upper part of the viewer remains (and gets
- redrawn on a new surface) and the lower space is now available
- for e.g. a new viewer.
- '''
- assert y >= self.MINIMUM_HEIGHT
- new_viewer_h = self.h - y
- self.resurface(self.surface.subsurface((0, 0, self.w, y)))
- if y <= self.last_touch[1]: self.last_touch = 0, 0
- self.draw()
- return new_viewer_h
-
- def handle(self, message):
- assert self is not message.sender
- pass
-
-[docs] def draw(self):
- '''Draw the viewer onto its surface.'''
- self.surface.fill(BACKGROUND)
- x, y, h = self.w - 1, self.MINIMUM_HEIGHT, self.h - 1
- # Right-hand side.
- pygame.draw.line(self.surface, FOREGROUND, (x, 0), (x, h))
- # Between header and body.
- pygame.draw.line(self.surface, FOREGROUND, (0, y), (x, y))
- # Bottom.
- pygame.draw.line(self.surface, FOREGROUND, (0, h), (x, h))
-
-
-
- def focus(self, display):
- pass
-
- def unfocus(self):
- pass
-
- # Event handling.
-
- def mouse_down(self, display, x, y, button):
- self.last_touch = x, y
-
- def mouse_up(self, display, x, y, button):
- pass
-
- def mouse_motion(self, display, x, y, dx, dy, button0, button1, button2):
- pass
-
- def key_up(self, display, key, mod):
- if key == pygame.K_q and mod & pygame.KMOD_CTRL: # Ctrl-q
- display.close_viewer(self)
- return True
- if key == pygame.K_g and mod & pygame.KMOD_CTRL: # Ctrl-g
- display.grow_viewer(self)
- return True
-
- def key_down(self, display, uch, key, mod):
- pass
-
-
-[docs]class MenuViewer(Viewer):
-
- '''
- MenuViewer class
- '''
-
- MINIMUM_HEIGHT = 26
-
- def __init__(self, surface):
- Viewer.__init__(self, surface)
- self.resizing = 0
- self.bg = 100, 150, 100
-
- def resurface(self, surface):
- Viewer.resurface(self, surface)
- n = self.MINIMUM_HEIGHT - 2
- self.close_rect = pygame.rect.Rect(self.w - 2 - n, 1, n, n)
- self.grow_rect = pygame.rect.Rect(1, 1, n, n)
- self.body_rect = pygame.rect.Rect(
- 0, self.MINIMUM_HEIGHT + 1,
- self.w - 1, self.h - self.MINIMUM_HEIGHT - 2)
-
-[docs] def draw(self):
- '''Draw the viewer onto its surface.'''
- Viewer.draw(self)
- if not self.resizing:
- self.draw_menu()
- self.draw_body()
-
- def draw_menu(self):
- # menu buttons
- pygame.draw.rect(self.surface, FOREGROUND, self.close_rect, 1)
- pygame.draw.rect(self.surface, FOREGROUND, self.grow_rect, 1)
-
- def draw_body(self):
- self.surface.fill(self.bg, self.body_rect)
-
- def mouse_down(self, display, x, y, button):
- Viewer.mouse_down(self, display, x, y, button)
- if y <= self.MINIMUM_HEIGHT:
- self.menu_click(display, x, y, button)
- else:
- bx, by = self.body_rect.topleft
- self.body_click(display, x - bx, y - by, button)
-
- def body_click(self, display, x, y, button):
- if button == 1:
- self.draw_an_a(x, y)
-
- def menu_click(self, display, x, y, button):
- if button == 1:
- self.resizing = 1
- elif button == 3:
- if self.close_rect.collidepoint(x, y):
- display.close_viewer(self)
- return True
- elif self.grow_rect.collidepoint(x, y):
- display.grow_viewer(self)
- return True
-
- def mouse_up(self, display, x, y, button):
-
- if button == 1 and self.resizing:
- if self.resizing == 2:
- self.resizing = 0
- self.draw()
- display.done_resizing()
- self.resizing = 0
- return True
-
- def mouse_motion(self, display, x, y, rel_x, rel_y, button0, button1, button2):
- if self.resizing and button0:
- self.resizing = 2
- display.change_viewer(self, rel_y, relative=True)
- return True
- else:
- self.resizing = 0
- #self.draw_an_a(x, y)
-
- def key_up(self, display, key, mod):
- if Viewer.key_up(self, display, key, mod):
- return True
-
- def draw_an_a(self, x, y):
- # Draw a crude letter A.
- lw, lh = 10, 14
- try: surface = self.surface.subsurface((x - lw, y - lh, lw, lh))
- except ValueError: return
- draw_a(surface, blend=1)
-
-
-[docs]class SomeViewer(MenuViewer):
-
- def __init__(self, surface):
- MenuViewer.__init__(self, surface)
-
- def resurface(self, surface):
- MenuViewer.resurface(self, surface)
-
- def draw_menu(self):
- MenuViewer.draw_menu(self)
-
- def draw_body(self):
- pass
-
- def body_click(self, display, x, y, button):
- pass
-
- def menu_click(self, display, x, y, button):
- if MenuViewer.menu_click(self, display, x, y, button):
- return True
-
- def mouse_up(self, display, x, y, button):
- if MenuViewer.mouse_up(self, display, x, y, button):
- return True
-
- def mouse_motion(self, display, x, y, rel_x, rel_y, button0, button1, button2):
- if MenuViewer.mouse_motion(self, display, x, y, rel_x, rel_y,
- button0, button1, button2):
- return True
-
- def key_down(self, display, uch, key, mod):
- try:
- print chr(key),
- except ValueError:
- pass
-
-
-# Note that Oberon book says that if you split at the exact top of a viewer
-# it should close, and I think this implies the new viewer gets the old
-# viewer's whole height. I haven't implemented that yet, so the edge-case
-# in the code is broken by "intent" for now..
-
-
-def draw_a(surface, color=FOREGROUND, blend=False):
- w, h = surface.get_width() - 2, surface.get_height() - 2
- pygame.draw.aalines(surface, color, False, (
- (1, h), (w / 2, 1), (w, h), (1, h / 2)
- ), blend)
-' + _('Hide Search Matches') + '
') - .appendTo($('#searchbox')); - } - }, - - /** - * init the domain index toggle buttons - */ - initIndexTable : function() { - var togglers = $('img.toggler').click(function() { - var src = $(this).attr('src'); - var idnum = $(this).attr('id').substr(7); - $('tr.cg-' + idnum).toggle(); - if (src.substr(-9) === 'minus.png') - $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); - else - $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); - }).css('display', ''); - if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { - togglers.click(); - } - }, - - /** - * helper function to hide the search marks again - */ - hideSearchWords : function() { - $('#searchbox .highlight-link').fadeOut(300); - $('span.highlighted').removeClass('highlighted'); - }, - - /** - * make the url absolute - */ - makeURL : function(relativeURL) { - return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; - }, - - /** - * get the current relative url - */ - getCurrentURL : function() { - var path = document.location.pathname; - var parts = path.split(/\//); - $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { - if (this === '..') - parts.pop(); - }); - var url = parts.join('/'); - return path.substring(url.lastIndexOf('/') + 1, path.length - 1); - }, - - initOnKeyListeners: function() { - $(document).keyup(function(event) { - var activeElementType = document.activeElement.tagName; - // don't navigate when in search box or textarea - if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT') { - switch (event.keyCode) { - case 37: // left - var prevHref = $('link[rel="prev"]').prop('href'); - if (prevHref) { - window.location.href = prevHref; - return false; - } - case 39: // right - var nextHref = $('link[rel="next"]').prop('href'); - if (nextHref) { - window.location.href = nextHref; - return false; - } - } - } - }); - } -}; - -// quick alias for translations -_ = Documentation.gettext; - -$(document).ready(function() { - Documentation.init(); -}); diff --git a/docs/VUI-docs/build/html/_static/documentation_options.js b/docs/VUI-docs/build/html/_static/documentation_options.js deleted file mode 100644 index bee34fe..0000000 --- a/docs/VUI-docs/build/html/_static/documentation_options.js +++ /dev/null @@ -1,10 +0,0 @@ -var DOCUMENTATION_OPTIONS = { - URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), - VERSION: '0.1', - LANGUAGE: 'None', - COLLAPSE_INDEX: false, - FILE_SUFFIX: '.html', - HAS_SOURCE: true, - SOURCELINK_SUFFIX: '.txt', - NAVIGATION_WITH_KEYS: false, -}; \ No newline at end of file diff --git a/docs/VUI-docs/build/html/_static/down-pressed.png b/docs/VUI-docs/build/html/_static/down-pressed.png deleted file mode 100644 index 5756c8c..0000000 Binary files a/docs/VUI-docs/build/html/_static/down-pressed.png and /dev/null differ diff --git a/docs/VUI-docs/build/html/_static/down.png b/docs/VUI-docs/build/html/_static/down.png deleted file mode 100644 index 1b3bdad..0000000 Binary files a/docs/VUI-docs/build/html/_static/down.png and /dev/null differ diff --git a/docs/VUI-docs/build/html/_static/file.png b/docs/VUI-docs/build/html/_static/file.png deleted file mode 100644 index a858a41..0000000 Binary files a/docs/VUI-docs/build/html/_static/file.png and /dev/null differ diff --git a/docs/VUI-docs/build/html/_static/jquery-3.2.1.js b/docs/VUI-docs/build/html/_static/jquery-3.2.1.js deleted file mode 100644 index d2d8ca4..0000000 --- a/docs/VUI-docs/build/html/_static/jquery-3.2.1.js +++ /dev/null @@ -1,10253 +0,0 @@ -/*! - * jQuery JavaScript Library v3.2.1 - * https://jquery.com/ - * - * Includes Sizzle.js - * https://sizzlejs.com/ - * - * Copyright JS Foundation and other contributors - * Released under the MIT license - * https://jquery.org/license - * - * Date: 2017-03-20T18:59Z - */ -( function( global, factory ) { - - "use strict"; - - if ( typeof module === "object" && typeof module.exports === "object" ) { - - // For CommonJS and CommonJS-like environments where a proper `window` - // is present, execute the factory and get jQuery. - // For environments that do not have a `window` with a `document` - // (such as Node.js), expose a factory as module.exports. - // This accentuates the need for the creation of a real `window`. - // e.g. var jQuery = require("jquery")(window); - // See ticket #14549 for more info. - module.exports = global.document ? - factory( global, true ) : - function( w ) { - if ( !w.document ) { - throw new Error( "jQuery requires a window with a document" ); - } - return factory( w ); - }; - } else { - factory( global ); - } - -// Pass this if window is not defined yet -} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { - -// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 -// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode -// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common -// enough that all such attempts are guarded in a try block. -"use strict"; - -var arr = []; - -var document = window.document; - -var getProto = Object.getPrototypeOf; - -var slice = arr.slice; - -var concat = arr.concat; - -var push = arr.push; - -var indexOf = arr.indexOf; - -var class2type = {}; - -var toString = class2type.toString; - -var hasOwn = class2type.hasOwnProperty; - -var fnToString = hasOwn.toString; - -var ObjectFunctionString = fnToString.call( Object ); - -var support = {}; - - - - function DOMEval( code, doc ) { - doc = doc || document; - - var script = doc.createElement( "script" ); - - script.text = code; - doc.head.appendChild( script ).parentNode.removeChild( script ); - } -/* global Symbol */ -// Defining this global in .eslintrc.json would create a danger of using the global -// unguarded in another place, it seems safer to define global only for this module - - - -var - version = "3.2.1", - - // Define a local copy of jQuery - jQuery = function( selector, context ) { - - // The jQuery object is actually just the init constructor 'enhanced' - // Need init if jQuery is called (just allow error to be thrown if not included) - return new jQuery.fn.init( selector, context ); - }, - - // Support: Android <=4.0 only - // Make sure we trim BOM and NBSP - rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, - - // Matches dashed string for camelizing - rmsPrefix = /^-ms-/, - rdashAlpha = /-([a-z])/g, - - // Used by jQuery.camelCase as callback to replace() - fcamelCase = function( all, letter ) { - return letter.toUpperCase(); - }; - -jQuery.fn = jQuery.prototype = { - - // The current version of jQuery being used - jquery: version, - - constructor: jQuery, - - // The default length of a jQuery object is 0 - length: 0, - - toArray: function() { - return slice.call( this ); - }, - - // Get the Nth element in the matched element set OR - // Get the whole matched element set as a clean array - get: function( num ) { - - // Return all the elements in a clean array - if ( num == null ) { - return slice.call( this ); - } - - // Return just the one element from the set - return num < 0 ? this[ num + this.length ] : this[ num ]; - }, - - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack: function( elems ) { - - // Build a new jQuery matched element set - var ret = jQuery.merge( this.constructor(), elems ); - - // Add the old object onto the stack (as a reference) - ret.prevObject = this; - - // Return the newly-formed element set - return ret; - }, - - // Execute a callback for every element in the matched set. - each: function( callback ) { - return jQuery.each( this, callback ); - }, - - map: function( callback ) { - return this.pushStack( jQuery.map( this, function( elem, i ) { - return callback.call( elem, i, elem ); - } ) ); - }, - - slice: function() { - return this.pushStack( slice.apply( this, arguments ) ); - }, - - first: function() { - return this.eq( 0 ); - }, - - last: function() { - return this.eq( -1 ); - }, - - eq: function( i ) { - var len = this.length, - j = +i + ( i < 0 ? len : 0 ); - return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); - }, - - end: function() { - return this.prevObject || this.constructor(); - }, - - // For internal use only. - // Behaves like an Array's method, not like a jQuery method. - push: push, - sort: arr.sort, - splice: arr.splice -}; - -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; - - // Skip the boolean and the target - target = arguments[ i ] || {}; - i++; - } - - // 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 ( i === length ) { - 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 = Array.isArray( copy ) ) ) ) { - - if ( copyIsArray ) { - copyIsArray = false; - clone = src && Array.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( { - - // Unique for each copy of jQuery on the page - expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), - - // Assume jQuery is ready without the ready module - isReady: true, - - error: function( msg ) { - throw new Error( msg ); - }, - - noop: function() {}, - - isFunction: function( obj ) { - return jQuery.type( obj ) === "function"; - }, - - isWindow: function( obj ) { - return obj != null && obj === obj.window; - }, - - isNumeric: function( obj ) { - - // As of jQuery 3.0, isNumeric is limited to - // strings and numbers (primitives or objects) - // that can be coerced to finite numbers (gh-2662) - var type = jQuery.type( obj ); - return ( type === "number" || type === "string" ) && - - // parseFloat NaNs numeric-cast false positives ("") - // ...but misinterprets leading-number strings, particularly hex literals ("0x...") - // subtraction forces infinities to NaN - !isNaN( obj - parseFloat( obj ) ); - }, - - isPlainObject: function( obj ) { - var proto, Ctor; - - // Detect obvious negatives - // Use toString instead of jQuery.type to catch host objects - if ( !obj || toString.call( obj ) !== "[object Object]" ) { - return false; - } - - proto = getProto( obj ); - - // Objects with no prototype (e.g., `Object.create( null )`) are plain - if ( !proto ) { - return true; - } - - // Objects with prototype are plain iff they were constructed by a global Object function - Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; - return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; - }, - - isEmptyObject: function( obj ) { - - /* eslint-disable no-unused-vars */ - // See https://github.com/eslint/eslint/issues/6125 - var name; - - for ( name in obj ) { - return false; - } - return true; - }, - - type: function( obj ) { - if ( obj == null ) { - return obj + ""; - } - - // Support: Android <=2.3 only (functionish RegExp) - return typeof obj === "object" || typeof obj === "function" ? - class2type[ toString.call( obj ) ] || "object" : - typeof obj; - }, - - // Evaluates a script in a global context - globalEval: function( code ) { - DOMEval( code ); - }, - - // Convert dashed to camelCase; used by the css and data modules - // Support: IE <=9 - 11, Edge 12 - 13 - // Microsoft forgot to hump their vendor prefix (#9572) - camelCase: function( string ) { - return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); - }, - - each: function( obj, callback ) { - var length, i = 0; - - if ( isArrayLike( obj ) ) { - length = obj.length; - for ( ; i < length; i++ ) { - if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { - break; - } - } - } else { - for ( i in obj ) { - if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { - break; - } - } - } - - return obj; - }, - - // Support: Android <=4.0 only - trim: function( text ) { - return text == null ? - "" : - ( text + "" ).replace( rtrim, "" ); - }, - - // results is for internal usage only - makeArray: function( arr, results ) { - var ret = results || []; - - if ( arr != null ) { - if ( isArrayLike( Object( arr ) ) ) { - jQuery.merge( ret, - typeof arr === "string" ? - [ arr ] : arr - ); - } else { - push.call( ret, arr ); - } - } - - return ret; - }, - - inArray: function( elem, arr, i ) { - return arr == null ? -1 : indexOf.call( arr, elem, i ); - }, - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - merge: function( first, second ) { - var len = +second.length, - j = 0, - i = first.length; - - for ( ; j < len; j++ ) { - first[ i++ ] = second[ j ]; - } - - first.length = i; - - return first; - }, - - grep: function( elems, callback, invert ) { - var callbackInverse, - matches = [], - i = 0, - length = elems.length, - callbackExpect = !invert; - - // Go through the array, only saving the items - // that pass the validator function - for ( ; i < length; i++ ) { - callbackInverse = !callback( elems[ i ], i ); - if ( callbackInverse !== callbackExpect ) { - matches.push( elems[ i ] ); - } - } - - return matches; - }, - - // arg is for internal usage only - map: function( elems, callback, arg ) { - var length, value, - i = 0, - ret = []; - - // Go through the array, translating each of the items to their new values - if ( isArrayLike( elems ) ) { - length = elems.length; - for ( ; i < length; i++ ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret.push( value ); - } - } - - // Go through every key on the object, - } else { - for ( i in elems ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret.push( value ); - } - } - } - - // Flatten any nested arrays - return 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 ) { - var tmp, args, proxy; - - if ( typeof context === "string" ) { - 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 - args = slice.call( arguments, 2 ); - proxy = function() { - return fn.apply( context || this, 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 || jQuery.guid++; - - return proxy; - }, - - now: Date.now, - - // jQuery.support is not used in Core but other projects attach their - // properties to it so it needs to exist. - support: support -} ); - -if ( typeof Symbol === "function" ) { - jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; -} - -// Populate the class2type map -jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), -function( i, name ) { - class2type[ "[object " + name + "]" ] = name.toLowerCase(); -} ); - -function isArrayLike( obj ) { - - // Support: real iOS 8.2 only (not reproducible in simulator) - // `in` check used to prevent JIT error (gh-2145) - // hasOwn isn't used here due to false negatives - // regarding Nodelist length in IE - var length = !!obj && "length" in obj && obj.length, - type = jQuery.type( obj ); - - if ( type === "function" || jQuery.isWindow( obj ) ) { - return false; - } - - return type === "array" || length === 0 || - typeof length === "number" && length > 0 && ( length - 1 ) in obj; -} -var Sizzle = -/*! - * Sizzle CSS Selector Engine v2.3.3 - * https://sizzlejs.com/ - * - * Copyright jQuery Foundation and other contributors - * Released under the MIT license - * http://jquery.org/license - * - * Date: 2016-08-08 - */ -(function( window ) { - -var i, - support, - Expr, - getText, - isXML, - tokenize, - compile, - select, - outermostContext, - sortInput, - hasDuplicate, - - // Local document vars - setDocument, - document, - docElem, - documentIsHTML, - rbuggyQSA, - rbuggyMatches, - matches, - contains, - - // Instance-specific data - expando = "sizzle" + 1 * new Date(), - preferredDoc = window.document, - dirruns = 0, - done = 0, - classCache = createCache(), - tokenCache = createCache(), - compilerCache = createCache(), - sortOrder = function( a, b ) { - if ( a === b ) { - hasDuplicate = true; - } - return 0; - }, - - // Instance methods - hasOwn = ({}).hasOwnProperty, - arr = [], - pop = arr.pop, - push_native = arr.push, - push = arr.push, - slice = arr.slice, - // Use a stripped-down indexOf as it's faster than native - // https://jsperf.com/thor-indexof-vs-for/5 - indexOf = function( list, elem ) { - var i = 0, - len = list.length; - for ( ; i < len; i++ ) { - if ( list[i] === elem ) { - return i; - } - } - return -1; - }, - - booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", - - // Regular expressions - - // http://www.w3.org/TR/css3-selectors/#whitespace - whitespace = "[\\x20\\t\\r\\n\\f]", - - // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier - identifier = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+", - - // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors - attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + - // Operator (capture 2) - "*([*^$|!~]?=)" + whitespace + - // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" - "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + - "*\\]", - - pseudos = ":(" + identifier + ")(?:\\((" + - // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: - // 1. quoted (capture 3; capture 4 or capture 5) - "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + - // 2. simple (capture 6) - "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + - // 3. anything else (capture 2) - ".*" + - ")\\)|)", - - // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter - rwhitespace = new RegExp( whitespace + "+", "g" ), - rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), - - rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), - rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), - - rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), - - rpseudo = new RegExp( pseudos ), - ridentifier = new RegExp( "^" + identifier + "$" ), - - matchExpr = { - "ID": new RegExp( "^#(" + identifier + ")" ), - "CLASS": new RegExp( "^\\.(" + identifier + ")" ), - "TAG": new RegExp( "^(" + identifier + "|[*])" ), - "ATTR": new RegExp( "^" + attributes ), - "PSEUDO": new RegExp( "^" + pseudos ), - "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + - "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + - "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), - "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), - // For use in libraries implementing .is() - // We use this for POS matching in `select` - "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + - whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) - }, - - rinputs = /^(?:input|select|textarea|button)$/i, - rheader = /^h\d$/i, - - rnative = /^[^{]+\{\s*\[native \w/, - - // Easily-parseable/retrievable ID or TAG or CLASS selectors - rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, - - rsibling = /[+~]/, - - // CSS escapes - // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters - runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), - funescape = function( _, escaped, escapedWhitespace ) { - var high = "0x" + escaped - 0x10000; - // NaN means non-codepoint - // Support: Firefox<24 - // Workaround erroneous numeric interpretation of +"0x" - return high !== high || escapedWhitespace ? - escaped : - high < 0 ? - // BMP codepoint - String.fromCharCode( high + 0x10000 ) : - // Supplemental Plane codepoint (surrogate pair) - String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); - }, - - // CSS string/identifier serialization - // https://drafts.csswg.org/cssom/#common-serializing-idioms - rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, - fcssescape = function( ch, asCodePoint ) { - if ( asCodePoint ) { - - // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER - if ( ch === "\0" ) { - return "\uFFFD"; - } - - // Control characters and (dependent upon position) numbers get escaped as code points - return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; - } - - // Other potentially-special ASCII characters get backslash-escaped - return "\\" + ch; - }, - - // Used for iframes - // See setDocument() - // Removing the function wrapper causes a "Permission Denied" - // error in IE - unloadHandler = function() { - setDocument(); - }, - - disabledAncestor = addCombinator( - function( elem ) { - return elem.disabled === true && ("form" in elem || "label" in elem); - }, - { dir: "parentNode", next: "legend" } - ); - -// Optimize for push.apply( _, NodeList ) -try { - push.apply( - (arr = slice.call( preferredDoc.childNodes )), - preferredDoc.childNodes - ); - // Support: Android<4.0 - // Detect silently failing push.apply - arr[ preferredDoc.childNodes.length ].nodeType; -} catch ( e ) { - push = { apply: arr.length ? - - // Leverage slice if possible - function( target, els ) { - push_native.apply( target, slice.call(els) ); - } : - - // Support: IE<9 - // Otherwise append directly - function( target, els ) { - var j = target.length, - i = 0; - // Can't trust NodeList.length - while ( (target[j++] = els[i++]) ) {} - target.length = j - 1; - } - }; -} - -function Sizzle( selector, context, results, seed ) { - var m, i, elem, nid, match, groups, newSelector, - newContext = context && context.ownerDocument, - - // nodeType defaults to 9, since context defaults to document - nodeType = context ? context.nodeType : 9; - - results = results || []; - - // Return early from calls with invalid selector or context - if ( typeof selector !== "string" || !selector || - nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { - - return results; - } - - // Try to shortcut find operations (as opposed to filters) in HTML documents - if ( !seed ) { - - if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { - setDocument( context ); - } - context = context || document; - - if ( documentIsHTML ) { - - // If the selector is sufficiently simple, try using a "get*By*" DOM method - // (excepting DocumentFragment context, where the methods don't exist) - if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { - - // ID selector - if ( (m = match[1]) ) { - - // Document context - if ( nodeType === 9 ) { - if ( (elem = context.getElementById( m )) ) { - - // Support: IE, Opera, Webkit - // TODO: identify versions - // getElementById can match elements by name instead of ID - if ( elem.id === m ) { - results.push( elem ); - return results; - } - } else { - return results; - } - - // Element context - } else { - - // Support: IE, Opera, Webkit - // TODO: identify versions - // getElementById can match elements by name instead of ID - if ( newContext && (elem = newContext.getElementById( m )) && - contains( context, elem ) && - elem.id === m ) { - - results.push( elem ); - return results; - } - } - - // Type selector - } else if ( match[2] ) { - push.apply( results, context.getElementsByTagName( selector ) ); - return results; - - // Class selector - } else if ( (m = match[3]) && support.getElementsByClassName && - context.getElementsByClassName ) { - - push.apply( results, context.getElementsByClassName( m ) ); - return results; - } - } - - // Take advantage of querySelectorAll - if ( support.qsa && - !compilerCache[ selector + " " ] && - (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { - - if ( nodeType !== 1 ) { - newContext = context; - newSelector = selector; - - // qSA looks outside Element context, which is not what we want - // Thanks to Andrew Dupont for this workaround technique - // Support: IE <=8 - // Exclude object elements - } else if ( context.nodeName.toLowerCase() !== "object" ) { - - // Capture the context ID, setting it first if necessary - if ( (nid = context.getAttribute( "id" )) ) { - nid = nid.replace( rcssescape, fcssescape ); - } else { - context.setAttribute( "id", (nid = expando) ); - } - - // Prefix every selector in the list - groups = tokenize( selector ); - i = groups.length; - while ( i-- ) { - groups[i] = "#" + nid + " " + toSelector( groups[i] ); - } - newSelector = groups.join( "," ); - - // Expand context for sibling selectors - newContext = rsibling.test( selector ) && testContext( context.parentNode ) || - context; - } - - if ( newSelector ) { - try { - push.apply( results, - newContext.querySelectorAll( newSelector ) - ); - return results; - } catch ( qsaError ) { - } finally { - if ( nid === expando ) { - context.removeAttribute( "id" ); - } - } - } - } - } - } - - // All others - return select( selector.replace( rtrim, "$1" ), context, results, seed ); -} - -/** - * Create key-value caches of limited size - * @returns {function(string, object)} Returns the Object data after storing it on itself with - * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) - * deleting the oldest entry - */ -function createCache() { - var keys = []; - - function cache( key, value ) { - // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) - if ( keys.push( key + " " ) > Expr.cacheLength ) { - // Only keep the most recent entries - delete cache[ keys.shift() ]; - } - return (cache[ key + " " ] = value); - } - return cache; -} - -/** - * Mark a function for special use by Sizzle - * @param {Function} fn The function to mark - */ -function markFunction( fn ) { - fn[ expando ] = true; - return fn; -} - -/** - * Support testing using an element - * @param {Function} fn Passed the created element and returns a boolean result - */ -function assert( fn ) { - var el = document.createElement("fieldset"); - - try { - return !!fn( el ); - } catch (e) { - return false; - } finally { - // Remove from its parent by default - if ( el.parentNode ) { - el.parentNode.removeChild( el ); - } - // release memory in IE - el = null; - } -} - -/** - * Adds the same handler for all of the specified attrs - * @param {String} attrs Pipe-separated list of attributes - * @param {Function} handler The method that will be applied - */ -function addHandle( attrs, handler ) { - var arr = attrs.split("|"), - i = arr.length; - - while ( i-- ) { - Expr.attrHandle[ arr[i] ] = handler; - } -} - -/** - * Checks document order of two siblings - * @param {Element} a - * @param {Element} b - * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b - */ -function siblingCheck( a, b ) { - var cur = b && a, - diff = cur && a.nodeType === 1 && b.nodeType === 1 && - a.sourceIndex - b.sourceIndex; - - // Use IE sourceIndex if available on both nodes - if ( diff ) { - return diff; - } - - // Check if b follows a - if ( cur ) { - while ( (cur = cur.nextSibling) ) { - if ( cur === b ) { - return -1; - } - } - } - - return a ? 1 : -1; -} - -/** - * Returns a function to use in pseudos for input types - * @param {String} type - */ -function createInputPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === type; - }; -} - -/** - * Returns a function to use in pseudos for buttons - * @param {String} type - */ -function createButtonPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return (name === "input" || name === "button") && elem.type === type; - }; -} - -/** - * Returns a function to use in pseudos for :enabled/:disabled - * @param {Boolean} disabled true for :disabled; false for :enabled - */ -function createDisabledPseudo( disabled ) { - - // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable - return function( elem ) { - - // Only certain elements can match :enabled or :disabled - // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled - // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled - if ( "form" in elem ) { - - // Check for inherited disabledness on relevant non-disabled elements: - // * listed form-associated elements in a disabled fieldset - // https://html.spec.whatwg.org/multipage/forms.html#category-listed - // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled - // * option elements in a disabled optgroup - // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled - // All such elements have a "form" property. - if ( elem.parentNode && elem.disabled === false ) { - - // Option elements defer to a parent optgroup if present - if ( "label" in elem ) { - if ( "label" in elem.parentNode ) { - return elem.parentNode.disabled === disabled; - } else { - return elem.disabled === disabled; - } - } - - // Support: IE 6 - 11 - // Use the isDisabled shortcut property to check for disabled fieldset ancestors - return elem.isDisabled === disabled || - - // Where there is no isDisabled, check manually - /* jshint -W018 */ - elem.isDisabled !== !disabled && - disabledAncestor( elem ) === disabled; - } - - return elem.disabled === disabled; - - // Try to winnow out elements that can't be disabled before trusting the disabled property. - // Some victims get caught in our net (label, legend, menu, track), but it shouldn't - // even exist on them, let alone have a boolean value. - } else if ( "label" in elem ) { - return elem.disabled === disabled; - } - - // Remaining elements are neither :enabled nor :disabled - return false; - }; -} - -/** - * Returns a function to use in pseudos for positionals - * @param {Function} fn - */ -function createPositionalPseudo( fn ) { - return markFunction(function( argument ) { - argument = +argument; - return markFunction(function( seed, matches ) { - var j, - matchIndexes = fn( [], seed.length, argument ), - i = matchIndexes.length; - - // Match elements found at the specified indexes - while ( i-- ) { - if ( seed[ (j = matchIndexes[i]) ] ) { - seed[j] = !(matches[j] = seed[j]); - } - } - }); - }); -} - -/** - * Checks a node for validity as a Sizzle context - * @param {Element|Object=} context - * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value - */ -function testContext( context ) { - return context && typeof context.getElementsByTagName !== "undefined" && context; -} - -// Expose support vars for convenience -support = Sizzle.support = {}; - -/** - * Detects XML nodes - * @param {Element|Object} elem An element or a document - * @returns {Boolean} True iff elem is a non-HTML XML node - */ -isXML = 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).documentElement; - return documentElement ? documentElement.nodeName !== "HTML" : false; -}; - -/** - * Sets document-related variables once based on the current document - * @param {Element|Object} [doc] An element or document object to use to set the document - * @returns {Object} Returns the current document - */ -setDocument = Sizzle.setDocument = function( node ) { - var hasCompare, subWindow, - doc = node ? node.ownerDocument || node : preferredDoc; - - // Return early if doc is invalid or already selected - if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { - return document; - } - - // Update global variables - document = doc; - docElem = document.documentElement; - documentIsHTML = !isXML( document ); - - // Support: IE 9-11, Edge - // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) - if ( preferredDoc !== document && - (subWindow = document.defaultView) && subWindow.top !== subWindow ) { - - // Support: IE 11, Edge - if ( subWindow.addEventListener ) { - subWindow.addEventListener( "unload", unloadHandler, false ); - - // Support: IE 9 - 10 only - } else if ( subWindow.attachEvent ) { - subWindow.attachEvent( "onunload", unloadHandler ); - } - } - - /* Attributes - ---------------------------------------------------------------------- */ - - // Support: IE<8 - // Verify that getAttribute really returns attributes and not properties - // (excepting IE8 booleans) - support.attributes = assert(function( el ) { - el.className = "i"; - return !el.getAttribute("className"); - }); - - /* getElement(s)By* - ---------------------------------------------------------------------- */ - - // Check if getElementsByTagName("*") returns only elements - support.getElementsByTagName = assert(function( el ) { - el.appendChild( document.createComment("") ); - return !el.getElementsByTagName("*").length; - }); - - // Support: IE<9 - support.getElementsByClassName = rnative.test( document.getElementsByClassName ); - - // Support: IE<10 - // Check if getElementById returns elements by name - // The broken getElementById methods don't pick up programmatically-set names, - // so use a roundabout getElementsByName test - support.getById = assert(function( el ) { - docElem.appendChild( el ).id = expando; - return !document.getElementsByName || !document.getElementsByName( expando ).length; - }); - - // ID filter and find - if ( support.getById ) { - Expr.filter["ID"] = function( id ) { - var attrId = id.replace( runescape, funescape ); - return function( elem ) { - return elem.getAttribute("id") === attrId; - }; - }; - Expr.find["ID"] = function( id, context ) { - if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { - var elem = context.getElementById( id ); - return elem ? [ elem ] : []; - } - }; - } else { - Expr.filter["ID"] = function( id ) { - var attrId = id.replace( runescape, funescape ); - return function( elem ) { - var node = typeof elem.getAttributeNode !== "undefined" && - elem.getAttributeNode("id"); - return node && node.value === attrId; - }; - }; - - // Support: IE 6 - 7 only - // getElementById is not reliable as a find shortcut - Expr.find["ID"] = function( id, context ) { - if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { - var node, i, elems, - elem = context.getElementById( id ); - - if ( elem ) { - - // Verify the id attribute - node = elem.getAttributeNode("id"); - if ( node && node.value === id ) { - return [ elem ]; - } - - // Fall back on getElementsByName - elems = context.getElementsByName( id ); - i = 0; - while ( (elem = elems[i++]) ) { - node = elem.getAttributeNode("id"); - if ( node && node.value === id ) { - return [ elem ]; - } - } - } - - return []; - } - }; - } - - // Tag - Expr.find["TAG"] = support.getElementsByTagName ? - function( tag, context ) { - if ( typeof context.getElementsByTagName !== "undefined" ) { - return context.getElementsByTagName( tag ); - - // DocumentFragment nodes don't have gEBTN - } else if ( support.qsa ) { - return context.querySelectorAll( tag ); - } - } : - - function( tag, context ) { - var elem, - tmp = [], - i = 0, - // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too - results = context.getElementsByTagName( tag ); - - // Filter out possible comments - if ( tag === "*" ) { - while ( (elem = results[i++]) ) { - if ( elem.nodeType === 1 ) { - tmp.push( elem ); - } - } - - return tmp; - } - return results; - }; - - // Class - Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { - if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { - return context.getElementsByClassName( className ); - } - }; - - /* QSA/matchesSelector - ---------------------------------------------------------------------- */ - - // QSA and matchesSelector support - - // matchesSelector(:active) reports false when true (IE9/Opera 11.5) - rbuggyMatches = []; - - // qSa(:focus) reports false when true (Chrome 21) - // We allow this because of a bug in IE8/9 that throws an error - // whenever `document.activeElement` is accessed on an iframe - // So, we allow :focus to pass through QSA all the time to avoid the IE error - // See https://bugs.jquery.com/ticket/13378 - rbuggyQSA = []; - - if ( (support.qsa = rnative.test( document.querySelectorAll )) ) { - // Build QSA regex - // Regex strategy adopted from Diego Perini - assert(function( el ) { - // Select is set to empty string on purpose - // This is to test IE's treatment of not explicitly - // setting a boolean content attribute, - // since its presence should be enough - // https://bugs.jquery.com/ticket/12359 - docElem.appendChild( el ).innerHTML = "" + - ""; - - // Support: IE8, Opera 11-12.16 - // Nothing should be selected when empty strings follow ^= or $= or *= - // The test attribute must be unknown in Opera but "safe" for WinRT - // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section - if ( el.querySelectorAll("[msallowcapture^='']").length ) { - rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); - } - - // Support: IE8 - // Boolean attributes and "value" are not treated correctly - if ( !el.querySelectorAll("[selected]").length ) { - rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); - } - - // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ - if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { - rbuggyQSA.push("~="); - } - - // Webkit/Opera - :checked should return selected option elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - // IE8 throws error here and will not see later tests - if ( !el.querySelectorAll(":checked").length ) { - rbuggyQSA.push(":checked"); - } - - // Support: Safari 8+, iOS 8+ - // https://bugs.webkit.org/show_bug.cgi?id=136851 - // In-page `selector#id sibling-combinator selector` fails - if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { - rbuggyQSA.push(".#.+[+~]"); - } - }); - - assert(function( el ) { - el.innerHTML = "" + - ""; - - // Support: Windows 8 Native Apps - // The type and name attributes are restricted during .innerHTML assignment - var input = document.createElement("input"); - input.setAttribute( "type", "hidden" ); - el.appendChild( input ).setAttribute( "name", "D" ); - - // Support: IE8 - // Enforce case-sensitivity of name attribute - if ( el.querySelectorAll("[name=d]").length ) { - rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); - } - - // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) - // IE8 throws error here and will not see later tests - if ( el.querySelectorAll(":enabled").length !== 2 ) { - rbuggyQSA.push( ":enabled", ":disabled" ); - } - - // Support: IE9-11+ - // IE's :disabled selector does not pick up the children of disabled fieldsets - docElem.appendChild( el ).disabled = true; - if ( el.querySelectorAll(":disabled").length !== 2 ) { - rbuggyQSA.push( ":enabled", ":disabled" ); - } - - // Opera 10-11 does not throw on post-comma invalid pseudos - el.querySelectorAll("*,:x"); - rbuggyQSA.push(",.*:"); - }); - } - - if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || - docElem.webkitMatchesSelector || - docElem.mozMatchesSelector || - docElem.oMatchesSelector || - docElem.msMatchesSelector) )) ) { - - assert(function( el ) { - // Check to see if it's possible to do matchesSelector - // on a disconnected node (IE 9) - support.disconnectedMatch = matches.call( el, "*" ); - - // This should fail with an exception - // Gecko does not error, returns false instead - matches.call( el, "[s!='']:x" ); - rbuggyMatches.push( "!=", pseudos ); - }); - } - - rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); - rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); - - /* Contains - ---------------------------------------------------------------------- */ - hasCompare = rnative.test( docElem.compareDocumentPosition ); - - // Element contains another - // Purposefully self-exclusive - // As in, an element does not contain itself - contains = hasCompare || rnative.test( docElem.contains ) ? - function( a, b ) { - var adown = a.nodeType === 9 ? a.documentElement : a, - bup = b && b.parentNode; - return a === bup || !!( bup && bup.nodeType === 1 && ( - adown.contains ? - adown.contains( bup ) : - a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 - )); - } : - function( a, b ) { - if ( b ) { - while ( (b = b.parentNode) ) { - if ( b === a ) { - return true; - } - } - } - return false; - }; - - /* Sorting - ---------------------------------------------------------------------- */ - - // Document order sorting - sortOrder = hasCompare ? - function( a, b ) { - - // Flag for duplicate removal - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - // Sort on method existence if only one input has compareDocumentPosition - var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; - if ( compare ) { - return compare; - } - - // Calculate position if both inputs belong to the same document - compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? - a.compareDocumentPosition( b ) : - - // Otherwise we know they are disconnected - 1; - - // Disconnected nodes - if ( compare & 1 || - (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { - - // Choose the first element that is related to our preferred document - if ( a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { - return -1; - } - if ( b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { - return 1; - } - - // Maintain original order - return sortInput ? - ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : - 0; - } - - return compare & 4 ? -1 : 1; - } : - function( a, b ) { - // Exit early if the nodes are identical - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - var cur, - i = 0, - aup = a.parentNode, - bup = b.parentNode, - ap = [ a ], - bp = [ b ]; - - // Parentless nodes are either documents or disconnected - if ( !aup || !bup ) { - return a === document ? -1 : - b === document ? 1 : - aup ? -1 : - bup ? 1 : - sortInput ? - ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : - 0; - - // If the nodes are siblings, we can do a quick check - } else if ( aup === bup ) { - return siblingCheck( a, b ); - } - - // Otherwise we need full lists of their ancestors for comparison - cur = a; - while ( (cur = cur.parentNode) ) { - ap.unshift( cur ); - } - cur = b; - while ( (cur = cur.parentNode) ) { - bp.unshift( cur ); - } - - // Walk down the tree looking for a discrepancy - while ( ap[i] === bp[i] ) { - i++; - } - - return i ? - // Do a sibling check if the nodes have a common ancestor - siblingCheck( ap[i], bp[i] ) : - - // Otherwise nodes in our document sort first - ap[i] === preferredDoc ? -1 : - bp[i] === preferredDoc ? 1 : - 0; - }; - - return document; -}; - -Sizzle.matches = function( expr, elements ) { - return Sizzle( expr, null, null, elements ); -}; - -Sizzle.matchesSelector = function( elem, expr ) { - // Set document vars if needed - if ( ( elem.ownerDocument || elem ) !== document ) { - setDocument( elem ); - } - - // Make sure that attribute selectors are quoted - expr = expr.replace( rattributeQuotes, "='$1']" ); - - if ( support.matchesSelector && documentIsHTML && - !compilerCache[ expr + " " ] && - ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && - ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { - - try { - var ret = matches.call( elem, expr ); - - // IE 9's matchesSelector returns false on disconnected nodes - if ( ret || support.disconnectedMatch || - // As well, disconnected nodes are said to be in a document - // fragment in IE 9 - elem.document && elem.document.nodeType !== 11 ) { - return ret; - } - } catch (e) {} - } - - return Sizzle( expr, document, null, [ elem ] ).length > 0; -}; - -Sizzle.contains = function( context, elem ) { - // Set document vars if needed - if ( ( context.ownerDocument || context ) !== document ) { - setDocument( context ); - } - return contains( context, elem ); -}; - -Sizzle.attr = function( elem, name ) { - // Set document vars if needed - if ( ( elem.ownerDocument || elem ) !== document ) { - setDocument( elem ); - } - - var fn = Expr.attrHandle[ name.toLowerCase() ], - // Don't get fooled by Object.prototype properties (jQuery #13807) - val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? - fn( elem, name, !documentIsHTML ) : - undefined; - - return val !== undefined ? - val : - support.attributes || !documentIsHTML ? - elem.getAttribute( name ) : - (val = elem.getAttributeNode(name)) && val.specified ? - val.value : - null; -}; - -Sizzle.escape = function( sel ) { - return (sel + "").replace( rcssescape, fcssescape ); -}; - -Sizzle.error = function( msg ) { - throw new Error( "Syntax error, unrecognized expression: " + msg ); -}; - -/** - * Document sorting and removing duplicates - * @param {ArrayLike} results - */ -Sizzle.uniqueSort = function( results ) { - var elem, - duplicates = [], - j = 0, - i = 0; - - // Unless we *know* we can detect duplicates, assume their presence - hasDuplicate = !support.detectDuplicates; - sortInput = !support.sortStable && results.slice( 0 ); - results.sort( sortOrder ); - - if ( hasDuplicate ) { - while ( (elem = results[i++]) ) { - if ( elem === results[ i ] ) { - j = duplicates.push( i ); - } - } - while ( j-- ) { - results.splice( duplicates[ j ], 1 ); - } - } - - // Clear input after sorting to release objects - // See https://github.com/jquery/sizzle/pull/225 - sortInput = null; - - return results; -}; - -/** - * Utility function for retrieving the text value of an array of DOM nodes - * @param {Array|Element} elem - */ -getText = Sizzle.getText = function( elem ) { - var node, - ret = "", - i = 0, - nodeType = elem.nodeType; - - if ( !nodeType ) { - // If no nodeType, this is expected to be an array - while ( (node = elem[i++]) ) { - // Do not traverse comment nodes - ret += getText( node ); - } - } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { - // Use textContent for elements - // innerText usage removed for consistency of new lines (jQuery #11153) - if ( typeof elem.textContent === "string" ) { - return elem.textContent; - } else { - // Traverse its children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - ret += getText( elem ); - } - } - } else if ( nodeType === 3 || nodeType === 4 ) { - return elem.nodeValue; - } - // Do not include comment or processing instruction nodes - - return ret; -}; - -Expr = Sizzle.selectors = { - - // Can be adjusted by the user - cacheLength: 50, - - createPseudo: markFunction, - - match: matchExpr, - - attrHandle: {}, - - find: {}, - - relative: { - ">": { dir: "parentNode", first: true }, - " ": { dir: "parentNode" }, - "+": { dir: "previousSibling", first: true }, - "~": { dir: "previousSibling" } - }, - - preFilter: { - "ATTR": function( match ) { - match[1] = match[1].replace( runescape, funescape ); - - // Move the given value to match[3] whether quoted or unquoted - match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); - - if ( match[2] === "~=" ) { - match[3] = " " + match[3] + " "; - } - - return match.slice( 0, 4 ); - }, - - "CHILD": function( match ) { - /* matches from matchExpr["CHILD"] - 1 type (only|nth|...) - 2 what (child|of-type) - 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) - 4 xn-component of xn+y argument ([+-]?\d*n|) - 5 sign of xn-component - 6 x of xn-component - 7 sign of y-component - 8 y of y-component - */ - match[1] = match[1].toLowerCase(); - - if ( match[1].slice( 0, 3 ) === "nth" ) { - // nth-* requires argument - if ( !match[3] ) { - Sizzle.error( match[0] ); - } - - // numeric x and y parameters for Expr.filter.CHILD - // remember that false/true cast respectively to 0/1 - match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); - match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); - - // other types prohibit arguments - } else if ( match[3] ) { - Sizzle.error( match[0] ); - } - - return match; - }, - - "PSEUDO": function( match ) { - var excess, - unquoted = !match[6] && match[2]; - - if ( matchExpr["CHILD"].test( match[0] ) ) { - return null; - } - - // Accept quoted arguments as-is - if ( match[3] ) { - match[2] = match[4] || match[5] || ""; - - // Strip excess characters from unquoted arguments - } else if ( unquoted && rpseudo.test( unquoted ) && - // Get excess from tokenize (recursively) - (excess = tokenize( unquoted, true )) && - // advance to the next closing parenthesis - (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { - - // excess is a negative index - match[0] = match[0].slice( 0, excess ); - match[2] = unquoted.slice( 0, excess ); - } - - // Return only captures needed by the pseudo filter method (type and argument) - return match.slice( 0, 3 ); - } - }, - - filter: { - - "TAG": function( nodeNameSelector ) { - var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); - return nodeNameSelector === "*" ? - function() { return true; } : - function( elem ) { - return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; - }; - }, - - "CLASS": function( className ) { - var pattern = classCache[ className + " " ]; - - return pattern || - (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && - classCache( className, function( elem ) { - return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" ); - }); - }, - - "ATTR": function( name, operator, check ) { - return function( elem ) { - var result = Sizzle.attr( elem, name ); - - if ( result == null ) { - return operator === "!="; - } - if ( !operator ) { - return true; - } - - result += ""; - - return operator === "=" ? result === check : - operator === "!=" ? result !== check : - operator === "^=" ? check && result.indexOf( check ) === 0 : - operator === "*=" ? check && result.indexOf( check ) > -1 : - operator === "$=" ? check && result.slice( -check.length ) === check : - operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : - operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : - false; - }; - }, - - "CHILD": function( type, what, argument, first, last ) { - var simple = type.slice( 0, 3 ) !== "nth", - forward = type.slice( -4 ) !== "last", - ofType = what === "of-type"; - - return first === 1 && last === 0 ? - - // Shortcut for :nth-*(n) - function( elem ) { - return !!elem.parentNode; - } : - - function( elem, context, xml ) { - var cache, uniqueCache, outerCache, node, nodeIndex, start, - dir = simple !== forward ? "nextSibling" : "previousSibling", - parent = elem.parentNode, - name = ofType && elem.nodeName.toLowerCase(), - useCache = !xml && !ofType, - diff = false; - - if ( parent ) { - - // :(first|last|only)-(child|of-type) - if ( simple ) { - while ( dir ) { - node = elem; - while ( (node = node[ dir ]) ) { - if ( ofType ? - node.nodeName.toLowerCase() === name : - node.nodeType === 1 ) { - - return false; - } - } - // Reverse direction for :only-* (if we haven't yet done so) - start = dir = type === "only" && !start && "nextSibling"; - } - return true; - } - - start = [ forward ? parent.firstChild : parent.lastChild ]; - - // non-xml :nth-child(...) stores cache data on `parent` - if ( forward && useCache ) { - - // Seek `elem` from a previously-cached index - - // ...in a gzip-friendly way - node = parent; - outerCache = node[ expando ] || (node[ expando ] = {}); - - // Support: IE <9 only - // Defend against cloned attroperties (jQuery gh-1709) - uniqueCache = outerCache[ node.uniqueID ] || - (outerCache[ node.uniqueID ] = {}); - - cache = uniqueCache[ type ] || []; - nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; - diff = nodeIndex && cache[ 2 ]; - node = nodeIndex && parent.childNodes[ nodeIndex ]; - - while ( (node = ++nodeIndex && node && node[ dir ] || - - // Fallback to seeking `elem` from the start - (diff = nodeIndex = 0) || start.pop()) ) { - - // When found, cache indexes on `parent` and break - if ( node.nodeType === 1 && ++diff && node === elem ) { - uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; - break; - } - } - - } else { - // Use previously-cached element index if available - if ( useCache ) { - // ...in a gzip-friendly way - node = elem; - outerCache = node[ expando ] || (node[ expando ] = {}); - - // Support: IE <9 only - // Defend against cloned attroperties (jQuery gh-1709) - uniqueCache = outerCache[ node.uniqueID ] || - (outerCache[ node.uniqueID ] = {}); - - cache = uniqueCache[ type ] || []; - nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; - diff = nodeIndex; - } - - // xml :nth-child(...) - // or :nth-last-child(...) or :nth(-last)?-of-type(...) - if ( diff === false ) { - // Use the same loop as above to seek `elem` from the start - while ( (node = ++nodeIndex && node && node[ dir ] || - (diff = nodeIndex = 0) || start.pop()) ) { - - if ( ( ofType ? - node.nodeName.toLowerCase() === name : - node.nodeType === 1 ) && - ++diff ) { - - // Cache the index of each encountered element - if ( useCache ) { - outerCache = node[ expando ] || (node[ expando ] = {}); - - // Support: IE <9 only - // Defend against cloned attroperties (jQuery gh-1709) - uniqueCache = outerCache[ node.uniqueID ] || - (outerCache[ node.uniqueID ] = {}); - - uniqueCache[ type ] = [ dirruns, diff ]; - } - - if ( node === elem ) { - break; - } - } - } - } - } - - // Incorporate the offset, then check against cycle size - diff -= last; - return diff === first || ( diff % first === 0 && diff / first >= 0 ); - } - }; - }, - - "PSEUDO": function( pseudo, argument ) { - // pseudo-class names are case-insensitive - // http://www.w3.org/TR/selectors/#pseudo-classes - // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters - // Remember that setFilters inherits from pseudos - var args, - fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || - Sizzle.error( "unsupported pseudo: " + pseudo ); - - // The user may use createPseudo to indicate that - // arguments are needed to create the filter function - // just as Sizzle does - if ( fn[ expando ] ) { - return fn( argument ); - } - - // But maintain support for old signatures - if ( fn.length > 1 ) { - args = [ pseudo, pseudo, "", argument ]; - return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? - markFunction(function( seed, matches ) { - var idx, - matched = fn( seed, argument ), - i = matched.length; - while ( i-- ) { - idx = indexOf( seed, matched[i] ); - seed[ idx ] = !( matches[ idx ] = matched[i] ); - } - }) : - function( elem ) { - return fn( elem, 0, args ); - }; - } - - return fn; - } - }, - - pseudos: { - // Potentially complex pseudos - "not": markFunction(function( selector ) { - // Trim the selector passed to compile - // to avoid treating leading and trailing - // spaces as combinators - var input = [], - results = [], - matcher = compile( selector.replace( rtrim, "$1" ) ); - - return matcher[ expando ] ? - markFunction(function( seed, matches, context, xml ) { - var elem, - unmatched = matcher( seed, null, xml, [] ), - i = seed.length; - - // Match elements unmatched by `matcher` - while ( i-- ) { - if ( (elem = unmatched[i]) ) { - seed[i] = !(matches[i] = elem); - } - } - }) : - function( elem, context, xml ) { - input[0] = elem; - matcher( input, null, xml, results ); - // Don't keep the element (issue #299) - input[0] = null; - return !results.pop(); - }; - }), - - "has": markFunction(function( selector ) { - return function( elem ) { - return Sizzle( selector, elem ).length > 0; - }; - }), - - "contains": markFunction(function( text ) { - text = text.replace( runescape, funescape ); - return function( elem ) { - return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; - }; - }), - - // "Whether an element is represented by a :lang() selector - // is based solely on the element's language value - // being equal to the identifier C, - // or beginning with the identifier C immediately followed by "-". - // The matching of C against the element's language value is performed case-insensitively. - // The identifier C does not have to be a valid language name." - // http://www.w3.org/TR/selectors/#lang-pseudo - "lang": markFunction( function( lang ) { - // lang value must be a valid identifier - if ( !ridentifier.test(lang || "") ) { - Sizzle.error( "unsupported lang: " + lang ); - } - lang = lang.replace( runescape, funescape ).toLowerCase(); - return function( elem ) { - var elemLang; - do { - if ( (elemLang = documentIsHTML ? - elem.lang : - elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { - - elemLang = elemLang.toLowerCase(); - return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; - } - } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); - return false; - }; - }), - - // Miscellaneous - "target": function( elem ) { - var hash = window.location && window.location.hash; - return hash && hash.slice( 1 ) === elem.id; - }, - - "root": function( elem ) { - return elem === docElem; - }, - - "focus": function( elem ) { - return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); - }, - - // Boolean properties - "enabled": createDisabledPseudo( false ), - "disabled": createDisabledPseudo( true ), - - "checked": function( elem ) { - // In CSS3, :checked should return both checked and selected elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - var nodeName = elem.nodeName.toLowerCase(); - return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); - }, - - "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; - }, - - // Contents - "empty": function( elem ) { - // http://www.w3.org/TR/selectors/#empty-pseudo - // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), - // but not by others (comment: 8; processing instruction: 7; etc.) - // nodeType < 6 works because attributes (2) do not appear as children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - if ( elem.nodeType < 6 ) { - return false; - } - } - return true; - }, - - "parent": function( elem ) { - return !Expr.pseudos["empty"]( elem ); - }, - - // Element/input types - "header": function( elem ) { - return rheader.test( elem.nodeName ); - }, - - "input": function( elem ) { - return rinputs.test( elem.nodeName ); - }, - - "button": function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === "button" || name === "button"; - }, - - "text": function( elem ) { - var attr; - return elem.nodeName.toLowerCase() === "input" && - elem.type === "text" && - - // Support: IE<8 - // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" - ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); - }, - - // Position-in-collection - "first": createPositionalPseudo(function() { - return [ 0 ]; - }), - - "last": createPositionalPseudo(function( matchIndexes, length ) { - return [ length - 1 ]; - }), - - "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { - return [ argument < 0 ? argument + length : argument ]; - }), - - "even": createPositionalPseudo(function( matchIndexes, length ) { - var i = 0; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "odd": createPositionalPseudo(function( matchIndexes, length ) { - var i = 1; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { - var i = argument < 0 ? argument + length : argument; - for ( ; --i >= 0; ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { - var i = argument < 0 ? argument + length : argument; - for ( ; ++i < length; ) { - matchIndexes.push( i ); - } - return matchIndexes; - }) - } -}; - -Expr.pseudos["nth"] = Expr.pseudos["eq"]; - -// Add button/input type pseudos -for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { - Expr.pseudos[ i ] = createInputPseudo( i ); -} -for ( i in { submit: true, reset: true } ) { - Expr.pseudos[ i ] = createButtonPseudo( i ); -} - -// Easy API for creating new setFilters -function setFilters() {} -setFilters.prototype = Expr.filters = Expr.pseudos; -Expr.setFilters = new setFilters(); - -tokenize = Sizzle.tokenize = function( selector, parseOnly ) { - var matched, match, tokens, type, - soFar, groups, preFilters, - cached = tokenCache[ selector + " " ]; - - if ( cached ) { - return parseOnly ? 0 : cached.slice( 0 ); - } - - soFar = selector; - groups = []; - preFilters = Expr.preFilter; - - while ( soFar ) { - - // Comma and first run - if ( !matched || (match = rcomma.exec( soFar )) ) { - if ( match ) { - // Don't consume trailing commas as valid - soFar = soFar.slice( match[0].length ) || soFar; - } - groups.push( (tokens = []) ); - } - - matched = false; - - // Combinators - if ( (match = rcombinators.exec( soFar )) ) { - matched = match.shift(); - tokens.push({ - value: matched, - // Cast descendant combinators to space - type: match[0].replace( rtrim, " " ) - }); - soFar = soFar.slice( matched.length ); - } - - // Filters - for ( type in Expr.filter ) { - if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || - (match = preFilters[ type ]( match ))) ) { - matched = match.shift(); - tokens.push({ - value: matched, - type: type, - matches: match - }); - soFar = soFar.slice( matched.length ); - } - } - - if ( !matched ) { - break; - } - } - - // Return the length of the invalid excess - // if we're just parsing - // Otherwise, throw an error or return tokens - return parseOnly ? - soFar.length : - soFar ? - Sizzle.error( selector ) : - // Cache the tokens - tokenCache( selector, groups ).slice( 0 ); -}; - -function toSelector( tokens ) { - var i = 0, - len = tokens.length, - selector = ""; - for ( ; i < len; i++ ) { - selector += tokens[i].value; - } - return selector; -} - -function addCombinator( matcher, combinator, base ) { - var dir = combinator.dir, - skip = combinator.next, - key = skip || dir, - checkNonElements = base && key === "parentNode", - doneName = done++; - - return combinator.first ? - // Check against closest ancestor/preceding element - function( elem, context, xml ) { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - return matcher( elem, context, xml ); - } - } - return false; - } : - - // Check against all ancestor/preceding elements - function( elem, context, xml ) { - var oldCache, uniqueCache, outerCache, - newCache = [ dirruns, doneName ]; - - // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching - if ( xml ) { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - if ( matcher( elem, context, xml ) ) { - return true; - } - } - } - } else { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - outerCache = elem[ expando ] || (elem[ expando ] = {}); - - // Support: IE <9 only - // Defend against cloned attroperties (jQuery gh-1709) - uniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {}); - - if ( skip && skip === elem.nodeName.toLowerCase() ) { - elem = elem[ dir ] || elem; - } else if ( (oldCache = uniqueCache[ key ]) && - oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { - - // Assign to newCache so results back-propagate to previous elements - return (newCache[ 2 ] = oldCache[ 2 ]); - } else { - // Reuse newcache so results back-propagate to previous elements - uniqueCache[ key ] = newCache; - - // A match means we're done; a fail means we have to keep checking - if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { - return true; - } - } - } - } - } - return false; - }; -} - -function elementMatcher( matchers ) { - return matchers.length > 1 ? - function( elem, context, xml ) { - var i = matchers.length; - while ( i-- ) { - if ( !matchers[i]( elem, context, xml ) ) { - return false; - } - } - return true; - } : - matchers[0]; -} - -function multipleContexts( selector, contexts, results ) { - var i = 0, - len = contexts.length; - for ( ; i < len; i++ ) { - Sizzle( selector, contexts[i], results ); - } - return results; -} - -function condense( unmatched, map, filter, context, xml ) { - var elem, - newUnmatched = [], - i = 0, - len = unmatched.length, - mapped = map != null; - - for ( ; i < len; i++ ) { - if ( (elem = unmatched[i]) ) { - if ( !filter || filter( elem, context, xml ) ) { - newUnmatched.push( elem ); - if ( mapped ) { - map.push( i ); - } - } - } - } - - return newUnmatched; -} - -function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { - if ( postFilter && !postFilter[ expando ] ) { - postFilter = setMatcher( postFilter ); - } - if ( postFinder && !postFinder[ expando ] ) { - postFinder = setMatcher( postFinder, postSelector ); - } - return markFunction(function( seed, results, context, xml ) { - var temp, i, elem, - preMap = [], - postMap = [], - preexisting = results.length, - - // Get initial elements from seed or context - elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), - - // Prefilter to get matcher input, preserving a map for seed-results synchronization - matcherIn = preFilter && ( seed || !selector ) ? - condense( elems, preMap, preFilter, context, xml ) : - elems, - - matcherOut = matcher ? - // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, - postFinder || ( seed ? preFilter : preexisting || postFilter ) ? - - // ...intermediate processing is necessary - [] : - - // ...otherwise use results directly - results : - matcherIn; - - // Find primary matches - if ( matcher ) { - matcher( matcherIn, matcherOut, context, xml ); - } - - // Apply postFilter - if ( postFilter ) { - temp = condense( matcherOut, postMap ); - postFilter( temp, [], context, xml ); - - // Un-match failing elements by moving them back to matcherIn - i = temp.length; - while ( i-- ) { - if ( (elem = temp[i]) ) { - matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); - } - } - } - - if ( seed ) { - if ( postFinder || preFilter ) { - if ( postFinder ) { - // Get the final matcherOut by condensing this intermediate into postFinder contexts - temp = []; - i = matcherOut.length; - while ( i-- ) { - if ( (elem = matcherOut[i]) ) { - // Restore matcherIn since elem is not yet a final match - temp.push( (matcherIn[i] = elem) ); - } - } - postFinder( null, (matcherOut = []), temp, xml ); - } - - // Move matched elements from seed to results to keep them synchronized - i = matcherOut.length; - while ( i-- ) { - if ( (elem = matcherOut[i]) && - (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) { - - seed[temp] = !(results[temp] = elem); - } - } - } - - // Add elements to results, through postFinder if defined - } else { - matcherOut = condense( - matcherOut === results ? - matcherOut.splice( preexisting, matcherOut.length ) : - matcherOut - ); - if ( postFinder ) { - postFinder( null, results, matcherOut, xml ); - } else { - push.apply( results, matcherOut ); - } - } - }); -} - -function matcherFromTokens( tokens ) { - var checkContext, matcher, j, - len = tokens.length, - leadingRelative = Expr.relative[ tokens[0].type ], - implicitRelative = leadingRelative || Expr.relative[" "], - i = leadingRelative ? 1 : 0, - - // The foundational matcher ensures that elements are reachable from top-level context(s) - matchContext = addCombinator( function( elem ) { - return elem === checkContext; - }, implicitRelative, true ), - matchAnyContext = addCombinator( function( elem ) { - return indexOf( checkContext, elem ) > -1; - }, implicitRelative, true ), - matchers = [ function( elem, context, xml ) { - var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( - (checkContext = context).nodeType ? - matchContext( elem, context, xml ) : - matchAnyContext( elem, context, xml ) ); - // Avoid hanging onto element (issue #299) - checkContext = null; - return ret; - } ]; - - for ( ; i < len; i++ ) { - if ( (matcher = Expr.relative[ tokens[i].type ]) ) { - matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; - } else { - matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); - - // Return special upon seeing a positional matcher - if ( matcher[ expando ] ) { - // Find the next relative operator (if any) for proper handling - j = ++i; - for ( ; j < len; j++ ) { - if ( Expr.relative[ tokens[j].type ] ) { - break; - } - } - return setMatcher( - i > 1 && elementMatcher( matchers ), - i > 1 && toSelector( - // If the preceding token was a descendant combinator, insert an implicit any-element `*` - tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) - ).replace( rtrim, "$1" ), - matcher, - i < j && matcherFromTokens( tokens.slice( i, j ) ), - j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), - j < len && toSelector( tokens ) - ); - } - matchers.push( matcher ); - } - } - - return elementMatcher( matchers ); -} - -function matcherFromGroupMatchers( elementMatchers, setMatchers ) { - var bySet = setMatchers.length > 0, - byElement = elementMatchers.length > 0, - superMatcher = function( seed, context, xml, results, outermost ) { - var elem, j, matcher, - matchedCount = 0, - i = "0", - unmatched = seed && [], - setMatched = [], - contextBackup = outermostContext, - // We must always have either seed elements or outermost context - elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), - // Use integer dirruns iff this is the outermost matcher - dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), - len = elems.length; - - if ( outermost ) { - outermostContext = context === document || context || outermost; - } - - // Add elements passing elementMatchers directly to results - // Support: IE<9, Safari - // Tolerate NodeList properties (IE: "length"; Safari: