Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLionel LASKE <llaske@c2s.fr>2012-08-25 20:23:36 (GMT)
committer Lionel LASKE <llaske@c2s.fr>2012-08-25 20:23:36 (GMT)
commitca3ad6305ec0655ad8475a12ac2228b61cdd9ba0 (patch)
tree7ef2b853640f99807fba7fd3f2a28bda81727297
Init commit
-rw-r--r--COPYING340
-rw-r--r--activity.py295
-rw-r--r--activity/activity-enyo.svg94
-rw-r--r--activity/activity.info7
-rw-r--r--enyo.py85
-rw-r--r--html/app.js205
-rw-r--r--html/enyo/enyo.css167
-rw-r--r--html/enyo/enyo.js3627
-rw-r--r--html/images/html5-logo.jpgbin0 -> 5048 bytes
-rw-r--r--html/images/liogo.pngbin0 -> 281 bytes
-rw-r--r--html/index.html16
-rw-r--r--html/lib/canvas/Canvas.js68
-rw-r--r--html/lib/canvas/CanvasControl.js52
-rw-r--r--html/lib/canvas/Circle.js14
-rw-r--r--html/lib/canvas/Image.js26
-rw-r--r--html/lib/canvas/Rectangle.js25
-rw-r--r--html/lib/canvas/Shape.js37
-rw-r--r--html/lib/canvas/Text.js25
-rw-r--r--html/lib/canvas/package.js9
-rw-r--r--html/lib/fu/README.md3
-rw-r--r--html/lib/fu/package.js3
-rw-r--r--html/lib/fu/source/button.css48
-rw-r--r--html/lib/fu/source/input.css30
-rw-r--r--html/lib/fu/source/minify/minify.bat11
-rw-r--r--html/lib/fu/source/minify/minify.sh12
-rw-r--r--html/lib/fu/source/package.js5
-rw-r--r--html/lib/fu/source/tab.css47
-rw-r--r--html/lib/layout/fittable/package.js3
-rw-r--r--html/lib/layout/fittable/source/FittableColumns.js41
-rw-r--r--html/lib/layout/fittable/source/FittableLayout.css69
-rw-r--r--html/lib/layout/fittable/source/FittableLayout.js265
-rw-r--r--html/lib/layout/fittable/source/FittableRows.js40
-rw-r--r--html/lib/layout/fittable/source/design.js23
-rw-r--r--html/lib/layout/fittable/source/package.js6
-rw-r--r--html/lib/layout/list/package.js3
-rw-r--r--html/lib/layout/list/source/AlphaJumpList.js37
-rw-r--r--html/lib/layout/list/source/AlphaJumper.css36
-rw-r--r--html/lib/layout/list/source/AlphaJumper.js70
-rw-r--r--html/lib/layout/list/source/FlyweightRepeater.js175
-rw-r--r--html/lib/layout/list/source/List.css15
-rw-r--r--html/lib/layout/list/source/List.js368
-rw-r--r--html/lib/layout/list/source/PulldownList.css60
-rw-r--r--html/lib/layout/list/source/PulldownList.js180
-rw-r--r--html/lib/layout/list/source/Selection.js141
-rw-r--r--html/lib/layout/list/source/package.js7
-rw-r--r--html/lib/layout/list/source/wip-package.js6
-rw-r--r--html/lib/layout/package.js7
-rw-r--r--html/lib/layout/panels/package.js3
-rw-r--r--html/lib/layout/panels/source/Panels.css12
-rw-r--r--html/lib/layout/panels/source/Panels.js386
-rw-r--r--html/lib/layout/panels/source/arrangers/Arranger.css29
-rw-r--r--html/lib/layout/panels/source/arrangers/Arranger.js226
-rw-r--r--html/lib/layout/panels/source/arrangers/CardArranger.js49
-rw-r--r--html/lib/layout/panels/source/arrangers/CardSlideInArranger.js62
-rw-r--r--html/lib/layout/panels/source/arrangers/CarouselArranger.js109
-rw-r--r--html/lib/layout/panels/source/arrangers/CollapsingArranger.js80
-rw-r--r--html/lib/layout/panels/source/arrangers/OtherArrangers.js202
-rw-r--r--html/lib/layout/panels/source/arrangers/package.js9
-rw-r--r--html/lib/layout/panels/source/package.js5
-rw-r--r--html/lib/layout/slideable/package.js3
-rw-r--r--html/lib/layout/slideable/source/Slideable.js312
-rw-r--r--html/lib/layout/slideable/source/package.js3
-rw-r--r--html/lib/layout/tree/package.js3
-rw-r--r--html/lib/layout/tree/source/Node.css28
-rw-r--r--html/lib/layout/tree/source/Node.js269
-rw-r--r--html/lib/layout/tree/source/package.js4
-rw-r--r--html/lib/onyx/design/inline-controlled.html230
-rw-r--r--html/lib/onyx/design/inline.html102
-rw-r--r--html/lib/onyx/design/menu-icon-bookmark.pngbin0 -> 1883 bytes
-rw-r--r--html/lib/onyx/design/toolbar-extended.html243
-rw-r--r--html/lib/onyx/design/toolbar.html110
-rw-r--r--html/lib/onyx/images/checkbox.pngbin0 -> 5462 bytes
-rw-r--r--html/lib/onyx/images/grabbutton.pngbin0 -> 676 bytes
-rw-r--r--html/lib/onyx/images/gradient-invert.pngbin0 -> 255 bytes
-rw-r--r--html/lib/onyx/images/gradient.pngbin0 -> 255 bytes
-rw-r--r--html/lib/onyx/images/more.pngbin0 -> 1220 bytes
-rw-r--r--html/lib/onyx/images/progress-button-cancel.pngbin0 -> 2631 bytes
-rw-r--r--html/lib/onyx/images/search-input-cancel.pngbin0 -> 303 bytes
-rw-r--r--html/lib/onyx/images/search-input-search.pngbin0 -> 447 bytes
-rw-r--r--html/lib/onyx/images/slider-handle.pngbin0 -> 3095 bytes
-rw-r--r--html/lib/onyx/images/spinner-dark.gifbin0 -> 1924 bytes
-rw-r--r--html/lib/onyx/images/spinner-light.gifbin0 -> 1924 bytes
-rw-r--r--html/lib/onyx/package.js8
-rw-r--r--html/lib/onyx/source/Button.js18
-rw-r--r--html/lib/onyx/source/Checkbox.js35
-rw-r--r--html/lib/onyx/source/Drawer.js75
-rw-r--r--html/lib/onyx/source/FlyweightPicker.js110
-rw-r--r--html/lib/onyx/source/Grabber.js19
-rw-r--r--html/lib/onyx/source/Groupbox.js37
-rw-r--r--html/lib/onyx/source/Icon.js31
-rw-r--r--html/lib/onyx/source/IconButton.js38
-rw-r--r--html/lib/onyx/source/Input.js20
-rw-r--r--html/lib/onyx/source/InputDecorator.js46
-rw-r--r--html/lib/onyx/source/Item.js60
-rw-r--r--html/lib/onyx/source/Menu.js130
-rw-r--r--html/lib/onyx/source/MenuDecorator.js60
-rw-r--r--html/lib/onyx/source/MenuItem.js41
-rw-r--r--html/lib/onyx/source/MoreToolbar.js141
-rw-r--r--html/lib/onyx/source/Picker.js87
-rw-r--r--html/lib/onyx/source/PickerButton.js16
-rw-r--r--html/lib/onyx/source/PickerDecorator.js30
-rw-r--r--html/lib/onyx/source/Popup.js87
-rw-r--r--html/lib/onyx/source/ProgressBar.js92
-rw-r--r--html/lib/onyx/source/ProgressButton.js28
-rw-r--r--html/lib/onyx/source/RadioButton.js8
-rw-r--r--html/lib/onyx/source/RadioGroup.js18
-rw-r--r--html/lib/onyx/source/RichText.js22
-rw-r--r--html/lib/onyx/source/Scrim.js93
-rw-r--r--html/lib/onyx/source/Slider.js109
-rw-r--r--html/lib/onyx/source/Spinner.js31
-rw-r--r--html/lib/onyx/source/SwipeableItem.js142
-rw-r--r--html/lib/onyx/source/TabPanels.js128
-rw-r--r--html/lib/onyx/source/TextArea.js19
-rw-r--r--html/lib/onyx/source/ToggleButton.js114
-rw-r--r--html/lib/onyx/source/Toolbar.js26
-rw-r--r--html/lib/onyx/source/Tooltip.js80
-rw-r--r--html/lib/onyx/source/TooltipDecorator.js44
-rw-r--r--html/lib/onyx/source/css/onyx.css896
-rw-r--r--html/lib/onyx/source/css/package.js3
-rw-r--r--html/lib/onyx/source/design.js66
-rw-r--r--html/lib/onyx/source/package.js37
-rw-r--r--html/lib/onyx/source/wip-package.js4
-rw-r--r--html/liogo.js111
-rw-r--r--html/package.js8
-rw-r--r--html/styles.css73
-rw-r--r--html/sugar.js46
-rw-r--r--images/python-logo.jpgbin0 -> 6577 bytes
-rw-r--r--setup.py22
128 files changed, 12591 insertions, 0 deletions
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..623b625
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/activity.py b/activity.py
new file mode 100644
index 0000000..54460df
--- /dev/null
+++ b/activity.py
@@ -0,0 +1,295 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Enyo activity: A case study for developing a HTML5 activity with Enyo Framework.
+# Lionel Laské inspired by Manuel Quiñones and Simon Schampijer
+
+
+from gi.repository import Gtk
+import logging
+import os
+
+from gettext import gettext as _
+
+from sugar3.activity import activity
+from sugar3.graphics.toolbarbox import ToolbarBox
+from sugar3.graphics.toolbutton import ToolButton
+from sugar3.activity.widgets import ActivityButton
+from sugar3.activity.widgets import TitleEntry
+from sugar3.activity.widgets import StopButton
+from sugar3.activity.widgets import ShareButton
+from sugar3.activity.widgets import DescriptionItem
+from sugar3.presence import presenceservice
+
+from gi.repository import WebKit
+import logging
+import gconf
+
+from datetime import date
+
+from enyo import Enyo
+
+
+class EnyoActivity(activity.Activity):
+ """EnyoActivity class as specified in activity.info"""
+
+ def __init__(self, handle):
+ """Set up the activity."""
+ activity.Activity.__init__(self, handle)
+
+ self.max_participants = 1
+
+ self.make_toolbar()
+ self.make_mainview()
+
+ class DummyObject:
+ """Dummy class used to demonstrated object transfer to JavaScript"""
+ name = "Lionel"
+ version = 2.0
+ modified = date.today()
+ language = ['Python', 'JavaScript']
+ def foo(self):
+ pass
+
+ def send_string(self, button):
+ """Click on send string button, send a simple string to JavaScript"""
+ self.enyo.send_message("helloFromPY", "Hello JavaScript !")
+
+ def send_object(self, button):
+ """Click on send string button, send a simple string to JavaScript"""
+ self.enyo.send_message("helloFromPY", self.DummyObject())
+
+ def alert(self, msg):
+ """Display a message dialog"""
+ messagedialog = Gtk.MessageDialog(self, type=1, buttons=1, message_format=msg)
+ messagedialog.run()
+ messagedialog.destroy()
+
+ def display_message(self, param):
+ """A message was received from JavaScript, display it"""
+ # Display as a JSON string to see structure
+ self.alert("Python received "+self.enyo.json_encode(param))
+
+ def go_home(self, button):
+ """Toolbar button home clicked, signal to JavaScript to update page count"""
+ self.enyo.send_message("home_clicked", 0)
+
+ def go_back(self, button):
+ """Toolbar button back clicked, signal to JavaScript to update page count"""
+ self.enyo.send_message("back_clicked", -1)
+
+ def go_forward(self, button):
+ """Toolbar button forward clicked, signal to JavaScript to update page count"""
+ self.enyo.send_message("forward_clicked", 1)
+
+ def checkbox_changed(self, button):
+ """Checked state changed, signal to JavaScript to update the matching control"""
+ self.enyo.send_message("py_checkbox_changed", self.checkbox.get_active())
+
+ def slider_changed(self, event):
+ """Slider changed, signal to JavaScript to update page count"""
+ self.enyo.send_message("py_slider_changed", self.adjustment.get_value())
+
+ def turtle_forward(self, button):
+ """Turtle forward button clicked, signal to JavaScript to update Canvas"""
+ self.enyo.send_message("liogo_forward_clicked", 60)
+
+ def turtle_left(self, button):
+ """Turtle left button clicked, signal to JavaScript to update Canvas"""
+ self.enyo.send_message("liogo_left_clicked", 90)
+
+ def turtle_right(self, button):
+ """Turtle right button clicked, signal to JavaScript to update Canvas"""
+ self.enyo.send_message("liogo_right_clicked", 90)
+
+ def turtle_clear(self, button):
+ """Turtle clear button clicked, signal to JavaScript to update Canvas"""
+ self.enyo.send_message("liogo_clear_clicked")
+
+ def jscript_slider_changed(self, args):
+ """Javascript slider changed, update the matching slider"""
+ self.adjustment.set_value(int(args))
+
+ def jscript_checkbox_changed(self, args):
+ """Javascript checkbox changed, update the matching checkbox"""
+ if args:
+ self.checkbox.set_active(True)
+ else:
+ self.checkbox.set_active(False)
+
+ def disable_back(self, args):
+ """Javascript request to change sensitive status of the toolbar back button"""
+ self.back_button.set_sensitive(args != "True")
+
+ def disable_forward(self, args):
+ """Javascript request to change sensitive status of the toolbar forward button"""
+ self.forward_button.set_sensitive(args != "True")
+
+ def init_context(self, args):
+ """Init Javascript context sending buddy information"""
+ # Get XO colors
+ buddy = {}
+ client = gconf.client_get_default()
+ colors = client.get_string("/desktop/sugar/user/color")
+ buddy["colors"] = colors.split(",")
+
+ # Get XO name
+ presenceService = presenceservice.get_instance()
+ buddy["name"] = presenceService.get_owner().props.nick
+
+ self.enyo.send_message("buddy", buddy)
+
+ def make_mainview(self):
+ """Create the activity view"""
+ # Create global box
+ vbox = Gtk.VBox(True)
+
+ # Create webview
+ scrolled_window = Gtk.ScrolledWindow()
+ self.webview = webview = WebKit.WebView()
+ scrolled_window.add(webview)
+ webview.show()
+ vbox.pack_start(scrolled_window, True, True, 0)
+ scrolled_window.show()
+
+ # Create python view
+ hbox = Gtk.VBox(False)
+
+ # Create image and label
+ ubox = Gtk.HBox(False)
+ image = Gtk.Image()
+ image.set_from_file("images/python-logo.jpg")
+ ubox.pack_start(image, False, False, 5)
+ image.show()
+ label = Gtk.Label("This the Sugar part of the activity. All content is handle by Python code.")
+ ubox.pack_start(label, False, False, 10)
+ label.show()
+ ubox.show()
+ hbox.pack_start(ubox, False, False, 10)
+
+ # Create send simple message buttons
+ ubox = Gtk.HBox(False)
+ button = Gtk.Button("Send string to JavaScript")
+ button.connect('clicked', self.send_string)
+ ubox.pack_start(button, False, False, 0)
+ button.show()
+ button = Gtk.Button("Send object to JavaScript")
+ button.connect('clicked', self.send_object)
+ ubox.pack_start(button, False, False, 10)
+ button.show()
+ ubox.show()
+ hbox.pack_start(ubox, False, False, 0)
+
+ # Create synchronized control
+ ubox = Gtk.HBox(False)
+ checkbox = self.checkbox = Gtk.CheckButton("Checkbox")
+ checkbox.set_active(True)
+ checkbox.connect('toggled', self.checkbox_changed)
+ ubox.pack_start(checkbox, False, False, 0)
+ checkbox.show()
+ adjustment = self.adjustment = Gtk.Adjustment(50, 0, 100, 1)
+ adjustment.connect('value-changed', self.slider_changed)
+ hscale = self.hscale = Gtk.HScale()
+ hscale.set_adjustment(adjustment)
+ hscale.set_size_request(250, 50)
+ hscale.set_digits(0)
+ ubox.pack_start(hscale, False, False, 20)
+ hscale.show()
+ ubox.show()
+ hbox.pack_start(ubox, False, False, 20)
+
+ # Create turtle commander
+ ubox = Gtk.HBox(False)
+ label = Gtk.Label("Turtle command:")
+ ubox.pack_start(label, False, False, 10)
+ label.show()
+ button = Gtk.Button("forward 60")
+ button.connect('clicked', self.turtle_forward)
+ ubox.pack_start(button, False, False, 10)
+ button.show()
+ button = Gtk.Button("left 90")
+ button.connect('clicked', self.turtle_left)
+ ubox.pack_start(button, False, False, 10)
+ button.show()
+ button = Gtk.Button("right 90")
+ button.connect('clicked', self.turtle_right)
+ ubox.pack_start(button, False, False, 10)
+ button.show()
+ button = Gtk.Button("clear")
+ button.connect('clicked', self.turtle_clear)
+ ubox.pack_start(button, False, False, 10)
+ button.show()
+ ubox.show()
+ hbox.pack_start(ubox, False, False, 20)
+
+ vbox.pack_start(hbox, False, False, 0)
+ hbox.show()
+
+ # Activate Enyo interface
+ self.enyo = Enyo(webview)
+ self.enyo.connect("ready", self.init_context)
+ self.enyo.connect("helloFromJS", self.display_message)
+ self.enyo.connect("disableBack", self.disable_back)
+ self.enyo.connect("disableForward", self.disable_forward)
+ self.enyo.connect("JSsliderChanged", self.jscript_slider_changed)
+ self.enyo.connect("JScheckboxChanged", self.jscript_checkbox_changed)
+
+ # Go to first page
+ web_app_page = os.path.join(activity.get_bundle_path(), "html/index.html")
+ self.webview.load_uri('file://' + web_app_page)
+
+ # Display all
+ self.set_canvas(vbox)
+ vbox.show()
+
+ def make_toolbar(self):
+ # toolbar with the new toolbar redesign
+ toolbar_box = ToolbarBox()
+
+ activity_button = ActivityButton(self)
+ toolbar_box.toolbar.insert(activity_button, 0)
+ activity_button.show()
+
+ home_button = ToolButton('go-home')
+ home_button.set_tooltip('Page count to 0')
+ home_button.connect('clicked', self.go_home)
+ toolbar_box.toolbar.insert(home_button, -1)
+ home_button.show()
+
+ title_entry = TitleEntry(self)
+ toolbar_box.toolbar.insert(title_entry, -1)
+ title_entry.show()
+
+ description_item = DescriptionItem(self)
+ toolbar_box.toolbar.insert(description_item, -1)
+ description_item.show()
+
+ back_button = self.back_button = ToolButton('go-previous-paired')
+ back_button.set_tooltip('Page count -1')
+ back_button.connect('clicked', self.go_back)
+ back_button.set_sensitive(False)
+ toolbar_box.toolbar.insert(back_button, -1)
+ back_button.show()
+
+ forward_button = self.forward_button = ToolButton('go-next-paired')
+ forward_button.set_tooltip('Page count +1')
+ forward_button.connect('clicked', self.go_forward)
+ toolbar_box.toolbar.insert(forward_button, -1)
+ forward_button.show()
+
+ share_button = ShareButton(self)
+ toolbar_box.toolbar.insert(share_button, -1)
+ share_button.show()
+
+ separator = Gtk.SeparatorToolItem()
+ separator.props.draw = False
+ separator.set_expand(True)
+ toolbar_box.toolbar.insert(separator, -1)
+ separator.show()
+
+ stop_button = StopButton(self)
+ toolbar_box.toolbar.insert(stop_button, -1)
+ stop_button.show()
+
+ self.set_toolbar_box(toolbar_box)
+ toolbar_box.show()
diff --git a/activity/activity-enyo.svg b/activity/activity-enyo.svg
new file mode 100644
index 0000000..3bfe0f4
--- /dev/null
+++ b/activity/activity-enyo.svg
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
+ <!ENTITY stroke_color "#666666">
+ <!ENTITY fill_color "#FFFFFF">
+]>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="54.8689"
+ height="55.000004"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.2 r9819"
+ sodipodi:docname="activity-enyo.svg">
+ <defs
+ id="defs4" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="3.9378078"
+ inkscape:cx="61.434798"
+ inkscape:cy="26.180673"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1280"
+ inkscape:window-height="737"
+ inkscape:window-x="-8"
+ inkscape:window-y="-8"
+ inkscape:window-maximized="1"
+ fit-margin-top="5"
+ fit-margin-left="7"
+ fit-margin-right="7"
+ fit-margin-bottom="5" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Calque 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-125.9662,-324.05782)">
+ <path
+ style="fill:#FFFFFF"
+ d="m 514.45072,857.60725 c -2.69854,-2.05979 -15.29854,-11.21623 -28,-20.34764 l -23.09357,-16.60257 -0.27903,-13.77165 c -0.25911,-12.78847 -0.15202,-13.73746 1.5,-13.29262 0.97847,0.26348 11.67903,6.99589 23.77903,14.96093 12.1,7.96504 24.50498,16.072 27.56663,18.01546 l 5.56663,3.53357 26.0206,-17.12027 c 28.90341,-19.01703 31.01732,-20.28203 32.09463,-19.20605 0.41333,0.41282 0.90792,6.51388 1.0991,13.5579 l 0.34758,12.80732 -28.62401,20.36027 c -15.7432,11.19816 -29.62461,20.47056 -30.84758,20.60535 -1.31126,0.14452 -4.23665,-1.2915 -7.13001,-3.5 z m 36.65643,-19.64896 27.75,-19.69684 -0.0153,-9.19964 c -0.0161,-9.64983 -0.81618,-12.50885 -3.12289,-11.15889 -0.74899,0.43833 -12.89446,8.37098 -26.98992,17.62811 -14.09548,9.25713 -26.34734,16.83115 -27.22637,16.83115 -1.66609,0 -14.15673,-7.79687 -40.92833,-25.54815 l -15.78282,-10.46501 0.28282,11.15467 0.28281,11.15467 23.09357,16.60191 c 12.70146,9.13105 25.29927,18.25296 27.99513,20.27091 2.69586,2.01795 5.35376,3.32137 5.90643,2.89648 0.55268,-0.4249 13.49237,-9.63611 28.75487,-20.46937 z m -50.44594,-30.37922 c -10.83276,-6.99429 -23.77026,-15.32427 -28.75,-18.51108 l -9.05406,-5.79418 0,-13.45582 c 0,-11.19112 0.24819,-13.45581 1.47462,-13.45581 1.21839,0 52.80362,29.48195 56.02538,32.01958 0.55818,0.43966 9.61548,-4.24525 20.5,-10.60366 10.725,-6.26522 20.0625,-11.39685 20.75,-11.40361 0.9409,-0.009 1.25,3.44034 1.25,13.95043 l 0,13.96274 -5.9768,4.05876 c -11.76325,7.98828 -34.37723,22.01515 -35.4402,21.98265 -0.59565,-0.0182 -9.94617,-5.75571 -20.77894,-12.75 z m 39.69594,-1.78722 c 9.9,-6.35952 18.57799,-12.12689 19.28443,-12.81637 0.92996,-0.90763 1.20594,-4.31233 1,-12.33682 l -0.28443,-11.08323 -18.5,10.86113 c -17.77121,10.43327 -21.47163,12.01207 -23.55,10.04775 -0.96087,-0.90815 -52.54014,-31.10213 -53.13062,-31.10213 -0.17566,0 -0.31938,4.93012 -0.31938,10.95581 l 0,10.95582 9.05274,5.79418 c 4.97901,3.18681 17.46651,11.26405 27.75,17.94943 10.28349,6.68538 19.14726,12.19617 19.69726,12.24621 0.55,0.05 9.1,-5.11226 19,-11.47178 z m -48.71681,-42.2602 -28.7168,-15.66947 -0.0332,-13.75 c -0.0301,-12.46363 0.13053,-13.74438 1.71681,-13.68989 0.9625,0.0331 14.10517,6.25423 29.20594,13.8248 l 27.45593,13.76469 28.34089,-13.8248 c 15.58749,-7.60364 28.78252,-13.8248 29.32229,-13.8248 0.6074,0 0.87536,5.18471 0.70317,13.6052 l -0.27822,13.6052 -28.5,15.8499 c -15.675,8.71745 -28.95,15.83387 -29.5,15.81427 -0.55,-0.0196 -13.92256,-7.08689 -29.71681,-15.7051 z m 57.93327,-2.56394 27.78354,-15.39447 0.28225,-11.05171 0.28225,-11.05171 -17.28225,8.30511 c -9.50524,4.56781 -21.88657,10.61859 -27.51408,13.44618 -5.62751,2.82759 -10.88989,5.14107 -11.69419,5.14107 -0.80429,0 -13.64609,-6.075 -28.53733,-13.5 -14.89123,-7.425 -27.29135,-13.5 -27.55581,-13.5 -0.26446,0 -0.46682,5.0625 -0.44968,11.25 l 0.0312,11.25 27.71884,15.14504 c 15.24536,8.32978 28.04125,15.19228 28.4353,15.25 0.39406,0.0577 13.21906,-6.82255 28.5,-15.28951 z"
+ id="path2996"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:&fill_color;;stroke:&stroke_color;"
+ d="m 150.83544,372.93566 c -0.93463,-0.65853 -5.29859,-3.58588 -9.6977,-6.50522 l -7.99837,-5.30792 -0.0966,-4.40286 c -0.0897,-4.08853 -0.0527,-4.39193 0.51952,-4.24971 0.33889,0.0842 4.04498,2.23662 8.23578,4.78308 4.19079,2.54645 8.49012,5.14 9.55406,5.76344 l 1.93446,1.13352 7.42157,-4.51813 c 10.55537,-6.42594 12.47163,-7.55533 12.81926,-7.55533 0.16923,0 0.30768,2.00826 0.30768,4.46277 l 0,4.46279 -9.88001,6.48709 c -5.43402,3.56791 -10.22659,6.52236 -10.65016,6.56545 -0.45414,0.0462 -1.46734,-0.41289 -2.46945,-1.11897 z"
+ id="path3762" />
+ <path
+ style="fill:&fill_color;;stroke:&stroke_color;"
+ d="m 163.53125,366.65381 9.61112,-6.29717 0,-3.51189 0,-3.5119 -0.77927,0.37795 c -0.42861,0.20788 -4.77358,2.80254 -9.65549,5.76592 -4.88192,2.96339 -9.12531,5.38798 -9.42975,5.38798 -0.57705,0 -4.90313,-2.49269 -14.17537,-8.16786 l -5.46633,-3.34569 0.0979,3.56618 0.0979,3.5662 7.99839,5.30771 c 4.3991,2.91923 8.7623,5.83555 9.69601,6.48069 0.9337,0.64514 1.85425,1.06186 2.04567,0.92603 0.19141,-0.13586 4.67303,-3.08072 9.95913,-6.54415 z"
+ id="path3760" />
+ <path
+ style="fill:&fill_color;;stroke:&stroke_color;"
+ d="m 146.0595,356.94144 c -3.7519,-2.23612 -8.23275,-4.89924 -9.95747,-5.91808 l -3.13583,-1.85242 0,-4.30188 c 0,-3.57785 0.086,-4.30188 0.51072,-4.30188 0.42199,0 18.28835,9.4255 19.40419,10.23679 0.19332,0.14056 3.33029,-1.35722 7.1001,-3.39004 3.71457,-2.00302 6.94857,-3.64361 7.18669,-3.64577 0.32587,-0.002 0.43293,1.09988 0.43293,4.46001 l 0,4.46394 -2.07005,1.29761 c -4.07415,2.55388 -11.90642,7.03833 -12.27457,7.02795 -0.20631,-0.006 -3.44482,-1.84013 -7.19671,-4.07623 z"
+ id="path3758" />
+ <path
+ style="fill:&fill_color;;stroke:&stroke_color;"
+ d="m 159.80804,356.37006 c 3.42882,-2.03317 6.43442,-3.87703 6.67909,-4.09746 0.32209,-0.29017 0.41768,-1.37866 0.34634,-3.94413 l -0.0985,-3.54335 -6.40741,3.47235 c -6.15499,3.33555 -7.43661,3.8403 -8.15645,3.2123 -0.33279,-0.29033 -18.19709,-9.94347 -18.40159,-9.94347 -0.0608,0 -0.11061,1.57617 -0.11061,3.5026 l 0,3.50263 3.13537,1.85242 c 1.72446,1.01884 6.04946,3.60117 9.61112,5.73851 3.56164,2.13735 6.63157,3.89918 6.82207,3.91518 0.19049,0.016 3.15175,-1.63442 6.58058,-3.66758 z"
+ id="path3756" />
+ <path
+ style="fill:&fill_color;;stroke:&stroke_color;"
+ d="m 142.93515,342.85929 -9.94596,-5.0096 -0.0115,-4.39593 c -0.0104,-3.98468 0.0452,-4.39414 0.59461,-4.37673 0.33335,0.0106 4.88527,1.99951 10.11537,4.41986 l 9.50926,4.40062 9.81576,-4.41984 c 5.39868,-2.43092 9.96873,-4.41985 10.15566,-4.41985 0.21038,0 0.30318,1.65757 0.24355,4.34965 l -0.0964,4.34963 -9.87088,5.06728 c -5.42897,2.787 -10.02672,5.06216 -10.2172,5.05589 -0.1905,-0.007 -4.82203,-2.26571 -10.29232,-5.02098 z"
+ id="path3754" />
+ <path
+ style="fill:&fill_color;;stroke:&stroke_color;"
+ d="m 163.00013,342.03959 9.62272,-4.92168 0.0978,-3.53329 0.0978,-3.53327 -5.98565,2.65518 c -3.29211,1.46034 -7.58033,3.39481 -9.5294,4.2988 -1.94907,0.90399 -3.77167,1.64362 -4.05024,1.64362 -0.27856,0 -4.72627,-1.94221 -9.88379,-4.31602 -5.15753,-2.37379 -9.45227,-4.31599 -9.54387,-4.31599 -0.0916,0 -0.16168,1.61849 -0.15574,3.59666 l 0.0109,3.59668 9.60033,4.84193 c 5.28017,2.66307 9.71198,4.85704 9.84845,4.8755 0.13649,0.0185 4.57839,-2.1812 9.87088,-4.88812 z"
+ id="path3009"
+ inkscape:connector-curvature="0" />
+ </g>
+</svg>
diff --git a/activity/activity.info b/activity/activity.info
new file mode 100644
index 0000000..556de7b
--- /dev/null
+++ b/activity/activity.info
@@ -0,0 +1,7 @@
+[Activity]
+name = Enyo
+activity_version = 1
+bundle_id = org.olpcfrance.Enyo
+exec = sugar-activity activity.EnyoActivity
+icon = activity-enyo
+license = GPLv2+
diff --git a/enyo.py b/enyo.py
new file mode 100644
index 0000000..473721a
--- /dev/null
+++ b/enyo.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# Sugar interface for Enyo JavaScript framework
+
+import logging
+import json
+import inspect
+
+
+class Enyo:
+ """Class to handle communication with Enyo"""
+
+ def __init__(self, webview):
+ """Constructor: link to the WebKit widget"""
+ self.webview = webview
+ self.handlers = {}
+ self.webview.connect("console-message", self._message_emitted)
+
+ def connect(self, name, callback):
+ """Add a new handler for an event"""
+ self.handlers[name] = callback
+
+ def send_message(self, name, args=None):
+ """Send a message to Enyo"""
+ script = "enyo.Sugar.sendMessage('"+name+"', "
+ if not args is None:
+ value = "'"+self.json_encode(args)+"'"
+ else:
+ value = "null"
+ script = script+value+")"
+ logging.warning("sugar://"+name+"/"+value)
+ return self.webview.execute_script(script)
+
+ def _message_emitted(self, widget, value, line, source):
+ """Raised when a message from Enyo has been received"""
+ # Only consider prefixed message
+ prefix = "enyo://"
+ if not value.startswith(prefix):
+ return False
+
+ # Get name
+ prefixlen = len(prefix)
+ size = value[prefixlen:prefixlen+value[prefixlen:].index("/")]
+ start = prefixlen+1+len(size)
+ name = value[start:start+int(size)]
+
+ # Get param
+ start = start + len(name)+1
+ size = value[start:start+value[start:].index("/")]
+ if int(size) == 0:
+ args = None
+ else:
+ start = start+1+len(size)
+ args = value[start:start+int(size)]
+
+ # Call handler if exist
+ logging.warning(value);
+ if name in self.handlers:
+ callback = self.handlers[name]
+ if args:
+ callback(json.loads(args))
+ else:
+ callback(None)
+
+ return True
+
+ def json_encode(self, obj):
+ """Encode object as a JSON string"""
+ try:
+ result = json.dumps(obj)
+ except TypeError:
+ result = "{"
+ first = True
+ for name in dir(obj):
+ value = getattr(obj, name)
+ if not name.startswith('__') and not inspect.ismethod(value) and not inspect.isroutine(value) and not inspect.isbuiltin(value) and not isinstance(value, obj.__class__):
+ if not first:
+ result = result + ', '
+ else:
+ first = False
+ result = result + '"'+name+'": '
+ result = result + self.json_encode(value)
+ result = result + "}"
+ return result
diff --git a/html/app.js b/html/app.js
new file mode 100644
index 0000000..9b1b927
--- /dev/null
+++ b/html/app.js
@@ -0,0 +1,205 @@
+// Enyo content used in a Sugar Activity
+// Show bi-directionnal communication between Sugar and Enyo
+
+enyo.kind({
+ name: "App",
+ classes: "onyx main-window",
+ components: [
+ // HTML5 logo and introduction text
+ {kind: "Image", classes: "logo", src: "images/html5-logo.jpg"},
+ {classes: "block-top", components: [
+ {content: "This is the HTML5 part of the activity.", classes:"hello-text"},
+ {content: "All content is handle by JavaScript code.", classes:"hello-line"}
+ ]},
+
+ // Welcome message using buddy setup
+ {classes: "divider", content: "Environment"},
+ {components: [
+ {content: "Hello", classes: "hello-text"},
+ {name: "buddyName", content: "NAME", classes: "hello-text hello-buddy"},
+ {content: "!", classes: "hello-text"},
+ {content: "(colors and name come from Sugar)", classes: "hello-advice"}
+ ]},
+
+ // Buttons to send basic messages
+ {classes: "divider", content: "Basic message"},
+ {kind: "onyx.Button", content: "Send string to Python", ontap: "buttonStringClicked"},
+ {kind: "onyx.Button", content: "Send object to Python", ontap: "buttonObjectClicked", classes: "spacer"},
+ {content: "(send a simple message with a string or an object)", classes: "hello-advice"},
+
+ // Counter affected by toolbar activity buttons
+ {classes: "divider", content: "Toolbar"},
+ {components: [
+ {content: "Page count:", classes: "hello-text"},
+ {name: "pageCount", classes: "hello-text hello-buddy"},
+ {content: "(use toolbar buttons to update page count)", classes: "hello-advice"}
+ ]},
+
+ // Right part of the screen
+ {classes: "right-part", components: [
+ // Controls synchronized with matching Python controls
+ {classes: "divider", content: "Synchronized controls"},
+ {name: "checkbox", kind: "onyx.Checkbox", checked: true, onchange: "checkboxClicked"},
+ {content: "Checkbox", classes: "hello-text spacer"},
+ {components: [
+ {kind: "onyx.InputDecorator", components: [
+ {name: "sliderValue", kind: "onyx.Input", classes: "slider-text", onchange: "valueChanged"}
+ ]},
+ {name: "sliderBar", kind: "onyx.Slider", classes: "slider-bar", onChange: "sliderChanged"}
+ ]},
+ {content: "(click to update Python counterpart at the same time)", classes: "hello-advice"},
+
+ // Canvas area
+ {classes: "divider", content: "Canvas"},
+ {kind: "Canvas", name: "canvas", classes: "canvas-style", attributes: {width: 200, height: 200}},
+ {content: "(use Python buttons to move the turtle)", classes: "hello-advice"},
+ {kind: "Image", id: "liogo", src:"images/liogo.png", classes: "liogo-logo", onload: "initLiogo" }
+ ]}
+ ],
+
+ // Constructor
+ create: function() {
+ this.inherited(arguments);
+
+ // Init sugar interface
+ this.sugar = new Sugar();
+
+ // Connect to python events
+ this.sugar.connect("helloFromPY", function(args) {
+ // Displayed as JSON string to see structure
+ alert("JavaScript received "+JSON.stringify(args));
+ });
+ this.sugar.connect("buddy", enyo.bind(this, "buddyChanged"));
+ this.sugar.connect("home_clicked", enyo.bind(this, "upgradePageCount"));
+ this.sugar.connect("back_clicked", enyo.bind(this, "upgradePageCount"));
+ this.sugar.connect("forward_clicked", enyo.bind(this, "upgradePageCount"));
+ this.sugar.connect("py_slider_changed", enyo.bind(this, "pySliderChanged"));
+ this.sugar.connect("py_checkbox_changed", enyo.bind(this, "pyCheckboxChanged"));
+ this.sugar.connect("liogo_forward_clicked", enyo.bind(this, "bForward"));
+ this.sugar.connect("liogo_left_clicked", enyo.bind(this, "bLeft"));
+ this.sugar.connect("liogo_right_clicked", enyo.bind(this, "bRight"));
+ this.sugar.connect("liogo_clear_clicked", enyo.bind(this, "bClear"));
+
+ // Send a message to python
+ this.sugar.sendMessage("ready")
+
+ // Init synchronized control
+ this.$.sliderBar.setValue(50);
+ this.$.sliderValue.setValue(50);
+ this.$.pageCount.setContent(1);
+
+ // Just to see that console function stay usable
+ console.log("Unhandled console message");
+ },
+
+ // Python sent buddy setup
+ buddyChanged: function(args) {
+ // Redraw buddy context
+ this.$.buddyName.applyStyle("background-color", args.colors[1]);
+ this.$.buddyName.applyStyle("color", args.colors[0]);
+ this.$.buddyName.setContent(args.name);
+ },
+
+ // Create Liogo component when the turtle image is loaded
+ initLiogo: function() {
+ // Init mini Logo component
+ this.liogo = new Liogo({canvas: this.$.canvas, image: document.getElementById("liogo") });
+ this.liogo.draw();
+ },
+
+ // Send a simple string message to Python
+ buttonStringClicked: function() {
+ this.sugar.sendMessage("helloFromJS", "Hello Python !");
+ },
+
+ // Send a dummy JavaScript object to Python
+ buttonObjectClicked: function() {
+ var person = { name: "Lionel", version: 2.0, language: ["Python", "JavaScript"] };
+ person.modified = new Date();
+ this.sugar.sendMessage("helloFromJS", person);
+ },
+
+ // Checkbox clicked, signal the changed to the matching Python control
+ checkboxClicked: function() {
+ this.sugar.sendMessage("JScheckboxChanged", this.$.checkbox.getChecked());
+ },
+
+ // Handle Python message coming from toolbar
+ upgradePageCount: function(args) {
+ // Process toolbar button click
+ var currentValue = parseInt(this.$.pageCount.getContent());
+ switch(args)
+ {
+ case 1:
+ currentValue++;
+ break;
+ case -1:
+ currentValue--;
+ break;
+ case 0:
+ currentValue = 1;
+ break;
+ }
+ this.$.pageCount.setContent(currentValue);
+
+ // Change toolbar button sensitivity depending of current page
+ var back = "False";
+ var forward = "False";
+ if (currentValue == 1)
+ back = "True"
+ else if (currentValue == 10)
+ forward = "True"
+ this.sugar.sendMessage("disableBack", back);
+ this.sugar.sendMessage("disableForward", forward);
+ },
+
+ // Slider changed, change the value and send a message to matching Python control
+ sliderChanged: function(inSender, inEvent) {
+ var newvalue = parseInt(this.$.sliderBar.getValue());
+ this.$.sliderValue.setValue(newvalue);
+ this.sugar.sendMessage("JSsliderChanged", newvalue);
+ },
+
+ // Slider value changed, change the slider and send a message to matching Python control
+ valueChanged: function(inSender, inEvent) {
+ var newvalue = parseInt(this.$.sliderValue.getValue());
+ this.$.sliderBar.setValue(newvalue);
+ this.sugar.sendMessage("JSsliderChanged", newvalue);
+ },
+
+ // Python slider value has changed, update the matching JavaScript control
+ pySliderChanged: function(args) {
+ var newvalue = parseInt(args);
+ this.$.sliderBar.setValue(newvalue);
+ this.$.sliderValue.setValue(newvalue);
+ },
+
+ // Python checkbox value has changed, update the matching JavaScript control
+ pyCheckboxChanged: function(args) {
+ this.$.checkbox.setChecked(args);
+ },
+
+ // Python forward turtle button clicked, process it in the mini Logo component
+ bForward: function(args) {
+ this.liogo.forward(args);
+ this.liogo.draw();
+ },
+
+ // Python left turtle button clicked, process it in the mini Logo component
+ bLeft: function(args) {
+ this.liogo.left(args);
+ this.liogo.draw();
+ },
+
+ // Python right turtle button clicked, process it in the mini Logo component
+ bRight: function(args) {
+ this.liogo.right(args);
+ this.liogo.draw();
+ },
+
+ // Python clear turtle button clicked, process it in the mini Logo component
+ bClear: function() {
+ this.liogo.clear();
+ this.liogo.draw();
+ }
+});
diff --git a/html/enyo/enyo.css b/html/enyo/enyo.css
new file mode 100644
index 0000000..09985fb
--- /dev/null
+++ b/html/enyo/enyo.css
@@ -0,0 +1,167 @@
+
+/* ../source/dom/dom.css */
+
+/* things we always want */
+body {
+ -webkit-overflow-scrolling: touch;
+ font-family: 'Helvetica Neue', 'Nimbus Sans L', Arial, sans-serif;
+}
+
+/* for apps */
+.enyo-document-fit {
+ margin: 0;
+ height: 100%;
+ /* note: giving html overflow: auto is odd and was only ever done to avoid duplication
+ however, android 4.04 sometimes does not hide nodes when their display is set to none
+ if document is overflow auto.
+ */
+ position: relative;
+}
+
+.enyo-body-fit {
+ margin: 0;
+ height: 100%;
+ /* helps prevent ios page scroll */
+ overflow: auto;
+ position: relative;
+}
+
+/* reset */
+
+button {
+ font-size: inherit;
+ font-family: inherit;
+}
+
+/* user selection */
+
+.enyo-unselectable {
+ cursor: default;
+ -webkit-user-select: none;
+ -moz-user-select: -moz-none;
+ user-select: none;
+}
+
+.enyo-unselectable::selection, .enyo-unselectable ::selection {
+ color: transparent;
+}
+
+.enyo-selectable {
+ cursor: auto;
+ -webkit-user-select: text;
+ -moz-user-select: text;
+ user-select: text;
+}
+
+.enyo-selectable::selection, .enyo-selectable ::selection {
+ background: #3297FD;
+ color: #FFF;
+}
+
+/* layout */
+
+body .enyo-fit {
+ position: absolute;
+ left: 0;
+ top: 0;
+ right: 0;
+ bottom: 0;
+}
+
+.enyo-clip {
+ overflow: hidden;
+}
+
+.enyo-border-box {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+/* compositing */
+
+.enyo-composite {
+ -webkit-transform: translateZ(0);
+ -moz-transform: translateZ(0);
+ -ms-transform: translateZ(0);
+ -o-transform: translateZ(0);
+ transform: translateZ(0);
+}
+
+
+/* ../source/touch/Thumb.css */
+
+.enyo-thumb {
+ position: absolute;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ border-radius: 4px;
+ background: #333;
+ border: 1px solid #666;
+ opacity: 0.75;
+ z-index: 1;
+}
+
+.enyo-vthumb {
+ top: 0;
+ right: 2px;
+ width: 4px;
+}
+
+.enyo-hthumb {
+ left: 0;
+ bottom: 2px;
+ height: 4px;
+}
+
+
+/* ../source/touch/Scroller.css */
+
+.enyo-scroller {
+ position: relative;
+}
+
+.enyo-fit.enyo-scroller {
+ position: absolute;
+}
+
+.enyo-touch-scroller {
+ overflow: hidden;
+}
+
+.enyo-touch-strategy-container {
+ overflow: hidden;
+}
+
+.enyo-scrollee-fit {
+ height: 100%;
+}
+
+/* ../source/ui/ui.css */
+
+.enyo-inline, .enyo-tool-decorator {
+ display: inline-block;
+}
+
+.enyo-children-inline > *, .enyo-tool-decorator > * {
+ display: inline-block;
+}
+
+.enyo-children-middle > *, .enyo-tool-decorator > * {
+ vertical-align: middle;
+}
+
+.enyo-positioned {
+ position: relative;
+}
+
+.enyo-fill {
+ position: relative;
+ width: 100%;
+ height: 100%;
+}
+
+.enyo-popup {
+ position: absolute;
+ z-index: 10;
+}
diff --git a/html/enyo/enyo.js b/html/enyo/enyo.js
new file mode 100644
index 0000000..f65fb62
--- /dev/null
+++ b/html/enyo/enyo.js
@@ -0,0 +1,3627 @@
+
+// enyo.js
+
+(function() {
+var a = "enyo.js";
+enyo = window.enyo || {}, enyo.locateScript = function(a) {
+var b = document.getElementsByTagName("script");
+for (var c = b.length - 1, d, e, f = a.length; c >= 0 && (d = b[c]); c--) if (!d.located) {
+e = d.getAttribute("src") || "";
+if (e.slice(-f) == a) return d.located = !0, {
+path: e.slice(0, Math.max(0, e.lastIndexOf("/"))),
+node: d
+};
+}
+}, enyo.args = enyo.args || {};
+var b = enyo.locateScript(a);
+if (b) {
+enyo.args.root = (enyo.args.root || b.path).replace("/source", "");
+for (var c = 0, d; d = b.node.attributes.item(c); c++) enyo.args[d.nodeName] = d.value;
+}
+})();
+
+// ../../loader.js
+
+(function() {
+enyo = window.enyo || {}, enyo.path = {
+paths: {},
+addPath: function(a, b) {
+return this.paths[a] = b;
+},
+addPaths: function(a) {
+if (a) for (var b in a) this.addPath(b, a[b]);
+},
+includeTrailingSlash: function(a) {
+return a && a.slice(-1) !== "/" ? a + "/" : a;
+},
+rewritePattern: /\$([^\/\\]*)(\/)?/g,
+rewrite: function(a) {
+var b, c = this.includeTrailingSlash, d = this.paths, e = function(a, e) {
+return b = !0, c(d[e]) || "";
+}, f = a;
+do b = !1, f = f.replace(this.rewritePattern, e); while (b);
+return f;
+}
+}, enyo.loaderFactory = function(a) {
+this.machine = a, this.packages = [], this.modules = [], this.sheets = [], this.stack = [];
+}, enyo.loaderFactory.prototype = {
+packageName: "",
+packageFolder: "",
+verbose: !1,
+loadScript: function(a) {
+this.machine.script(a);
+},
+loadSheet: function(a) {
+this.machine.sheet(a);
+},
+loadPackage: function(a) {
+this.machine.script(a);
+},
+report: function() {},
+load: function() {
+this.more({
+index: 0,
+depends: arguments || []
+});
+},
+more: function(a) {
+if (a && this.continueBlock(a)) return;
+var b = this.stack.pop();
+b ? (this.verbose && console.groupEnd("* finish package (" + (b.packageName || "anon") + ")"), this.packageFolder = b.folder, this.packageName = "", this.more(b)) : this.finish();
+},
+finish: function() {
+this.packageFolder = "", this.verbose && console.log("-------------- fini");
+},
+continueBlock: function(a) {
+while (a.index < a.depends.length) {
+var b = a.depends[a.index++];
+if (b) if (typeof b == "string") {
+if (this.require(b, a)) return !0;
+} else enyo.path.addPaths(b);
+}
+},
+require: function(a, b) {
+var c = enyo.path.rewrite(a), d = this.getPathPrefix(a);
+c = d + c;
+if (c.slice(-3) == "css") this.verbose && console.log("+ stylesheet: [" + d + "][" + a + "]"), this.requireStylesheet(c); else {
+if (c.slice(-2) != "js" || c.slice(-10) == "package.js") return this.requirePackage(c, b), !0;
+this.verbose && console.log("+ module: [" + d + "][" + a + "]"), this.requireScript(a, c);
+}
+},
+getPathPrefix: function(a) {
+var b = a.slice(0, 1);
+return b != "/" && b != "\\" && b != "$" && a.slice(0, 5) != "http:" ? this.packageFolder : "";
+},
+requireStylesheet: function(a) {
+this.sheets.push(a), this.loadSheet(a);
+},
+requireScript: function(a, b) {
+this.modules.push({
+packageName: this.packageName,
+rawPath: a,
+path: b
+}), this.loadScript(b);
+},
+decodePackagePath: function(a) {
+var b = "", c = "", d = "", e = "package.js", f = a.replace(/\\/g, "/").replace(/\/\//g, "/").replace(/:\//, "://").split("/");
+if (f.length) {
+var g = f.pop() || f.pop() || "";
+g.slice(-e.length) !== e ? f.push(g) : e = g, d = f.join("/"), d = d ? d + "/" : "", e = d + e;
+for (var h = f.length - 1; h >= 0; h--) if (f[h] == "source") {
+f.splice(h, 1);
+break;
+}
+c = f.join("/");
+for (var h = f.length - 1, i; i = f[h]; h--) if (i == "lib" || i == "enyo") {
+f = f.slice(h + 1);
+break;
+}
+for (var h = f.length - 1, i; i = f[h]; h--) (i == ".." || i == ".") && f.splice(h, 1);
+b = f.join("-");
+}
+return {
+alias: b,
+target: c,
+folder: d,
+manifest: e
+};
+},
+aliasPackage: function(a) {
+var b = this.decodePackagePath(a);
+this.manifest = b.manifest, b.alias && (enyo.path.addPath(b.alias, b.target), this.packageName = b.alias, this.packages.push({
+name: b.alias,
+folder: b.folder
+})), this.packageFolder = b.folder;
+},
+requirePackage: function(a, b) {
+b.folder = this.packageFolder, this.aliasPackage(a), b.packageName = this.packageName, this.stack.push(b), this.report("loading package", this.packageName), this.verbose && console.group("* start package [" + this.packageName + "]"), this.loadPackage(this.manifest);
+}
+};
+})();
+
+// boot.js
+
+enyo.machine = {
+sheet: function(a) {
+document.write('<link href="' + a + '" media="screen" rel="stylesheet" type="text/css" />');
+},
+script: function(a, b, c) {
+document.write('<script src="' + a + '"' + (b ? ' onload="' + b + '"' : "") + (c ? ' onerror="' + c + '"' : "") + "></scri" + "pt>");
+},
+inject: function(a) {
+document.write('<script type="text/javascript">' + a + "</script>");
+}
+}, enyo.loader = new enyo.loaderFactory(enyo.machine), enyo.depends = function() {
+var a = enyo.loader;
+if (!a.packageFolder) {
+var b = enyo.locateScript("package.js");
+b && b.path && (a.aliasPackage(b.path), a.packageFolder = b.path + "/");
+}
+a.load.apply(a, arguments);
+}, enyo.path.addPaths({
+enyo: enyo.args.root,
+lib: "$enyo/../lib"
+});
+
+// log.js
+
+enyo.logging = {
+level: 99,
+levels: {
+log: 20,
+warn: 10,
+error: 0
+},
+shouldLog: function(a) {
+var b = parseInt(this.levels[a], 0);
+return b <= this.level;
+},
+_log: function(a, b) {
+var c = enyo.isArray(b) ? b : enyo.cloneArray(b);
+enyo.dumbConsole && (c = [ c.join(" ") ]);
+var d = console[a];
+d && d.apply ? d.apply(console, c) : console.log.apply ? console.log.apply(console, c) : console.log(c.join(" "));
+},
+log: function(a, b) {
+window.console && this.shouldLog(a) && this._log(a, b);
+}
+}, enyo.setLogLevel = function(a) {
+var b = parseInt(a, 0);
+isFinite(b) && (enyo.logging.level = b);
+}, enyo.log = function() {
+enyo.logging.log("log", arguments);
+}, enyo.warn = function() {
+enyo.logging.log("warn", arguments);
+}, enyo.error = function() {
+enyo.logging.log("error", arguments);
+};
+
+// lang.js
+
+(function() {
+enyo.global = this, enyo._getProp = function(a, b, c) {
+var d = c || enyo.global;
+for (var e = 0, f; d && (f = a[e]); e++) d = f in d ? d[f] : b ? d[f] = {} : undefined;
+return d;
+}, enyo.setObject = function(a, b, c) {
+var d = a.split("."), e = d.pop(), f = enyo._getProp(d, !0, c);
+return f && e ? f[e] = b : undefined;
+}, enyo.getObject = function(a, b, c) {
+return enyo._getProp(a.split("."), b, c);
+}, enyo.irand = function(a) {
+return Math.floor(Math.random() * a);
+}, enyo.cap = function(a) {
+return a.slice(0, 1).toUpperCase() + a.slice(1);
+}, enyo.uncap = function(a) {
+return a.slice(0, 1).toLowerCase() + a.slice(1);
+}, enyo.format = function(a) {
+var b = /\%./g, c = 0, d = a, e = arguments, f = function(a) {
+return e[++c];
+};
+return d.replace(b, f);
+};
+var a = Object.prototype.toString;
+enyo.isString = function(b) {
+return a.call(b) === "[object String]";
+}, enyo.isFunction = function(b) {
+return a.call(b) === "[object Function]";
+}, enyo.isArray = Array.isArray || function(b) {
+return a.call(b) === "[object Array]";
+}, enyo.indexOf = function(a, b, c) {
+if (b.indexOf) return b.indexOf(a, c);
+if (c) {
+c < 0 && (c = 0);
+if (c > b.length) return -1;
+}
+for (var d = c || 0, e = b.length, f; (f = b[d]) || d < e; d++) if (f == a) return d;
+return -1;
+}, enyo.remove = function(a, b) {
+var c = enyo.indexOf(a, b);
+c >= 0 && b.splice(c, 1);
+}, enyo.forEach = function(a, b, c) {
+if (a) {
+var d = c || this;
+if (enyo.isArray(a) && a.forEach) a.forEach(b, d); else {
+var e = Object(a), f = e.length >>> 0;
+for (var g = 0; g < f; g++) g in e && b.call(d, e[g], g, e);
+}
+}
+}, enyo.map = function(a, b, c) {
+var d = c || this;
+if (enyo.isArray(a) && a.map) return a.map(b, d);
+var e = [], f = function(a, c, f) {
+e.push(b.call(d, a, c, f));
+};
+return enyo.forEach(a, f, d), e;
+}, enyo.filter = function(a, b, c) {
+var d = c || this;
+if (enyo.isArray(a) && a.filter) return a.filter(b, d);
+var e = [], f = function(a, c, f) {
+var g = a;
+b.call(d, a, c, f) && e.push(g);
+};
+return enyo.forEach(a, f, d), e;
+}, enyo.keys = Object.keys || function(a) {
+var b = [], c = Object.prototype.hasOwnProperty;
+for (var d in a) c.call(a, d) && b.push(d);
+if (!{
+toString: null
+}.propertyIsEnumerable("toString")) {
+var e = [ "toString", "toLocaleString", "valueOf", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "constructor" ];
+for (var f = 0, g; g = e[f]; f++) c.call(a, g) && b.push(g);
+}
+return b;
+}, enyo.cloneArray = function(a, b, c) {
+var d = c || [];
+for (var e = b || 0, f = a.length; e < f; e++) d.push(a[e]);
+return d;
+}, enyo.toArray = enyo.cloneArray, enyo.clone = function(a) {
+return enyo.isArray(a) ? enyo.cloneArray(a) : enyo.mixin({}, a);
+};
+var b = {};
+enyo.mixin = function(a, c) {
+a = a || {};
+if (c) {
+var d, e, f;
+for (d in c) e = c[d], b[d] !== e && (a[d] = e);
+}
+return a;
+}, enyo.bind = function(a, b) {
+b || (b = a, a = null), a = a || enyo.global;
+if (enyo.isString(b)) {
+if (!a[b]) throw [ 'enyo.bind: scope["', b, '"] is null (scope="', a, '")' ].join("");
+b = a[b];
+}
+if (enyo.isFunction(b)) {
+var c = enyo.cloneArray(arguments, 2);
+return b.bind ? b.bind.apply(b, [ a ].concat(c)) : function() {
+var d = enyo.cloneArray(arguments);
+return b.apply(a, c.concat(d));
+};
+}
+throw [ 'enyo.bind: scope["', b, '"] is not a function (scope="', a, '")' ].join("");
+}, enyo.asyncMethod = function(a, b) {
+return setTimeout(enyo.bind.apply(enyo, arguments), 1);
+}, enyo.call = function(a, b, c) {
+var d = a || this;
+if (b) {
+var e = d[b] || b;
+if (e && e.apply) return e.apply(d, c || []);
+}
+}, enyo.now = Date.now || function() {
+return (new Date).getTime();
+}, enyo.nop = function() {}, enyo.nob = {}, enyo.nar = [], enyo.instance = function() {}, enyo.setPrototype || (enyo.setPrototype = function(a, b) {
+a.prototype = b;
+}), enyo.delegate = function(a) {
+return enyo.setPrototype(enyo.instance, a), new enyo.instance;
+};
+})();
+
+// job.js
+
+enyo.job = function(a, b, c) {
+enyo.job.stop(a), enyo.job._jobs[a] = setTimeout(function() {
+enyo.job.stop(a), b();
+}, c);
+}, enyo.job.stop = function(a) {
+enyo.job._jobs[a] && (clearTimeout(enyo.job._jobs[a]), delete enyo.job._jobs[a]);
+}, enyo.job._jobs = {};
+
+// macroize.js
+
+enyo.macroize = function(a, b, c) {
+var d, e, f = a, g = c || enyo.macroize.pattern, h = function(a, c) {
+return d = enyo.getObject(c, !1, b), d === undefined || d === null ? "{$" + c + "}" : (e = !0, d);
+}, i = 0;
+do {
+e = !1, f = f.replace(g, h);
+if (++i >= 20) throw "enyo.macroize: recursion too deep";
+} while (e);
+return f;
+}, enyo.quickMacroize = function(a, b, c) {
+var d, e, f = a, g = c || enyo.macroize.pattern, h = function(a, c) {
+return c in b ? d = b[c] : d = enyo.getObject(c, !1, b), d === undefined || d === null ? "{$" + c + "}" : d;
+};
+return f = f.replace(g, h), f;
+}, enyo.macroize.pattern = /\{\$([^{}]*)\}/g;
+
+// animation.js
+
+(function() {
+var a = Math.round(1e3 / 60), b = [ "webkit", "moz", "ms", "o", "" ], c = "requestAnimationFrame", d = "cancel" + enyo.cap(c), e = function(b) {
+return window.setTimeout(b, a);
+}, f = function(a) {
+return window.clearTimeout(a);
+};
+for (var g = 0, h = b.length, i, j, k; (i = b[g]) || g < h; g++) {
+j = i ? i + enyo.cap(d) : d, k = i ? i + enyo.cap(c) : c;
+if (window[j]) {
+f = window[j], e = window[k], i == "webkit" && f(e(enyo.nop));
+break;
+}
+}
+enyo.requestAnimationFrame = function(a, b) {
+return e(a, b);
+}, enyo.cancelRequestAnimationFrame = function(a) {
+return f(a);
+};
+})(), enyo.easing = {
+cubicIn: function(a) {
+return Math.pow(a, 3);
+},
+cubicOut: function(a) {
+return Math.pow(a - 1, 3) + 1;
+},
+expoOut: function(a) {
+return a == 1 ? 1 : -1 * Math.pow(2, -10 * a) + 1;
+},
+quadInOut: function(a) {
+return a *= 2, a < 1 ? Math.pow(a, 2) / 2 : -1 * (--a * (a - 2) - 1) / 2;
+},
+linear: function(a) {
+return a;
+}
+}, enyo.easedLerp = function(a, b, c, d) {
+var e = (enyo.now() - a) / b;
+return d ? e >= 1 ? 0 : 1 - c(1 - e) : e >= 1 ? 1 : c(e);
+};
+
+// Oop.js
+
+enyo.kind = function(a) {
+enyo._kindCtors = {};
+var b = a.name || "";
+delete a.name;
+var c = "kind" in a, d = a.kind;
+delete a.kind;
+var e = enyo.constructorForKind(d), f = e && e.prototype || null;
+if (c && d !== null && e == null) throw "enyo.kind: Attempt to subclass an undefined kind. Check dependencies for [" + b + "].";
+var g = enyo.kind.makeCtor();
+return a.hasOwnProperty("constructor") && (a._constructor = a.constructor, delete a.constructor), enyo.setPrototype(g, f ? enyo.delegate(f) : {}), enyo.mixin(g.prototype, a), g.prototype.kindName = b, g.prototype.base = e, g.prototype.ctor = g, enyo.forEach(enyo.kind.features, function(b) {
+b(g, a);
+}), enyo.setObject(b, g), g;
+}, enyo.kind.makeCtor = function() {
+return function() {
+var a;
+this._constructor && (a = this._constructor.apply(this, arguments)), this.constructed && this.constructed.apply(this, arguments);
+if (a) return a;
+};
+}, enyo.kind.defaultNamespace = "enyo", enyo.kind.features = [], enyo.kind.features.push(function(a, b) {
+var c = a.prototype;
+c.inherited || (c.inherited = enyo.kind.inherited);
+if (c.base) for (var d in b) {
+var e = b[d];
+enyo.isFunction(e) && (e._inherited = c.base.prototype[d] || enyo.nop, e.nom = c.kindName + "." + d + "()");
+}
+}), enyo.kind.inherited = function(a, b) {
+return a.callee._inherited.apply(this, b || a);
+}, enyo.kind.features.push(function(a, b) {
+enyo.mixin(a, enyo.kind.statics), b.statics && (enyo.mixin(a, b.statics), delete a.prototype.statics);
+var c = a.prototype.base;
+while (c) c.subclass(a, b), c = c.prototype.base;
+}), enyo.kind.statics = {
+subclass: function(a, b) {},
+extend: function(a) {
+enyo.mixin(this.prototype, a);
+var b = this;
+enyo.forEach(enyo.kind.features, function(c) {
+c(b, a);
+});
+}
+}, enyo._kindCtors = {}, enyo.constructorForKind = function(a) {
+if (a === null || enyo.isFunction(a)) return a;
+if (a) {
+var b = enyo._kindCtors[a];
+return b ? b : enyo._kindCtors[a] = enyo.Theme[a] || enyo[a] || enyo.getObject(a, !1, enyo) || window[a] || enyo.getObject(a);
+}
+return enyo.defaultCtor;
+}, enyo.Theme = {}, enyo.registerTheme = function(a) {
+enyo.mixin(enyo.Theme, a);
+};
+
+// Object.js
+
+enyo.kind({
+name: "enyo.Object",
+kind: null,
+constructor: function() {
+enyo._objectCount++;
+},
+setPropertyValue: function(a, b, c) {
+if (this[c]) {
+var d = this[a];
+this[a] = b, this[c](d);
+} else this[a] = b;
+},
+_setProperty: function(a, b, c) {
+this.setPropertyValue(a, b, this.getProperty(a) !== b && c);
+},
+destroyObject: function(a) {
+this[a] && this[a].destroy && this[a].destroy(), this[a] = null;
+},
+getProperty: function(a) {
+var b = "get" + enyo.cap(a);
+return this[b] ? this[b]() : this[a];
+},
+setProperty: function(a, b) {
+var c = "set" + enyo.cap(a);
+this[c] ? this[c](b) : this._setProperty(a, b, a + "Changed");
+},
+log: function() {
+var a = arguments.callee.caller, b = ((a ? a.nom : "") || "(instance method)") + ":";
+enyo.logging.log("log", [ b ].concat(enyo.cloneArray(arguments)));
+},
+warn: function() {
+this._log("warn", arguments);
+},
+error: function() {
+this._log("error", arguments);
+},
+_log: function(a, b) {
+if (enyo.logging.shouldLog(a)) try {
+throw new Error;
+} catch (c) {
+enyo.logging._log(a, [ b.callee.caller.nom + ": " ].concat(enyo.cloneArray(b))), console.log(c.stack);
+}
+}
+}), enyo._objectCount = 0, enyo.Object.subclass = function(a, b) {
+this.publish(a, b);
+}, enyo.Object.publish = function(a, b) {
+var c = b.published;
+if (c) {
+var d = a.prototype;
+for (var e in c) enyo.Object.addGetterSetter(e, c[e], d);
+}
+}, enyo.Object.addGetterSetter = function(a, b, c) {
+var d = a;
+c[d] = b;
+var e = enyo.cap(d), f = "get" + e;
+c[f] || (c[f] = function() {
+return this[d];
+});
+var g = "set" + e, h = d + "Changed";
+c[g] || (c[g] = function(a) {
+this._setProperty(d, a, h);
+});
+};
+
+// Component.js
+
+enyo.kind({
+name: "enyo.Component",
+kind: enyo.Object,
+published: {
+name: "",
+id: "",
+owner: null
+},
+statics: {
+_kindPrefixi: {}
+},
+defaultKind: "Component",
+handlers: {},
+toString: function() {
+return this.kindName;
+},
+constructor: function() {
+this._componentNameMap = {}, this.$ = {}, this.inherited(arguments);
+},
+constructed: function(a) {
+this.importProps(a), this.create();
+},
+importProps: function(a) {
+if (a) for (var b in a) this[b] = a[b];
+this.handlers = enyo.mixin(enyo.clone(this.kindHandlers), this.handlers);
+},
+create: function() {
+this.ownerChanged(), this.initComponents();
+},
+initComponents: function() {
+this.createChrome(this.kindComponents), this.createClientComponents(this.components);
+},
+createChrome: function(a) {
+this.createComponents(a, {
+isChrome: !0
+});
+},
+createClientComponents: function(a) {
+this.createComponents(a, {
+owner: this.getInstanceOwner()
+});
+},
+getInstanceOwner: function() {
+return !this.owner || this.owner.notInstanceOwner ? this : this.owner;
+},
+destroy: function() {
+this.destroyComponents(), this.setOwner(null), this.destroyed = !0;
+},
+destroyComponents: function() {
+enyo.forEach(this.getComponents(), function(a) {
+a.destroyed || a.destroy();
+});
+},
+makeId: function() {
+var a = "_", b = this.owner && this.owner.getId();
+return this.name ? (b ? b + a : "") + this.name : "";
+},
+ownerChanged: function(a) {
+a && a.removeComponent(this), this.owner && this.owner.addComponent(this), this.id || (this.id = this.makeId());
+},
+nameComponent: function(a) {
+var b = enyo.Component.prefixFromKindName(a.kindName), c, d = this._componentNameMap[b] || 0;
+do c = b + (++d > 1 ? String(d) : ""); while (this.$[c]);
+return this._componentNameMap[b] = Number(d), a.name = c;
+},
+addComponent: function(a) {
+var b = a.getName();
+b || (b = this.nameComponent(a)), this.$[b] && this.warn('Duplicate component name "' + b + '" in owner "' + this.id + '" violates unique-name-under-owner rule, replacing existing component in the hash and continuing, but this is an error condition and should be fixed.'), this.$[b] = a;
+},
+removeComponent: function(a) {
+delete this.$[a.getName()];
+},
+getComponents: function() {
+var a = [];
+for (var b in this.$) a.push(this.$[b]);
+return a;
+},
+adjustComponentProps: function(a) {
+this.defaultProps && enyo.mixin(a, this.defaultProps), a.kind = a.kind || a.isa || this.defaultKind, a.owner = a.owner || this;
+},
+_createComponent: function(a, b) {
+var c = enyo.mixin(enyo.clone(b), a);
+return this.adjustComponentProps(c), enyo.Component.create(c);
+},
+createComponent: function(a, b) {
+return this._createComponent(a, b);
+},
+createComponents: function(a, b) {
+if (a) {
+var c = [];
+for (var d = 0, e; e = a[d]; d++) c.push(this._createComponent(e, b));
+return c;
+}
+},
+getBubbleTarget: function() {
+return this.owner;
+},
+bubble: function(a, b, c) {
+var d = b || {};
+return "originator" in d || (d.originator = c || this), this.dispatchBubble(a, d, c);
+},
+dispatchBubble: function(a, b, c) {
+return this.dispatchEvent(a, b, c) ? !0 : this.bubbleUp(a, b, c);
+},
+bubbleUp: function(a, b, c) {
+var d = this.getBubbleTarget();
+return d ? d.dispatchBubble(a, b, this) : !1;
+},
+dispatchEvent: function(a, b, c) {
+this.decorateEvent(a, b, c);
+if (this.handlers[a] && this.dispatch(this.handlers[a], b, c)) return !0;
+if (this[a]) return this.bubbleDelegation(this.owner, this[a], a, b, this);
+},
+decorateEvent: function(a, b, c) {},
+bubbleDelegation: function(a, b, c, d, e) {
+var f = this.getBubbleTarget();
+if (f) return f.delegateEvent(a, b, c, d, e);
+},
+delegateEvent: function(a, b, c, d, e) {
+return this.decorateEvent(c, d, e), a == this ? this.dispatch(b, d, e) : this.bubbleDelegation(a, b, c, d, e);
+},
+dispatch: function(a, b, c) {
+var d = a && this[a];
+if (d) return d.call(this, c || this, b);
+},
+waterfall: function(a, b, c) {
+if (this.dispatchEvent(a, b, c)) return !0;
+this.waterfallDown(a, b, c || this);
+},
+waterfallDown: function(a, b, c) {
+for (var d in this.$) this.$[d].waterfall(a, b, c);
+}
+}), enyo.defaultCtor = enyo.Component, enyo.create = enyo.Component.create = function(a) {
+if (!a.kind && "kind" in a) throw "enyo.create: Attempt to create a null kind. Check dependencies.";
+var b = a.kind || a.isa || enyo.defaultCtor, c = enyo.constructorForKind(b);
+return c || (console.error('no constructor found for kind "' + b + '"'), c = enyo.Component), new c(a);
+}, enyo.Component.subclass = function(a, b) {
+var c = a.prototype;
+b.components && (c.kindComponents = b.components, delete c.components);
+if (b.handlers) {
+var d = c.kindHandlers;
+c.kindHandlers = enyo.mixin(enyo.clone(d), c.handlers), c.handlers = null;
+}
+b.events && this.publishEvents(a, b);
+}, enyo.Component.publishEvents = function(a, b) {
+var c = b.events;
+if (c) {
+var d = a.prototype;
+for (var e in c) this.addEvent(e, c[e], d);
+}
+}, enyo.Component.addEvent = function(a, b, c) {
+var d, e;
+enyo.isString(b) ? (a.slice(0, 2) != "on" && (console.warn("enyo.Component.addEvent: event names must start with 'on'. " + c.kindName + " event '" + a + "' was auto-corrected to 'on" + a + "'."), a = "on" + a), d = b, e = "do" + enyo.cap(a.slice(2))) : (d = b.value, e = b.caller), c[a] = d, c[e] || (c[e] = function(b) {
+return this.bubble(a, b);
+});
+}, enyo.Component.prefixFromKindName = function(a) {
+var b = enyo.Component._kindPrefixi[a];
+if (!b) {
+var c = a.lastIndexOf(".");
+b = c >= 0 ? a.slice(c + 1) : a, b = b.charAt(0).toLowerCase() + b.slice(1), enyo.Component._kindPrefixi[a] = b;
+}
+return b;
+};
+
+// UiComponent.js
+
+enyo.kind({
+name: "enyo.UiComponent",
+kind: enyo.Component,
+published: {
+container: null,
+parent: null,
+controlParentName: "client",
+layoutKind: ""
+},
+handlers: {
+onresize: "resizeHandler"
+},
+statics: {
+_resizeFlags: {
+showingOnly: !0
+}
+},
+create: function() {
+this.controls = [], this.children = [], this.containerChanged(), this.inherited(arguments), this.layoutKindChanged();
+},
+destroy: function() {
+this.destroyClientControls(), this.setContainer(null), this.inherited(arguments);
+},
+importProps: function(a) {
+this.inherited(arguments), this.owner || (this.owner = enyo.master);
+},
+createComponents: function() {
+var a = this.inherited(arguments);
+return this.discoverControlParent(), a;
+},
+discoverControlParent: function() {
+this.controlParent = this.$[this.controlParentName] || this.controlParent;
+},
+adjustComponentProps: function(a) {
+a.container = a.container || this, this.inherited(arguments);
+},
+containerChanged: function(a) {
+a && a.removeControl(this), this.container && this.container.addControl(this);
+},
+parentChanged: function(a) {
+a && a != this.parent && a.removeChild(this);
+},
+isDescendantOf: function(a) {
+var b = this;
+while (b && b != a) b = b.parent;
+return a && b == a;
+},
+getControls: function() {
+return this.controls;
+},
+getClientControls: function() {
+var a = [];
+for (var b = 0, c = this.controls, d; d = c[b]; b++) d.isChrome || a.push(d);
+return a;
+},
+destroyClientControls: function() {
+var a = this.getClientControls();
+for (var b = 0, c; c = a[b]; b++) c.destroy();
+},
+addControl: function(a) {
+this.controls.push(a), this.addChild(a);
+},
+removeControl: function(a) {
+return a.setParent(null), enyo.remove(a, this.controls);
+},
+indexOfControl: function(a) {
+return enyo.indexOf(a, this.controls);
+},
+indexOfClientControl: function(a) {
+return enyo.indexOf(a, this.getClientControls());
+},
+indexInContainer: function() {
+return this.container.indexOfControl(this);
+},
+clientIndexInContainer: function() {
+return this.container.indexOfClientControl(this);
+},
+controlAtIndex: function(a) {
+return this.controls[a];
+},
+addChild: function(a) {
+this.controlParent ? this.controlParent.addChild(a) : (a.setParent(this), this.children[this.prepend ? "unshift" : "push"](a));
+},
+removeChild: function(a) {
+return enyo.remove(a, this.children);
+},
+indexOfChild: function(a) {
+return enyo.indexOf(a, this.children);
+},
+layoutKindChanged: function() {
+this.layout && this.layout.destroy(), this.layout = enyo.createFromKind(this.layoutKind, this), this.generated && this.render();
+},
+flow: function() {
+this.layout && this.layout.flow();
+},
+reflow: function() {
+this.layout && this.layout.reflow();
+},
+resized: function() {
+this.waterfall("onresize", enyo.UiComponent._resizeFlags), this.waterfall("onpostresize", enyo.UiComponent._resizeFlags);
+},
+resizeHandler: function() {
+this.reflow();
+},
+waterfallDown: function(a, b, c) {
+for (var d in this.$) this.$[d] instanceof enyo.UiComponent || this.$[d].waterfall(a, b, c);
+for (var e = 0, f = this.children, g; g = f[e]; e++) (g.showing || !b || !b.showingOnly) && g.waterfall(a, b, c);
+},
+getBubbleTarget: function() {
+return this.parent;
+}
+}), enyo.createFromKind = function(a, b) {
+var c = a && enyo.constructorForKind(a);
+if (c) return new c(b);
+}, enyo.master = new enyo.Component({
+name: "master",
+notInstanceOwner: !0,
+eventFlags: {
+showingOnly: !0
+},
+getId: function() {
+return "";
+},
+isDescendantOf: enyo.nop,
+bubble: function(a, b, c) {
+a == "onresize" ? (enyo.master.waterfallDown("onresize", this.eventFlags), enyo.master.waterfallDown("onpostresize", this.eventFlags)) : enyo.Signals.send(a, b);
+}
+});
+
+// Layout.js
+
+enyo.kind({
+name: "enyo.Layout",
+kind: null,
+layoutClass: "",
+constructor: function(a) {
+this.container = a, a && a.addClass(this.layoutClass);
+},
+destroy: function() {
+this.container && this.container.removeClass(this.layoutClass);
+},
+flow: function() {},
+reflow: function() {}
+});
+
+// Signals.js
+
+enyo.kind({
+name: "enyo.Signals",
+kind: enyo.Component,
+create: function() {
+this.inherited(arguments), enyo.Signals.addListener(this);
+},
+destroy: function() {
+enyo.Signals.removeListener(this), this.inherited(arguments);
+},
+notify: function(a, b) {
+this.dispatchEvent(a, b);
+},
+statics: {
+listeners: [],
+addListener: function(a) {
+this.listeners.push(a);
+},
+removeListener: function(a) {
+enyo.remove(a, this.listeners);
+},
+send: function(a, b) {
+enyo.forEach(this.listeners, function(c) {
+c.notify(a, b);
+});
+}
+}
+});
+
+// Async.js
+
+enyo.kind({
+name: "enyo.Async",
+kind: enyo.Object,
+failed: !1,
+context: null,
+constructor: function() {
+this.responders = [], this.errorHandlers = [];
+},
+accumulate: function(a, b) {
+var c = b.length < 2 ? b[0] : enyo.bind(b[0], b[1]);
+a.push(c);
+},
+response: function() {
+return this.accumulate(this.responders, arguments), this;
+},
+error: function() {
+return this.accumulate(this.errorHandlers, arguments), this;
+},
+route: function(a, b) {
+var c = enyo.bind(this, "respond");
+a.response(function(a, b) {
+c(b);
+});
+var d = enyo.bind(this, "fail");
+a.error(function(a, b) {
+d(b);
+}), a.go(b);
+},
+handle: function(a, b) {
+var c = b.shift();
+if (c) if (c instanceof enyo.Async) this.route(c, a); else {
+var d = enyo.call(this.context || this, c, [ this, a ]);
+d = d !== undefined ? d : a, (this.failed ? this.fail : this.respond).call(this, d);
+}
+},
+startTimer: function() {
+this.startTime = enyo.now(), this.timeout && (this.timeoutJob = setTimeout(enyo.bind(this, "timeoutComplete"), this.timeout));
+},
+endTimer: function() {
+this.timeoutJob && (this.endTime = enyo.now(), clearTimeout(this.timeoutJob), this.timeoutJob = null, this.latency = this.endTime - this.startTime);
+},
+timeoutComplete: function() {
+this.timedout = !0, this.fail("timeout");
+},
+respond: function(a) {
+this.failed = !1, this.endTimer(), this.handle(a, this.responders);
+},
+fail: function(a) {
+this.failed = !0, this.endTimer(), this.handle(a, this.errorHandlers);
+},
+recover: function() {
+this.failed = !1;
+},
+go: function(a) {
+return enyo.asyncMethod(this, function() {
+this.respond(a);
+}), this;
+}
+});
+
+// json.js
+
+enyo.json = {
+stringify: function(a, b, c) {
+return JSON.stringify(a, b, c);
+},
+parse: function(a, b) {
+return a ? JSON.parse(a, b) : null;
+}
+};
+
+// cookie.js
+
+enyo.getCookie = function(a) {
+var b = document.cookie.match(new RegExp("(?:^|; )" + a + "=([^;]*)"));
+return b ? decodeURIComponent(b[1]) : undefined;
+}, enyo.setCookie = function(a, b, c) {
+var d = a + "=" + encodeURIComponent(b), e = c || {}, f = e.expires;
+if (typeof f == "number") {
+var g = new Date;
+g.setTime(g.getTime() + f * 24 * 60 * 60 * 1e3), f = g;
+}
+f && f.toUTCString && (e.expires = f.toUTCString());
+var h, i;
+for (h in e) d += "; " + h, i = e[h], i !== !0 && (d += "=" + i);
+document.cookie = d;
+};
+
+// xhr.js
+
+enyo.xhr = {
+request: function(a) {
+var b = this.getXMLHttpRequest(), c = a.method || "GET", d = "sync" in a ? !a.sync : !0;
+a.username ? b.open(c, enyo.path.rewrite(a.url), d, a.username, a.password) : b.open(c, enyo.path.rewrite(a.url), d), enyo.mixin(b, a.xhrFields), this.makeReadyStateHandler(b, a.callback);
+if (a.headers) for (var e in a.headers) b.setRequestHeader(e, a.headers[e]);
+return b.send(a.body || null), d || b.onreadystatechange(b), b;
+},
+makeReadyStateHandler: function(a, b) {
+a.onreadystatechange = function() {
+a.readyState == 4 && b && b.apply(null, [ a.responseText, a ]);
+};
+},
+getXMLHttpRequest: function() {
+try {
+return new XMLHttpRequest;
+} catch (a) {}
+try {
+return new ActiveXObject("Msxml2.XMLHTTP");
+} catch (a) {}
+try {
+return new ActiveXObject("Microsoft.XMLHTTP");
+} catch (a) {}
+return null;
+}
+};
+
+// AjaxProperties.js
+
+enyo.AjaxProperties = {
+cacheBust: !0,
+url: "",
+method: "GET",
+handleAs: "json",
+contentType: "application/x-www-form-urlencoded",
+sync: !1,
+headers: null,
+postBody: "",
+username: "",
+password: "",
+xhrFields: null
+};
+
+// Ajax.js
+
+enyo.kind({
+name: "enyo.Ajax",
+kind: enyo.Async,
+published: enyo.AjaxProperties,
+constructor: function(a) {
+enyo.mixin(this, a), this.inherited(arguments);
+},
+go: function(a) {
+return this.startTimer(), this.request(a), this;
+},
+request: function(a) {
+var b = this.url.split("?"), c = b.shift() || "", d = b.join("?").split("&"), e = enyo.isString(a) ? a : enyo.Ajax.objectToQuery(a);
+this.method == "GET" && (e && (d.push(e), e = null), this.cacheBust && d.push(Math.random()));
+var f = [ c, d.join("&") ].join("?"), g = {
+"Content-Type": this.contentType
+};
+enyo.mixin(g, this.headers), this.xhr = enyo.xhr.request({
+url: f,
+method: this.method,
+callback: enyo.bind(this, "receive"),
+body: this.postBody || e,
+headers: g,
+sync: window.PalmSystem ? !1 : this.sync,
+username: this.username,
+password: this.password,
+xhrFields: this.xhrFields
+});
+},
+receive: function(a, b) {
+this.destroyed || (this.isFailure(b) ? this.fail(b.status) : this.respond(this.xhrToResponse(b)));
+},
+xhrToResponse: function(a) {
+if (a) return this[(this.handleAs || "text") + "Handler"](a);
+},
+isFailure: function(a) {
+return a.status !== 0 && (a.status < 200 || a.status >= 300);
+},
+xmlHandler: function(a) {
+return a.responseXML;
+},
+textHandler: function(a) {
+return a.responseText;
+},
+jsonHandler: function(a) {
+var b = a.responseText;
+try {
+return b && enyo.json.parse(b);
+} catch (c) {
+return console.warn("Ajax request set to handleAs JSON but data was not in JSON format"), b;
+}
+},
+statics: {
+objectToQuery: function(a) {
+var b = encodeURIComponent, c = [], d = {};
+for (var e in a) {
+var f = a[e];
+if (f != d[e]) {
+var g = b(e) + "=";
+if (enyo.isArray(f)) for (var h = 0; h < f.length; h++) c.push(g + b(f[h])); else c.push(g + b(f));
+}
+}
+return c.join("&");
+}
+}
+});
+
+// Jsonp.js
+
+enyo.kind({
+name: "enyo.JsonpRequest",
+kind: enyo.Async,
+published: {
+url: "",
+charset: null,
+callbackName: "callback",
+cacheBust: !0
+},
+statics: {
+nextCallbackID: 0
+},
+addScriptElement: function() {
+var a = document.createElement("script");
+a.src = this.src, a.async = "async", this.charset && (a.charset = this.charset), a.onerror = enyo.bind(this, function() {
+this.fail(400), this.removeScriptElement();
+});
+var b = document.getElementsByTagName("script")[0];
+b.parentNode.insertBefore(a, b), this.scriptTag = a;
+},
+removeScriptElement: function() {
+var a = this.scriptTag;
+this.scriptTag = null, a.onerror = null, a.parentNode && a.parentNode.removeChild(a);
+},
+constructor: function(a) {
+enyo.mixin(this, a), this.inherited(arguments);
+},
+go: function(a) {
+return this.startTimer(), this.jsonp(a), this;
+},
+jsonp: function(a) {
+var b = "enyo_jsonp_callback_" + enyo.JsonpRequest.nextCallbackID++;
+this.src = this.buildUrl(a, b), this.addScriptElement(), window[b] = enyo.bind(this, this.respond);
+var c = enyo.bind(this, function() {
+this.removeScriptElement(), window[b] = null;
+});
+this.response(c), this.error(c);
+},
+buildUrl: function(a, b) {
+var c = this.url.split("?"), d = c.shift() || "", e = c.join("?").split("&"), f = this.bodyArgsFromParams(a, b);
+return e.push(f), this.cacheBust && e.push(Math.random()), [ d, e.join("&") ].join("?");
+},
+bodyArgsFromParams: function(a, b) {
+if (enyo.isString(a)) return a.replace("=?", "=" + b);
+var c = enyo.mixin({}, a);
+return c[this.callbackName] = b, enyo.Ajax.objectToQuery(c);
+}
+});
+
+// WebService.js
+
+enyo.kind({
+name: "enyo._AjaxComponent",
+kind: enyo.Component,
+published: enyo.AjaxProperties
+}), enyo.kind({
+name: "enyo.WebService",
+kind: enyo._AjaxComponent,
+published: {
+jsonp: !1,
+callbackName: "callback",
+charset: null
+},
+events: {
+onResponse: "",
+onError: ""
+},
+constructor: function(a) {
+this.inherited(arguments);
+},
+send: function(a) {
+return this.jsonp ? this.sendJsonp(a) : this.sendAjax(a);
+},
+sendJsonp: function(a) {
+var b = new enyo.JsonpRequest;
+for (var c in {
+url: 1,
+callbackName: 1,
+charset: 1
+}) b[c] = this[c];
+return this.sendAsync(b, a);
+},
+sendAjax: function(a) {
+var b = new enyo.Ajax;
+for (var c in enyo.AjaxProperties) b[c] = this[c];
+return this.sendAsync(b, a);
+},
+sendAsync: function(a, b) {
+return a.go(b).response(this, "response").error(this, "error");
+},
+response: function(a, b) {
+this.doResponse({
+ajax: a,
+data: b
+});
+},
+error: function(a, b) {
+this.doError({
+ajax: a,
+data: b
+});
+}
+});
+
+// dom.js
+
+enyo.requiresWindow = function(a) {
+a();
+}, enyo.dom = {
+byId: function(a, b) {
+return typeof a == "string" ? (b || document).getElementById(a) : a;
+},
+escape: function(a) {
+return a !== null ? String(a).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;") : "";
+},
+getComputedStyle: function(a) {
+return window.getComputedStyle && a && window.getComputedStyle(a, null);
+},
+getComputedStyleValue: function(a, b, c) {
+var d = c || this.getComputedStyle(a);
+return d ? d.getPropertyValue(b) : null;
+},
+getFirstElementByTagName: function(a) {
+var b = document.getElementsByTagName(a);
+return b && b[0];
+},
+applyBodyFit: function() {
+var a = this.getFirstElementByTagName("html");
+a && (a.className += " enyo-document-fit");
+var b = this.getFirstElementByTagName("body");
+b && (b.className += " enyo-body-fit"), enyo.bodyIsFitting = !0;
+},
+getWindowWidth: function() {
+return window.innerWidth ? window.innerWidth : document.body && document.body.offsetWidth ? document.body.offsetWidth : document.compatMode == "CSS1Compat" && document.documentElement && document.documentElement.offsetWidth ? document.documentElement.offsetWidth : 320;
+}
+};
+
+// transform.js
+
+(function() {
+enyo.dom.calcCanAccelerate = function() {
+if (enyo.platform.android <= 2) return !1;
+var a = [ "perspective", "WebkitPerspective", "MozPerspective", "msPerspective", "OPerspective" ];
+for (var b = 0, c; c = a[b]; b++) if (typeof document.body.style[c] != "undefined") return !0;
+return !1;
+};
+var a = [ "transform", "-webkit-transform", "-moz-transform", "-ms-transform", "-o-transform" ], b = [ "transform", "webkitTransform", "MozTransform", "msTransform", "OTransform" ];
+enyo.dom.getCssTransformProp = function() {
+if (this._cssTransformProp) return this._cssTransformProp;
+var c = enyo.indexOf(this.getStyleTransformProp(), b);
+return this._cssTransformProp = a[c];
+}, enyo.dom.getStyleTransformProp = function() {
+if (this._styleTransformProp || !document.body) return this._styleTransformProp;
+for (var a = 0, c; c = b[a]; a++) if (typeof document.body.style[c] != "undefined") return this._styleTransformProp = c;
+}, enyo.dom.domTransformsToCss = function(a) {
+var b, c, d = "";
+for (b in a) c = a[b], c !== null && c !== undefined && c !== "" && (d += b + "(" + c + ") ");
+return d;
+}, enyo.dom.transformsToDom = function(a) {
+var b = this.domTransformsToCss(a.domTransforms), c = a.hasNode() ? a.node.style : null, d = a.domStyles, e = this.getStyleTransformProp(), f = this.getCssTransformProp();
+e && f && (d[f] = b, c ? c[e] = b : a.domStylesChanged());
+}, enyo.dom.canTransform = function() {
+return Boolean(this.getStyleTransformProp());
+}, enyo.dom.canAccelerate = function() {
+return this.accelerando !== undefined ? this.accelerando : document.body && (this.accelerando = this.calcCanAccelerate());
+}, enyo.dom.transform = function(a, b) {
+var c = a.domTransforms = a.domTransforms || {};
+enyo.mixin(c, b), this.transformsToDom(a);
+}, enyo.dom.transformValue = function(a, b, c) {
+var d = a.domTransforms = a.domTransforms || {};
+d[b] = c, this.transformsToDom(a);
+}, enyo.dom.accelerate = function(a, b) {
+var c = b == "auto" ? this.canAccelerate() : b;
+this.transformValue(a, "translateZ", c ? 0 : null);
+};
+})();
+
+// Control.js
+
+enyo.kind({
+name: "enyo.Control",
+kind: enyo.UiComponent,
+published: {
+tag: "div",
+attributes: null,
+classes: "",
+style: "",
+content: "",
+showing: !0,
+allowHtml: !1,
+src: "",
+canGenerate: !0,
+fit: !1,
+isContainer: !1
+},
+handlers: {
+ontap: "tap"
+},
+defaultKind: "Control",
+controlClasses: "",
+node: null,
+generated: !1,
+create: function() {
+this.initStyles(), this.inherited(arguments), this.showingChanged(), this.addClass(this.kindClasses), this.addClass(this.classes), this.initProps([ "id", "content", "src" ]);
+},
+destroy: function() {
+this.removeNodeFromDom(), this.inherited(arguments);
+},
+importProps: function(a) {
+this.inherited(arguments), this.attributes = enyo.mixin(enyo.clone(this.kindAttributes), this.attributes);
+},
+initProps: function(a) {
+for (var b = 0, c, d; c = a[b]; b++) this[c] && (d = c + "Changed", this[d] && this[d]());
+},
+classesChanged: function(a) {
+this.removeClass(a), this.addClass(this.classes);
+},
+addChild: function(a) {
+a.addClass(this.controlClasses), this.inherited(arguments);
+},
+removeChild: function(a) {
+this.inherited(arguments), a.removeClass(this.controlClasses);
+},
+strictlyInternalEvents: {
+onenter: 1,
+onleave: 1
+},
+dispatchEvent: function(a, b, c) {
+return this.strictlyInternalEvents[a] && this.isInternalEvent(b) ? !0 : this.inherited(arguments);
+},
+isInternalEvent: function(a) {
+var b = enyo.dispatcher.findDispatchTarget(a.relatedTarget);
+return b && b.isDescendantOf(this);
+},
+hasNode: function() {
+return this.generated && (this.node || this.findNodeById());
+},
+addContent: function(a) {
+this.setContent(this.content + a);
+},
+getAttribute: function(a) {
+return this.hasNode() ? this.node.getAttribute(a) : this.attributes[a];
+},
+setAttribute: function(a, b) {
+this.attributes[a] = b, this.hasNode() && this.attributeToNode(a, b), this.invalidateTags();
+},
+getNodeProperty: function(a, b) {
+return this.hasNode() ? this.node[a] : b;
+},
+setNodeProperty: function(a, b) {
+this.hasNode() && (this.node[a] = b);
+},
+setClassAttribute: function(a) {
+this.setAttribute("class", a);
+},
+getClassAttribute: function() {
+return this.attributes["class"] || "";
+},
+hasClass: function(a) {
+return a && (" " + this.getClassAttribute() + " ").indexOf(" " + a + " ") >= 0;
+},
+addClass: function(a) {
+if (a && !this.hasClass(a)) {
+var b = this.getClassAttribute();
+this.setClassAttribute(b + (b ? " " : "") + a);
+}
+},
+removeClass: function(a) {
+if (a && this.hasClass(a)) {
+var b = this.getClassAttribute();
+b = (" " + b + " ").replace(" " + a + " ", " ").slice(1, -1), this.setClassAttribute(b);
+}
+},
+addRemoveClass: function(a, b) {
+this[b ? "addClass" : "removeClass"](a);
+},
+initStyles: function() {
+this.domStyles = this.domStyles || {}, enyo.Control.cssTextToDomStyles(this.kindStyle, this.domStyles), this.domCssText = enyo.Control.domStylesToCssText(this.domStyles);
+},
+styleChanged: function() {
+this.invalidateTags(), this.renderStyles();
+},
+applyStyle: function(a, b) {
+this.domStyles[a] = b, this.domStylesChanged();
+},
+addStyles: function(a) {
+enyo.Control.cssTextToDomStyles(a, this.domStyles), this.domStylesChanged();
+},
+getComputedStyleValue: function(a, b) {
+return this.hasNode() ? enyo.dom.getComputedStyleValue(this.node, a) : b;
+},
+domStylesChanged: function() {
+this.domCssText = enyo.Control.domStylesToCssText(this.domStyles), this.invalidateTags(), this.renderStyles();
+},
+stylesToNode: function() {
+this.node.style.cssText = this.style + (this.style[this.style.length - 1] == ";" ? " " : "; ") + this.domCssText;
+},
+render: function() {
+if (this.parent) {
+this.parent.beforeChildRender(this);
+if (!this.parent.generated) return this;
+}
+return this.hasNode() || this.renderNode(), this.hasNode() && (this.renderDom(), this.rendered()), this;
+},
+renderInto: function(a) {
+this.teardownRender();
+var b = enyo.dom.byId(a);
+return b == document.body ? this.setupBodyFitting() : this.fit && this.addClass("enyo-fit enyo-clip"), b.innerHTML = this.generateHtml(), this.rendered(), this;
+},
+write: function() {
+return this.fit && this.setupBodyFitting(), document.write(this.generateHtml()), this.rendered(), this;
+},
+setupBodyFitting: function() {
+enyo.dom.applyBodyFit(), this.addClass("enyo-fit enyo-clip");
+},
+rendered: function() {
+this.reflow();
+for (var a = 0, b; b = this.children[a]; a++) b.rendered();
+},
+show: function() {
+this.setShowing(!0);
+},
+hide: function() {
+this.setShowing(!1);
+},
+getBounds: function() {
+var a = this.node || this.hasNode() || 0;
+return {
+left: a.offsetLeft,
+top: a.offsetTop,
+width: a.offsetWidth,
+height: a.offsetHeight
+};
+},
+setBounds: function(a, b) {
+var c = this.domStyles, d = b || "px", e = [ "width", "height", "left", "top", "right", "bottom" ];
+for (var f = 0, g, h; h = e[f]; f++) {
+g = a[h];
+if (g || g === 0) c[h] = g + (enyo.isString(g) ? "" : d);
+}
+this.domStylesChanged();
+},
+findNodeById: function() {
+return this.id && (this.node = enyo.dom.byId(this.id));
+},
+idChanged: function(a) {
+a && enyo.Control.unregisterDomEvents(a), this.setAttribute("id", this.id), this.id && enyo.Control.registerDomEvents(this.id, this);
+},
+contentChanged: function() {
+this.hasNode() && this.renderContent();
+},
+getSrc: function() {
+return this.getAttribute("src");
+},
+srcChanged: function() {
+this.setAttribute("src", enyo.path.rewrite(this.src));
+},
+attributesChanged: function() {
+this.invalidateTags(), this.renderAttributes();
+},
+generateHtml: function() {
+if (this.canGenerate === !1) return "";
+var a = this.generateInnerHtml(), b = this.generateOuterHtml(a);
+return this.generated = !0, b;
+},
+generateInnerHtml: function() {
+return this.flow(), this.children.length ? this.generateChildHtml() : this.allowHtml ? this.content : enyo.Control.escapeHtml(this.content);
+},
+generateChildHtml: function() {
+var a = "";
+for (var b = 0, c; c = this.children[b]; b++) {
+var d = c.generateHtml();
+c.prepend ? a = d + a : a += d;
+}
+return a;
+},
+generateOuterHtml: function(a) {
+return this.tag ? (this.tagsValid || this.prepareTags(), this._openTag + a + this._closeTag) : a;
+},
+invalidateTags: function() {
+this.tagsValid = !1;
+},
+prepareTags: function() {
+var a = this.domCssText + this.style;
+this._openTag = "<" + this.tag + (a ? ' style="' + a + '"' : "") + enyo.Control.attributesToHtml(this.attributes), enyo.Control.selfClosing[this.tag] ? (this._openTag += "/>", this._closeTag = "") : (this._openTag += ">", this._closeTag = "</" + this.tag + ">"), this.tagsValid = !0;
+},
+attributeToNode: function(a, b) {
+b === null || b === !1 || b === "" ? this.node.removeAttribute(a) : this.node.setAttribute(a, b);
+},
+attributesToNode: function() {
+for (var a in this.attributes) this.attributeToNode(a, this.attributes[a]);
+},
+getParentNode: function() {
+return this.parentNode || this.parent && this.parent.hasNode();
+},
+addNodeToParent: function() {
+if (this.node) {
+var a = this.getParentNode();
+a && this[this.prepend ? "insertNodeInParent" : "appendNodeToParent"](a);
+}
+},
+appendNodeToParent: function(a) {
+a.appendChild(this.node);
+},
+insertNodeInParent: function(a, b) {
+a.insertBefore(this.node, b || a.firstChild);
+},
+removeNodeFromDom: function() {
+this.hasNode() && this.node.parentNode && this.node.parentNode.removeChild(this.node);
+},
+teardownRender: function() {
+this.generated && this.teardownChildren(), this.node = null, this.generated = !1;
+},
+teardownChildren: function() {
+for (var a = 0, b; b = this.children[a]; a++) b.teardownRender();
+},
+renderNode: function() {
+this.teardownRender(), this.node = document.createElement(this.tag), this.addNodeToParent(), this.generated = !0;
+},
+renderDom: function() {
+this.renderAttributes(), this.renderStyles(), this.renderContent();
+},
+renderContent: function() {
+this.generated && this.teardownChildren(), this.node.innerHTML = this.generateInnerHtml();
+},
+renderStyles: function() {
+this.hasNode() && this.stylesToNode();
+},
+renderAttributes: function() {
+this.hasNode() && this.attributesToNode();
+},
+beforeChildRender: function() {
+this.generated && this.flow();
+},
+syncDisplayToShowing: function() {
+var a = this.domStyles;
+this.showing ? a.display == "none" && this.applyStyle("display", this._displayStyle || "") : (this._displayStyle = a.display == "none" ? "" : a.display, this.applyStyle("display", "none"));
+},
+showingChanged: function() {
+this.syncDisplayToShowing();
+},
+getShowing: function() {
+return this.showing = this.domStyles.display != "none";
+},
+fitChanged: function(a) {
+this.parent.reflow();
+},
+statics: {
+escapeHtml: function(a) {
+return a != null ? String(a).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;") : "";
+},
+registerDomEvents: function(a, b) {
+enyo.$[a] = b;
+},
+unregisterDomEvents: function(a) {
+enyo.$[a] = null;
+},
+selfClosing: {
+img: 1,
+hr: 1,
+br: 1,
+area: 1,
+base: 1,
+basefont: 1,
+input: 1,
+link: 1,
+meta: 1,
+command: 1,
+embed: 1,
+keygen: 1,
+wbr: 1,
+param: 1,
+source: 1,
+track: 1,
+col: 1
+},
+cssTextToDomStyles: function(a, b) {
+if (a) {
+var c = a.replace(/; /g, ";").split(";");
+for (var d = 0, e, f, g, h; h = c[d]; d++) e = h.split(":"), f = e.shift(), g = e.join(":"), b[f] = g;
+}
+},
+domStylesToCssText: function(a) {
+var b, c, d = "";
+for (b in a) c = a[b], c !== null && c !== undefined && c !== "" && (d += b + ":" + c + ";");
+return d;
+},
+stylesToHtml: function(a) {
+var b = enyo.Control.domStylesToCssText(a);
+return b ? ' style="' + b + '"' : "";
+},
+escapeAttribute: function(a) {
+return enyo.isString(a) ? String(a).replace(/&/g, "&amp;").replace(/\"/g, "&quot;") : a;
+},
+attributesToHtml: function(a) {
+var b, c, d = "";
+for (b in a) c = a[b], c !== null && c !== !1 && c !== "" && (d += " " + b + '="' + enyo.Control.escapeAttribute(c) + '"');
+return d;
+}
+}
+}), enyo.defaultCtor = enyo.Control, enyo.Control.subclass = function(a, b) {
+var c = a.prototype;
+if (c.classes) {
+var d = c.kindClasses;
+c.kindClasses = (d ? d + " " : "") + c.classes, c.classes = "";
+}
+if (c.style) {
+var e = c.kindStyle;
+c.kindStyle = (e ? e + ";" : "") + c.style, c.style = "";
+}
+if (b.attributes) {
+var f = c.kindAttributes;
+c.kindAttributes = enyo.mixin(enyo.clone(f), c.attributes), c.attributes = null;
+}
+};
+
+// platform.js
+
+enyo.platform = {
+touch: Boolean("ontouchstart" in window || window.navigator.msPointerEnabled),
+gesture: Boolean("ongesturestart" in window || window.navigator.msPointerEnabled)
+}, function() {
+var a = navigator.userAgent, b = enyo.platform, c = [ {
+platform: "android",
+regex: /Android (\d+)/
+}, {
+platform: "android",
+regex: /Silk\//,
+forceVersion: 2
+}, {
+platform: "ie",
+regex: /MSIE (\d+)/
+}, {
+platform: "ios",
+regex: /iP(?:hone|ad;(?: U;)? CPU) OS (\d+)/
+}, {
+platform: "webos",
+regex: /(?:web|hpw)OS\/(\d+)/
+}, {
+platform: "safari",
+regex: /Version\/(\d+)[.\d]+\s+Safari/
+} ];
+for (var d = 0, e, f, g; e = c[d]; d++) {
+f = e.regex.exec(a);
+if (f) {
+e.forceVersion ? g = e.forceVersion : g = Number(f[1]), b[e.platform] = g;
+break;
+}
+}
+enyo.dumbConsole = Boolean(b.android || b.ios || b.webos);
+}();
+
+// phonegap.js
+
+(function() {
+if (window.cordova || window.PhoneGap) {
+var a = [ "deviceready", "pause", "resume", "online", "offline", "backbutton", "batterycritical", "batterylow", "batterystatus", "menubutton", "searchbutton", "startcallbutton", "endcallbutton", "volumedownbutton", "volumeupbutton" ];
+for (var b = 0, c; c = a[b]; b++) document.addEventListener(c, enyo.bind(enyo.Signals, "send", "on" + c), !1);
+}
+})();
+
+// dispatcher.js
+
+enyo.$ = {}, enyo.dispatcher = {
+events: [ "mousedown", "mouseup", "mouseover", "mouseout", "mousemove", "mousewheel", "click", "dblclick", "change", "keydown", "keyup", "keypress", "input" ],
+windowEvents: [ "resize", "load", "unload", "message" ],
+features: [],
+connect: function() {
+var a = enyo.dispatcher;
+for (var b = 0, c; c = a.events[b]; b++) a.listen(document, c);
+for (b = 0, c; c = a.windowEvents[b]; b++) a.listen(window, c);
+},
+listen: function(a, b) {
+var c = enyo.dispatch;
+a.addEventListener ? this.listen = function(a, b) {
+a.addEventListener(b, c, !1);
+} : this.listen = function(a, b, e) {
+a.attachEvent("on" + b, function(a) {
+return a.target = a.srcElement, a.preventDefault || (a.preventDefault = enyo.iePreventDefault), c(a);
+});
+}, this.listen(a, b);
+},
+dispatch: function(a) {
+var b = this.findDispatchTarget(a.target) || this.findDefaultTarget(a);
+a.dispatchTarget = b;
+for (var c = 0, d; d = this.features[c]; c++) if (d.call(this, a) === !0) return;
+b && !a.preventDispatch && this.dispatchBubble(a, b);
+},
+findDispatchTarget: function(a) {
+var b, c = a;
+try {
+while (c) {
+if (b = enyo.$[c.id]) {
+b.eventNode = c;
+break;
+}
+c = c.parentNode;
+}
+} catch (d) {
+console.log(d, c);
+}
+return b;
+},
+findDefaultTarget: function(a) {
+return enyo.master;
+},
+dispatchBubble: function(a, b) {
+return b.bubble("on" + a.type, a, b);
+}
+}, enyo.iePreventDefault = function() {
+this.returnValue = !1;
+}, enyo.dispatch = function(a) {
+return enyo.dispatcher.dispatch(a);
+}, enyo.bubble = function(a) {
+var b = a || window.event;
+b && (b.target || (b.target = b.srcElement), enyo.dispatch(b));
+}, enyo.bubbler = "enyo.bubble(arguments[0])", enyo.requiresWindow(enyo.dispatcher.connect);
+
+// preview.js
+
+(function() {
+var a = "previewDomEvent", b = {
+feature: function(a) {
+b.dispatch(a, a.dispatchTarget);
+},
+dispatch: function(b, c) {
+var d = this.buildLineage(c);
+for (var e = 0, f; f = d[e]; e++) if (f[a] && f[a](b) === !0) {
+b.preventDispatch = !0;
+return;
+}
+},
+buildLineage: function(a) {
+var b = [], c = a;
+while (c) b.unshift(c), c = c.parent;
+return b;
+}
+};
+enyo.dispatcher.features.push(b.feature);
+})();
+
+// modal.js
+
+enyo.dispatcher.features.push(function(a) {
+var b = a.dispatchTarget, c = this.captureTarget && !this.noCaptureEvents[a.type], d = c && !(b && b.isDescendantOf && b.isDescendantOf(this.captureTarget));
+if (d) {
+var e = a.captureTarget = this.captureTarget, f = this.autoForwardEvents[a.type] || this.forwardEvents;
+this.dispatchBubble(a, e), f || (a.preventDispatch = !0);
+}
+}), enyo.mixin(enyo.dispatcher, {
+noCaptureEvents: {
+load: 1,
+unload: 1,
+error: 1
+},
+autoForwardEvents: {
+leave: 1,
+resize: 1
+},
+captures: [],
+capture: function(a, b) {
+var c = {
+target: a,
+forward: b
+};
+this.captures.push(c), this.setCaptureInfo(c);
+},
+release: function() {
+this.captures.pop(), this.setCaptureInfo(this.captures[this.captures.length - 1]);
+},
+setCaptureInfo: function(a) {
+this.captureTarget = a && a.target, this.forwardEvents = a && a.forward;
+}
+});
+
+// gesture.js
+
+enyo.gesture = {
+eventProps: [ "target", "relatedTarget", "clientX", "clientY", "pageX", "pageY", "screenX", "screenY", "altKey", "ctrlKey", "metaKey", "shiftKey", "detail", "identifier", "dispatchTarget", "which", "srcEvent" ],
+makeEvent: function(a, b) {
+var c = {
+type: a
+};
+for (var d = 0, e; e = this.eventProps[d]; d++) c[e] = b[e];
+c.srcEvent = c.srcEvent || b, c.preventDefault = this.preventDefault, c.disablePrevention = this.disablePrevention;
+if (enyo.platform.ie) {
+var f = window.event && window.event.button;
+c.which = f & 1 ? 1 : f & 2 ? 2 : f & 4 ? 3 : 0;
+}
+return c;
+},
+down: function(a) {
+var b = this.makeEvent("down", a);
+enyo.dispatch(b), this.downEvent = b;
+},
+move: function(a) {
+var b = this.makeEvent("move", a);
+b.dx = b.dy = b.horizontal = b.vertical = 0, b.which && this.downEvent && (b.dx = a.clientX - this.downEvent.clientX, b.dy = a.clientY - this.downEvent.clientY, b.horizontal = Math.abs(b.dx) > Math.abs(b.dy), b.vertical = !b.horizontal), enyo.dispatch(b);
+},
+up: function(a) {
+var b = this.makeEvent("up", a), c = !1;
+b.preventTap = function() {
+c = !0;
+}, enyo.dispatch(b), !c && this.downEvent && this.downEvent.which == 1 && this.sendTap(b), this.downEvent = null;
+},
+over: function(a) {
+enyo.dispatch(this.makeEvent("enter", a));
+},
+out: function(a) {
+enyo.dispatch(this.makeEvent("leave", a));
+},
+sendTap: function(a) {
+var b = this.findCommonAncestor(this.downEvent.target, a.target);
+if (b) {
+var c = this.makeEvent("tap", a);
+c.target = b, enyo.dispatch(c);
+}
+},
+findCommonAncestor: function(a, b) {
+var c = b;
+while (c) {
+if (this.isTargetDescendantOf(a, c)) return c;
+c = c.parentNode;
+}
+},
+isTargetDescendantOf: function(a, b) {
+var c = a;
+while (c) {
+if (c == b) return !0;
+c = c.parentNode;
+}
+}
+}, enyo.gesture.preventDefault = function() {
+this.srcEvent && this.srcEvent.preventDefault();
+}, enyo.gesture.disablePrevention = function() {
+this.preventDefault = enyo.nop, this.srcEvent && (this.srcEvent.preventDefault = enyo.nop);
+}, enyo.dispatcher.features.push(function(a) {
+if (enyo.gesture.events[a.type]) return enyo.gesture.events[a.type](a);
+}), enyo.gesture.events = {
+mousedown: function(a) {
+enyo.gesture.down(a);
+},
+mouseup: function(a) {
+enyo.gesture.up(a);
+},
+mousemove: function(a) {
+enyo.gesture.move(a);
+},
+mouseover: function(a) {
+enyo.gesture.over(a);
+},
+mouseout: function(a) {
+enyo.gesture.out(a);
+}
+}, enyo.requiresWindow(function() {
+document.addEventListener && document.addEventListener("DOMMouseScroll", function(a) {
+var b = enyo.clone(a);
+b.preventDefault = function() {
+a.preventDefault();
+}, b.type = "mousewheel";
+var c = b.VERTICAL_AXIS == b.axis ? "wheelDeltaY" : "wheelDeltaX";
+b[c] = b.detail * -12, enyo.dispatch(b);
+}, !1);
+});
+
+// drag.js
+
+enyo.dispatcher.features.push(function(a) {
+if (enyo.gesture.drag[a.type]) return enyo.gesture.drag[a.type](a);
+}), enyo.gesture.drag = {
+hysteresisSquared: 16,
+holdPulseDelay: 200,
+trackCount: 5,
+minFlick: .1,
+minTrack: 8,
+down: function(a) {
+this.stopDragging(a), this.cancelHold(), this.target = a.target, this.startTracking(a), this.beginHold(a);
+},
+move: function(a) {
+if (this.tracking) {
+this.track(a);
+if (!a.which) {
+this.stopDragging(a), this.cancelHold(), this.tracking = !1;
+return;
+}
+this.dragEvent ? this.sendDrag(a) : this.dy * this.dy + this.dx * this.dx >= this.hysteresisSquared && (this.sendDragStart(a), this.cancelHold());
+}
+},
+up: function(a) {
+this.endTracking(a), this.stopDragging(a), this.cancelHold();
+},
+leave: function(a) {
+this.dragEvent && this.sendDragOut(a);
+},
+stopDragging: function(a) {
+if (this.dragEvent) {
+this.sendDrop(a);
+var b = this.sendDragFinish(a);
+return this.dragEvent = null, b;
+}
+},
+makeDragEvent: function(a, b, c, d) {
+var e = Math.abs(this.dx), f = Math.abs(this.dy), g = e > f, h = (g ? f / e : e / f) < .414, i = {
+type: a,
+dx: this.dx,
+dy: this.dy,
+ddx: this.dx - this.lastDx,
+ddy: this.dy - this.lastDy,
+xDirection: this.xDirection,
+yDirection: this.yDirection,
+pageX: c.pageX,
+pageY: c.pageY,
+clientX: c.clientX,
+clientY: c.clientY,
+horizontal: g,
+vertical: !g,
+lockable: h,
+target: b,
+dragInfo: d,
+ctrlKey: c.ctrlKey,
+altKey: c.altKey,
+metaKey: c.metaKey,
+shiftKey: c.shiftKey,
+srcEvent: c.srcEvent
+};
+return enyo.platform.ie == 8 && i.target && (i.pageX = i.clientX + i.target.scrollLeft, i.pageY = i.clientY + i.target.scrollTop), i.preventDefault = enyo.gesture.preventDefault, i.disablePrevention = enyo.gesture.disablePrevention, i;
+},
+sendDragStart: function(a) {
+this.dragEvent = this.makeDragEvent("dragstart", this.target, a), enyo.dispatch(this.dragEvent);
+},
+sendDrag: function(a) {
+var b = this.makeDragEvent("dragover", a.target, a, this.dragEvent.dragInfo);
+enyo.dispatch(b), b.type = "drag", b.target = this.dragEvent.target, enyo.dispatch(b);
+},
+sendDragFinish: function(a) {
+var b = this.makeDragEvent("dragfinish", this.dragEvent.target, a, this.dragEvent.dragInfo);
+b.preventTap = function() {
+a.preventTap && a.preventTap();
+}, enyo.dispatch(b);
+},
+sendDragOut: function(a) {
+var b = this.makeDragEvent("dragout", a.target, a, this.dragEvent.dragInfo);
+enyo.dispatch(b);
+},
+sendDrop: function(a) {
+var b = this.makeDragEvent("drop", a.target, a, this.dragEvent.dragInfo);
+b.preventTap = function() {
+a.preventTap && a.preventTap();
+}, enyo.dispatch(b);
+},
+startTracking: function(a) {
+this.tracking = !0, this.px0 = a.clientX, this.py0 = a.clientY, this.flickInfo = {
+startEvent: a,
+moves: []
+}, this.track(a);
+},
+track: function(a) {
+this.lastDx = this.dx, this.lastDy = this.dy, this.dx = a.clientX - this.px0, this.dy = a.clientY - this.py0, this.xDirection = this.calcDirection(this.dx - this.lastDx, 0), this.yDirection = this.calcDirection(this.dy - this.lastDy, 0);
+var b = this.flickInfo;
+b.moves.push({
+x: a.clientX,
+y: a.clientY,
+t: enyo.now()
+}), b.moves.length > this.trackCount && b.moves.shift();
+},
+endTracking: function(a) {
+this.tracking = !1;
+var b = this.flickInfo, c = b && b.moves;
+if (c && c.length > 1) {
+var d = c[c.length - 1], e = enyo.now();
+for (var f = c.length - 2, g = 0, h = 0, i = 0, j = 0, k = 0, l = 0, m = 0, n; n = c[f]; f--) {
+g = e - n.t, h = (d.x - n.x) / g, i = (d.y - n.y) / g, l = l || (h < 0 ? -1 : h > 0 ? 1 : 0), m = m || (i < 0 ? -1 : i > 0 ? 1 : 0);
+if (h * l > j * l || i * m > k * m) j = h, k = i;
+}
+var o = Math.sqrt(j * j + k * k);
+o > this.minFlick && this.sendFlick(b.startEvent, j, k, o);
+}
+this.flickInfo = null;
+},
+calcDirection: function(a, b) {
+return a > 0 ? 1 : a < 0 ? -1 : b;
+},
+beginHold: function(a) {
+this.holdStart = enyo.now(), this.holdJob = setInterval(enyo.bind(this, "sendHoldPulse", a), this.holdPulseDelay);
+},
+cancelHold: function() {
+clearInterval(this.holdJob), this.holdJob = null, this.sentHold && (this.sentHold = !1, this.sendRelease(this.holdEvent));
+},
+sendHoldPulse: function(a) {
+this.sentHold || (this.sentHold = !0, this.sendHold(a));
+var b = enyo.gesture.makeEvent("holdpulse", a);
+b.holdTime = enyo.now() - this.holdStart, enyo.dispatch(b);
+},
+sendHold: function(a) {
+this.holdEvent = a;
+var b = enyo.gesture.makeEvent("hold", a);
+enyo.dispatch(b);
+},
+sendRelease: function(a) {
+var b = enyo.gesture.makeEvent("release", a);
+enyo.dispatch(b);
+},
+sendFlick: function(a, b, c, d) {
+var e = enyo.gesture.makeEvent("flick", a);
+e.xVelocity = b, e.yVelocity = c, e.velocity = d, enyo.dispatch(e);
+}
+};
+
+// touch.js
+
+enyo.requiresWindow(function() {
+var a = enyo.gesture;
+a.events.touchstart = function(c) {
+a.events = b, a.events.touchstart(c);
+};
+var b = {
+touchstart: function(b) {
+this.excludedTarget = null;
+var c = this.makeEvent(b);
+a.down(c), c = this.makeEvent(b), this.overEvent = c, a.over(c);
+},
+touchmove: function(b) {
+var c = a.drag.dragEvent;
+this.excludedTarget = c && c.dragInfo && c.dragInfo.node;
+var d = this.makeEvent(b);
+a.move(d), enyo.bodyIsFitting && b.preventDefault(), this.overEvent && this.overEvent.target != d.target && (this.overEvent.relatedTarget = d.target, d.relatedTarget = this.overEvent.target, a.out(this.overEvent), a.over(d)), this.overEvent = d;
+},
+touchend: function(b) {
+a.up(this.makeEvent(b)), a.out(this.overEvent);
+},
+makeEvent: function(a) {
+var b = enyo.clone(a.changedTouches[0]);
+return b.srcEvent = a, b.target = this.findTarget(b.clientX, b.clientY), b.which = 1, b;
+},
+calcNodeOffset: function(a) {
+if (a.getBoundingClientRect) {
+var b = a.getBoundingClientRect();
+return {
+left: b.left,
+top: b.top,
+width: b.width,
+height: b.height
+};
+}
+},
+findTarget: function(a, b) {
+return document.elementFromPoint(a, b);
+},
+findTargetTraverse: function(a, b, c) {
+var d = a || document.body, e = this.calcNodeOffset(d);
+if (e && d != this.excludedTarget) {
+var f = b - e.left, g = c - e.top;
+if (f > 0 && g > 0 && f <= e.width && g <= e.height) {
+var h;
+for (var i = d.childNodes, j = i.length - 1, k; k = i[j]; j--) {
+h = this.findTargetTraverse(k, b, c);
+if (h) return h;
+}
+return d;
+}
+}
+},
+connect: function() {
+enyo.forEach([ "ontouchstart", "ontouchmove", "ontouchend", "ongesturestart", "ongesturechange", "ongestureend" ], function(a) {
+document[a] = enyo.dispatch;
+}), document.elementFromPoint || (this.findTarget = function(a, b) {
+return this.findTargetTraverse(null, a, b);
+});
+}
+};
+b.connect();
+});
+
+// msevents.js
+
+(function() {
+if (window.navigator.msPointerEnabled) {
+var a = [ "MSPointerDown", "MSPointerUp", "MSPointerMove", "MSPointerOver", "MSPointerOut", "MSPointerCancel", "MSGestureTap", "MSGestureDoubleTap", "MSGestureHold", "MSGestureStart", "MSGestureChange", "MSGestureEnd" ];
+enyo.forEach(a, function(a) {
+enyo.dispatcher.listen(document, a);
+}), enyo.dispatcher.features.push(function(a) {
+c[a.type] && c[a.type](a);
+});
+}
+var b = function(a, b) {
+var c = enyo.clone(b);
+return enyo.mixin(c, {
+pageX: b.translationX || 0,
+pageY: b.translationY || 0,
+rotation: b.rotation * (180 / Math.PI) || 0,
+type: a,
+srcEvent: b,
+preventDefault: enyo.gesture.preventDefault,
+disablePrevention: enyo.gesture.disablePrevention
+});
+}, c = {
+MSGestureStart: function(a) {
+enyo.dispatch(b("gesturestart", a));
+},
+MSGestureChange: function(a) {
+enyo.dispatch(b("gesturechange", a));
+},
+MSGestureEnd: function(a) {
+enyo.dispatch(b("gestureend", a));
+}
+};
+})();
+
+// gesture.js
+
+(function() {
+!enyo.platform.gesture && enyo.platform.touch && enyo.dispatcher.features.push(function(c) {
+a[c.type] && b[c.type](c);
+});
+var a = {
+touchstart: !0,
+touchmove: !0,
+touchend: !0
+}, b = {
+orderedTouches: [],
+gesture: null,
+touchstart: function(a) {
+enyo.forEach(a.changedTouches, function(a) {
+var b = a.identifier;
+enyo.indexOf(b, this.orderedTouches) < 0 && this.orderedTouches.push(b);
+}, this);
+if (a.touches.length >= 2 && !this.gesture) {
+var b = this.gesturePositions(a);
+this.gesture = this.gestureVector(b), this.gesture.angle = this.gestureAngle(b), this.gesture.scale = 1, this.gesture.rotation = 0;
+var c = this.makeGesture("gesturestart", a, {
+vector: this.gesture,
+scale: 1,
+rotation: 0
+});
+enyo.dispatch(c);
+}
+},
+touchend: function(a) {
+enyo.forEach(a.changedTouches, function(a) {
+enyo.remove(a.identifier, this.orderedTouches);
+}, this);
+if (a.touches.length <= 1 && this.gesture) {
+var b = a.touches[0] || a.changedTouches[a.changedTouches.length - 1];
+enyo.dispatch(this.makeGesture("gestureend", a, {
+vector: {
+xcenter: b.pageX,
+ycenter: b.pageY
+},
+scale: this.gesture.scale,
+rotation: this.gesture.rotation
+})), this.gesture = null;
+}
+},
+touchmove: function(a) {
+if (this.gesture) {
+var b = this.makeGesture("gesturechange", a);
+this.gesture.scale = b.scale, this.gesture.rotation = b.rotation, enyo.dispatch(b);
+}
+},
+findIdentifiedTouch: function(a, b) {
+for (var c = 0, d; d = a[c]; c++) if (d.identifier === b) return d;
+},
+gesturePositions: function(a) {
+var b = this.findIdentifiedTouch(a.touches, this.orderedTouches[0]), c = this.findIdentifiedTouch(a.touches, this.orderedTouches[this.orderedTouches.length - 1]), d = b.pageX, e = c.pageX, f = b.pageY, g = c.pageY, h = e - d, i = g - f, j = Math.sqrt(h * h + i * i);
+return {
+x: h,
+y: i,
+h: j,
+fx: d,
+lx: e,
+fy: f,
+ly: g
+};
+},
+gestureAngle: function(a) {
+var b = a, c = Math.asin(b.y / b.h) * (180 / Math.PI);
+return b.x < 0 && (c = 180 - c), b.x > 0 && b.y < 0 && (c += 360), c;
+},
+gestureVector: function(a) {
+var b = a;
+return {
+magnitude: b.h,
+xcenter: Math.abs(Math.round(b.fx + b.x / 2)),
+ycenter: Math.abs(Math.round(b.fy + b.y / 2))
+};
+},
+makeGesture: function(a, b, c) {
+var d, e, f;
+if (c) d = c.vector, e = c.scale, f = c.rotation; else {
+var g = this.gesturePositions(b);
+d = this.gestureVector(g), e = d.magnitude / this.gesture.magnitude, f = (360 + this.gestureAngle(g) - this.gesture.angle) % 360;
+}
+var h = enyo.clone(b);
+return enyo.mixin(h, {
+type: a,
+scale: e,
+pageX: d.xcenter,
+pageY: d.ycenter,
+rotation: f
+});
+}
+};
+})();
+
+// ScrollMath.js
+
+enyo.kind({
+name: "enyo.ScrollMath",
+kind: enyo.Component,
+published: {
+vertical: !0,
+horizontal: !0
+},
+events: {
+onScrollStart: "",
+onScroll: "",
+onScrollStop: ""
+},
+kSpringDamping: .93,
+kDragDamping: .5,
+kFrictionDamping: .97,
+kSnapFriction: .9,
+kFlickScalar: 15,
+kMaxFlick: enyo.platform.android > 2 ? 2 : 1e9,
+kFrictionEpsilon: .01,
+topBoundary: 0,
+rightBoundary: 0,
+bottomBoundary: 0,
+leftBoundary: 0,
+interval: 20,
+fixedTime: !0,
+x0: 0,
+x: 0,
+y0: 0,
+y: 0,
+destroy: function() {
+this.stop(), this.inherited(arguments);
+},
+verlet: function(a) {
+var b = this.x;
+this.x += b - this.x0, this.x0 = b;
+var c = this.y;
+this.y += c - this.y0, this.y0 = c;
+},
+damping: function(a, b, c, d) {
+var e = .5, f = a - b;
+return Math.abs(f) < e ? b : a * d > b * d ? c * f + b : a;
+},
+boundaryDamping: function(a, b, c, d) {
+return this.damping(this.damping(a, b, d, 1), c, d, -1);
+},
+constrain: function() {
+var a = this.boundaryDamping(this.y, this.topBoundary, this.bottomBoundary, this.kSpringDamping);
+a != this.y && (this.y0 = a - (this.y - this.y0) * this.kSnapFriction, this.y = a);
+var b = this.boundaryDamping(this.x, this.leftBoundary, this.rightBoundary, this.kSpringDamping);
+b != this.x && (this.x0 = b - (this.x - this.x0) * this.kSnapFriction, this.x = b);
+},
+friction: function(a, b, c) {
+var d = this[a] - this[b], e = Math.abs(d) > this.kFrictionEpsilon ? c : 0;
+this[a] = this[b] + e * d;
+},
+frame: 10,
+simulate: function(a) {
+while (a >= this.frame) a -= this.frame, this.dragging || this.constrain(), this.verlet(), this.friction("y", "y0", this.kFrictionDamping), this.friction("x", "x0", this.kFrictionDamping);
+return a;
+},
+animate: function() {
+this.stop();
+var a = enyo.now(), b = 0, c, d, e = enyo.bind(this, function() {
+var f = enyo.now();
+this.job = enyo.requestAnimationFrame(e);
+var g = f - a;
+a = f, this.dragging && (this.y0 = this.y = this.uy, this.x0 = this.x = this.ux), b += Math.max(16, g), this.fixedTime && !this.isInOverScroll() && (b = this.interval), b = this.simulate(b), d != this.y || c != this.x ? this.scroll() : this.dragging || (this.stop(!0), this.scroll()), d = this.y, c = this.x;
+});
+this.job = enyo.requestAnimationFrame(e);
+},
+start: function() {
+this.job || (this.animate(), this.doScrollStart());
+},
+stop: function(a) {
+this.job = enyo.cancelRequestAnimationFrame(this.job), a && this.doScrollStop();
+},
+stabilize: function() {
+this.start();
+var a = Math.min(this.topBoundary, Math.max(this.bottomBoundary, this.y)), b = Math.min(this.leftBoundary, Math.max(this.rightBoundary, this.x));
+this.y = this.y0 = a, this.x = this.x0 = b, this.scroll(), this.stop(!0);
+},
+startDrag: function(a) {
+this.dragging = !0, this.my = a.pageY, this.py = this.uy = this.y, this.mx = a.pageX, this.px = this.ux = this.x;
+},
+drag: function(a) {
+if (this.dragging) {
+var b = this.vertical ? a.pageY - this.my : 0;
+this.uy = b + this.py, this.uy = this.boundaryDamping(this.uy, this.topBoundary, this.bottomBoundary, this.kDragDamping);
+var c = this.horizontal ? a.pageX - this.mx : 0;
+return this.ux = c + this.px, this.ux = this.boundaryDamping(this.ux, this.leftBoundary, this.rightBoundary, this.kDragDamping), this.start(), !0;
+}
+},
+dragDrop: function(a) {
+if (this.dragging && !window.PalmSystem) {
+var b = .5;
+this.y = this.uy, this.y0 = this.y - (this.y - this.y0) * b, this.x = this.ux, this.x0 = this.x - (this.x - this.x0) * b;
+}
+this.dragFinish();
+},
+dragFinish: function() {
+this.dragging = !1;
+},
+flick: function(a) {
+var b;
+this.vertical && (b = a.yVelocity > 0 ? Math.min(this.kMaxFlick, a.yVelocity) : Math.max(-this.kMaxFlick, a.yVelocity), this.y = this.y0 + b * this.kFlickScalar), this.horizontal && (b = a.xVelocity > 0 ? Math.min(this.kMaxFlick, a.xVelocity) : Math.max(-this.kMaxFlick, a.xVelocity), this.x = this.x0 + b * this.kFlickScalar), this.start();
+},
+mousewheel: function(a) {
+var b = this.vertical ? a.wheelDeltaY || a.wheelDelta : 0;
+if (b > 0 && this.y < this.topBoundary || b < 0 && this.y > this.bottomBoundary) return this.stop(!0), this.y = this.y0 = this.y0 + b, this.start(), !0;
+},
+scroll: function() {
+this.doScroll();
+},
+scrollTo: function(a, b) {
+a !== null && (this.y = this.y0 - (a + this.y0) * (1 - this.kFrictionDamping)), b !== null && (this.x = this.x0 - (b + this.x0) * (1 - this.kFrictionDamping)), this.start();
+},
+setScrollX: function(a) {
+this.x = this.x0 = a;
+},
+setScrollY: function(a) {
+this.y = this.y0 = a;
+},
+setScrollPosition: function(a) {
+this.setScrollY(a);
+},
+isScrolling: function() {
+return Boolean(this.job);
+},
+isInOverScroll: function() {
+return this.job && (this.x > this.leftBoundary || this.x < this.rightBoundary || this.y > this.topBoundary || this.y < this.bottomBoundary);
+}
+});
+
+// ScrollStrategy.js
+
+enyo.kind({
+name: "enyo.ScrollStrategy",
+tag: null,
+published: {
+vertical: "default",
+horizontal: "default",
+scrollLeft: 0,
+scrollTop: 0,
+maxHeight: null
+},
+handlers: {
+ondragstart: "dragstart",
+ondragfinish: "dragfinish",
+ondown: "down",
+onmove: "move"
+},
+create: function() {
+this.inherited(arguments), this.horizontalChanged(), this.verticalChanged(), this.maxHeightChanged(), this.container.setAttribute("onscroll", enyo.bubbler);
+},
+rendered: function() {
+this.inherited(arguments), this.scrollNode = this.calcScrollNode();
+},
+teardownRender: function() {
+this.inherited(arguments), this.scrollNode = null;
+},
+calcScrollNode: function() {
+return this.container.hasNode();
+},
+horizontalChanged: function() {
+this.container.applyStyle("overflow-x", this.horizontal == "default" ? "auto" : this.horizontal);
+},
+verticalChanged: function() {
+this.container.applyStyle("overflow-y", this.vertical == "default" ? "auto" : this.vertical);
+},
+maxHeightChanged: function() {
+this.container.applyStyle("max-height", this.maxHeight);
+},
+scrollTo: function(a, b) {
+this.scrollNode && (this.setScrollLeft(a), this.setScrollTop(b));
+},
+scrollToNode: function(a, b) {
+if (this.scrollNode) {
+var c = this.getScrollBounds(), d = a, e = {
+height: d.offsetHeight,
+width: d.offsetWidth,
+top: 0,
+left: 0
+};
+while (d && d.parentNode && d.id != this.scrollNode.id) e.top += d.offsetTop, e.left += d.offsetLeft, d = d.parentNode;
+this.setScrollTop(Math.min(c.maxTop, b === !1 ? e.top - c.clientHeight + e.height : e.top)), this.setScrollLeft(Math.min(c.maxLeft, b === !1 ? e.left - c.clientWidth + e.width : e.left));
+}
+},
+scrollIntoView: function(a, b) {
+a.hasNode() && a.node.scrollIntoView(b);
+},
+isInView: function(a) {
+var b = this.getScrollBounds(), c = a.offsetTop, d = a.offsetHeight, e = a.offsetLeft, f = a.offsetWidth;
+return c >= b.top && c + d <= b.top + b.clientHeight && e >= b.left && e + f <= b.left + b.clientWidth;
+},
+setScrollTop: function(a) {
+this.scrollTop = a, this.scrollNode && (this.scrollNode.scrollTop = this.scrollTop);
+},
+setScrollLeft: function(a) {
+this.scrollLeft = a, this.scrollNode && (this.scrollNode.scrollLeft = this.scrollLeft);
+},
+getScrollLeft: function() {
+return this.scrollNode ? this.scrollNode.scrollLeft : this.scrollLeft;
+},
+getScrollTop: function() {
+return this.scrollNode ? this.scrollNode.scrollTop : this.scrollTop;
+},
+_getScrollBounds: function() {
+var a = this.getScrollSize(), b = this.container.hasNode(), c = {
+left: this.getScrollLeft(),
+top: this.getScrollTop(),
+clientHeight: b ? b.clientHeight : 0,
+clientWidth: b ? b.clientWidth : 0,
+height: a.height,
+width: a.width
+};
+return c.maxLeft = Math.max(0, c.width - c.clientWidth), c.maxTop = Math.max(0, c.height - c.clientHeight), c;
+},
+getScrollSize: function() {
+var a = this.scrollNode;
+return {
+width: a ? a.scrollWidth : 0,
+height: a ? a.scrollHeight : 0
+};
+},
+getScrollBounds: function() {
+return this._getScrollBounds();
+},
+calcStartInfo: function() {
+var a = this.getScrollBounds(), b = this.getScrollTop(), c = this.getScrollLeft();
+this.canVertical = a.maxTop > 0 && this.vertical != "hidden", this.canHorizontal = a.maxLeft > 0 && this.horizontal != "hidden", this.startEdges = {
+top: b === 0,
+bottom: b === a.maxTop,
+left: c === 0,
+right: c === a.maxLeft
+};
+},
+shouldDrag: function(a) {
+var b = a.vertical;
+return b && this.canVertical || !b && this.canHorizontal;
+},
+dragstart: function(a, b) {
+this.dragging = this.shouldDrag(b);
+if (this.dragging) return this.preventDragPropagation;
+},
+dragfinish: function(a, b) {
+this.dragging && (this.dragging = !1, b.preventTap());
+},
+down: function(a, b) {
+this.calcStartInfo();
+},
+move: function(a, b) {
+b.which && (this.canVertical && b.vertical || this.canHorizontal && b.horizontal) && b.disablePrevention();
+}
+});
+
+// Thumb.js
+
+enyo.kind({
+name: "enyo.ScrollThumb",
+axis: "v",
+minSize: 4,
+cornerSize: 6,
+classes: "enyo-thumb",
+create: function() {
+this.inherited(arguments);
+var a = this.axis == "v";
+this.dimension = a ? "height" : "width", this.offset = a ? "top" : "left", this.translation = a ? "translateY" : "translateX", this.positionMethod = a ? "getScrollTop" : "getScrollLeft", this.sizeDimension = a ? "clientHeight" : "clientWidth", this.addClass("enyo-" + this.axis + "thumb"), this.transform = enyo.dom.canTransform(), enyo.dom.canAccelerate() && enyo.dom.transformValue(this, "translateZ", 0);
+},
+sync: function(a) {
+this.scrollBounds = a._getScrollBounds(), this.update(a);
+},
+update: function(a) {
+if (this.showing) {
+var b = this.dimension, c = this.offset, d = this.scrollBounds[this.sizeDimension], e = this.scrollBounds[b], f = 0, g = 0, h = 0;
+if (d >= e) {
+this.hide();
+return;
+}
+a.isOverscrolling() && (h = a.getOverScrollBounds()["over" + c], f = Math.abs(h), g = Math.max(h, 0));
+var i = a[this.positionMethod]() - h, j = d - this.cornerSize, k = Math.floor(d * d / e - f);
+k = Math.max(this.minSize, k);
+var l = Math.floor(j * i / e + g);
+l = Math.max(0, Math.min(j - this.minSize, l)), this.needed = k < d, this.needed && this.hasNode() ? (this._pos !== l && (this._pos = l, this.transform ? enyo.dom.transformValue(this, this.translation, l + "px") : this.axis == "v" ? this.setBounds({
+top: l + "px"
+}) : this.setBounds({
+left: l + "px"
+})), this._size !== k && (this._size = k, this.node.style[b] = this.domStyles[b] = k + "px")) : this.hide();
+}
+},
+setShowing: function(a) {
+if (a && a != this.showing && this.scrollBounds[this.sizeDimension] >= this.scrollBounds[this.dimension]) return;
+this.hasNode() && this.cancelDelayHide();
+if (a != this.showing) {
+var b = this.showing;
+this.showing = a, this.showingChanged(b);
+}
+},
+delayHide: function(a) {
+this.showing && enyo.job(this.id + "hide", enyo.bind(this, "hide"), a || 0);
+},
+cancelDelayHide: function() {
+enyo.job.stop(this.id + "hide");
+}
+});
+
+// TouchScrollStrategy.js
+
+enyo.kind({
+name: "enyo.TouchScrollStrategy",
+kind: "ScrollStrategy",
+overscroll: !0,
+preventDragPropagation: !0,
+published: {
+vertical: "default",
+horizontal: "default",
+thumb: !0,
+scrim: !1
+},
+events: {
+onShouldDrag: ""
+},
+handlers: {
+onscroll: "domScroll",
+onflick: "flick",
+onhold: "hold",
+ondragstart: "dragstart",
+onShouldDrag: "shouldDrag",
+ondrag: "drag",
+ondragfinish: "dragfinish",
+onmousewheel: "mousewheel"
+},
+tools: [ {
+kind: "ScrollMath",
+onScrollStart: "scrollMathStart",
+onScroll: "scrollMathScroll",
+onScrollStop: "scrollMathStop"
+}, {
+name: "vthumb",
+kind: "ScrollThumb",
+axis: "v",
+showing: !1
+}, {
+name: "hthumb",
+kind: "ScrollThumb",
+axis: "h",
+showing: !1
+} ],
+scrimTools: [ {
+name: "scrim",
+classes: "enyo-fit",
+style: "z-index: 1;",
+showing: !1
+} ],
+components: [ {
+name: "client",
+attributes: {
+onscroll: enyo.bubbler
+},
+classes: "enyo-touch-scroller"
+} ],
+create: function() {
+this.inherited(arguments), this.transform = enyo.dom.canTransform(), this.transform || this.overscroll && this.$.client.applyStyle("position", "relative"), this.accel = enyo.dom.canAccelerate();
+var a = "enyo-touch-strategy-container";
+enyo.platform.ios && this.accel && (a += " enyo-composite"), this.scrimChanged(), this.container.addClass(a), this.translation = this.accel ? "translate3d" : "translate";
+},
+initComponents: function() {
+this.createChrome(this.tools), this.inherited(arguments);
+},
+destroy: function() {
+this.container.removeClass("enyo-touch-strategy-container"), this.inherited(arguments);
+},
+rendered: function() {
+this.inherited(arguments), this.calcBoundaries(), this.syncScrollMath(), this.thumb && this.alertThumbs();
+},
+scrimChanged: function() {
+this.scrim && !this.$.scrim && this.makeScrim(), !this.scrim && this.$.scrim && this.$.scrim.destroy();
+},
+makeScrim: function() {
+var a = this.controlParent;
+this.controlParent = null, this.createChrome(this.scrimTools), this.controlParent = a;
+var b = this.container.hasNode();
+b && (this.$.scrim.parentNode = b, this.$.scrim.render());
+},
+isScrolling: function() {
+return this.$.scrollMath.isScrolling();
+},
+isOverscrolling: function() {
+return this.overscroll ? this.$.scrollMath.isInOverScroll() : !1;
+},
+domScroll: function() {
+this.isScrolling() || (this.calcBoundaries(), this.syncScrollMath(), this.thumb && this.alertThumbs());
+},
+horizontalChanged: function() {
+this.$.scrollMath.horizontal = this.horizontal != "hidden";
+},
+verticalChanged: function() {
+this.$.scrollMath.vertical = this.vertical != "hidden";
+},
+maxHeightChanged: function() {
+this.$.client.applyStyle("max-height", this.maxHeight), this.$.client.addRemoveClass("enyo-scrollee-fit", !this.maxHeight);
+},
+thumbChanged: function() {
+this.hideThumbs();
+},
+stop: function() {
+this.isScrolling() && this.$.scrollMath.stop(!0);
+},
+stabilize: function() {
+this.$.scrollMath.stabilize();
+},
+scrollTo: function(a, b) {
+this.stop(), this.$.scrollMath.scrollTo(b || b === 0 ? b : null, a);
+},
+scrollIntoView: function() {
+this.stop(), this.inherited(arguments);
+},
+setScrollLeft: function() {
+this.stop(), this.inherited(arguments);
+},
+setScrollTop: function() {
+this.stop(), this.inherited(arguments);
+},
+getScrollLeft: function() {
+return this.isScrolling() ? this.scrollLeft : this.inherited(arguments);
+},
+getScrollTop: function() {
+return this.isScrolling() ? this.scrollTop : this.inherited(arguments);
+},
+calcScrollNode: function() {
+return this.$.client.hasNode();
+},
+calcAutoScrolling: function() {
+var a = this.vertical == "auto", b = this.horizontal == "auto" || this.horizontal == "default";
+if ((a || b) && this.scrollNode) {
+var c = this.getScrollBounds();
+a && (this.$.scrollMath.vertical = c.height > c.clientHeight), b && (this.$.scrollMath.horizontal = c.width > c.clientWidth);
+}
+},
+shouldDrag: function(a, b) {
+this.calcAutoScrolling();
+var c = b.vertical, d = this.$.scrollMath.horizontal && !c, e = this.$.scrollMath.vertical && c, f = b.dy < 0, g = b.dx < 0, h = !f && this.startEdges.top || f && this.startEdges.bottom, i = !g && this.startEdges.left || g && this.startEdges.right;
+!b.boundaryDragger && (d || e) && (b.boundaryDragger = this);
+if (!h && e || !i && d) return b.dragger = this, !0;
+},
+flick: function(a, b) {
+var c = Math.abs(b.xVelocity) > Math.abs(b.yVelocity) ? this.$.scrollMath.horizontal : this.$.scrollMath.vertical;
+if (c && this.dragging) return this.$.scrollMath.flick(b), this.preventDragPropagation;
+},
+hold: function(a, b) {
+if (this.isScrolling() && !this.isOverscrolling()) return this.$.scrollMath.stop(b), !0;
+},
+move: function(a, b) {},
+dragstart: function(a, b) {
+this.doShouldDrag(b), this.dragging = b.dragger == this || !b.dragger && b.boundaryDragger == this;
+if (this.dragging) {
+b.preventDefault(), this.syncScrollMath(), this.$.scrollMath.startDrag(b);
+if (this.preventDragPropagation) return !0;
+}
+},
+drag: function(a, b) {
+this.dragging && (b.preventDefault(), this.$.scrollMath.drag(b), this.scrim && this.$.scrim.show());
+},
+dragfinish: function(a, b) {
+this.dragging && (b.preventTap(), this.$.scrollMath.dragFinish(), this.dragging = !1, this.scrim && this.$.scrim.hide());
+},
+mousewheel: function(a, b) {
+if (!this.dragging && this.$.scrollMath.mousewheel(b)) return b.preventDefault(), !0;
+},
+scrollMathStart: function(a) {
+this.scrollNode && (this.calcBoundaries(), this.thumb && this.showThumbs());
+},
+scrollMathScroll: function(a) {
+this.overscroll ? this.effectScroll(-a.x, -a.y) : this.effectScroll(-Math.min(a.leftBoundary, Math.max(a.rightBoundary, a.x)), -Math.min(a.topBoundary, Math.max(a.bottomBoundary, a.y))), this.thumb && this.updateThumbs();
+},
+scrollMathStop: function(a) {
+this.effectScrollStop(), this.thumb && this.delayHideThumbs(100);
+},
+calcBoundaries: function() {
+var a = this.$.scrollMath, b = this._getScrollBounds();
+a.bottomBoundary = b.clientHeight - b.height, a.rightBoundary = b.clientWidth - b.width;
+},
+syncScrollMath: function() {
+var a = this.$.scrollMath;
+a.setScrollX(-this.getScrollLeft()), a.setScrollY(-this.getScrollTop());
+},
+effectScroll: function(a, b) {
+this.scrollNode && (this.scrollLeft = this.scrollNode.scrollLeft = a, this.scrollTop = this.scrollNode.scrollTop = b, this.effectOverscroll(Math.round(a), Math.round(b)));
+},
+effectScrollStop: function() {
+this.effectOverscroll(null, null);
+},
+effectOverscroll: function(a, b) {
+var c = this.scrollNode, d = "0", e = "0", f = this.accel ? ",0" : "";
+b !== null && Math.abs(b - c.scrollTop) > 1 && (e = c.scrollTop - b), a !== null && Math.abs(a - c.scrollLeft) > 1 && (d = c.scrollLeft - a), this.transform ? enyo.dom.transformValue(this.$.client, this.translation, d + "px, " + e + "px" + f) : this.$.client.setBounds({
+left: d + "px",
+top: e + "px"
+});
+},
+getOverScrollBounds: function() {
+var a = this.$.scrollMath;
+return {
+overleft: Math.min(a.leftBoundary - a.x, 0) || Math.max(a.rightBoundary - a.x, 0),
+overtop: Math.min(a.topBoundary - a.y, 0) || Math.max(a.bottomBoundary - a.y, 0)
+};
+},
+_getScrollBounds: function() {
+var a = this.inherited(arguments);
+return enyo.mixin(a, this.getOverScrollBounds()), a;
+},
+getScrollBounds: function() {
+return this.stop(), this.inherited(arguments);
+},
+alertThumbs: function() {
+this.showThumbs(), this.delayHideThumbs(500);
+},
+syncThumbs: function() {
+this.$.vthumb.sync(this), this.$.hthumb.sync(this);
+},
+updateThumbs: function() {
+this.$.vthumb.update(this), this.$.hthumb.update(this);
+},
+showThumbs: function() {
+this.syncThumbs(), this.$.vthumb.show(), this.$.hthumb.show();
+},
+hideThumbs: function() {
+this.$.vthumb.hide(), this.$.hthumb.hide();
+},
+delayHideThumbs: function(a) {
+this.$.vthumb.delayHide(a), this.$.hthumb.delayHide(a);
+}
+});
+
+// TranslateScrollStrategy.js
+
+enyo.kind({
+name: "enyo.TranslateScrollStrategy",
+kind: "TouchScrollStrategy",
+components: [ {
+name: "clientContainer",
+classes: "enyo-touch-scroller",
+attributes: {
+onscroll: enyo.bubbler
+},
+components: [ {
+name: "client"
+} ]
+} ],
+translateOptimized: !1,
+getScrollSize: function() {
+var a = this.$.client.hasNode();
+return {
+width: a ? a.scrollWidth : 0,
+height: a ? a.scrollHeight : 0
+};
+},
+create: function() {
+this.inherited(arguments), enyo.dom.transformValue(this.$.client, this.translation, "0,0,0");
+},
+calcScrollNode: function() {
+return this.$.clientContainer.hasNode();
+},
+maxHeightChanged: function() {
+this.$.client.applyStyle("min-height", this.maxHeight ? null : "100%"), this.$.client.applyStyle("max-height", this.maxHeight), this.$.clientContainer.addRemoveClass("enyo-scrollee-fit", !this.maxHeight);
+},
+shouldDrag: function(a, b) {
+return this.stop(), this.calcStartInfo(), this.inherited(arguments);
+},
+syncScrollMath: function() {
+this.translateOptimized || this.inherited(arguments);
+},
+setScrollLeft: function(a) {
+this.stop();
+if (this.translateOptimized) {
+var b = this.$.scrollMath;
+b.setScrollX(-a), b.stabilize();
+} else this.inherited(arguments);
+},
+setScrollTop: function(a) {
+this.stop();
+if (this.translateOptimized) {
+var b = this.$.scrollMath;
+b.setScrollY(-a), b.stabilize();
+} else this.inherited(arguments);
+},
+getScrollLeft: function() {
+return this.translateOptimized ? this.scrollLeft : this.inherited(arguments);
+},
+getScrollTop: function() {
+return this.translateOptimized ? this.scrollTop : this.inherited(arguments);
+},
+scrollMathStart: function(a) {
+this.inherited(arguments), this.scrollStarting = !0, this.startX = 0, this.startY = 0, !this.translateOptimized && this.scrollNode && (this.startX = this.getScrollLeft(), this.startY = this.getScrollTop());
+},
+scrollMathScroll: function(a) {
+this.overscroll ? (this.scrollLeft = -a.x, this.scrollTop = -a.y) : (this.scrollLeft = -Math.min(a.leftBoundary, Math.max(a.rightBoundary, a.x)), this.scrollTop = -Math.min(a.topBoundary, Math.max(a.bottomBoundary, a.y))), this.isScrolling() && (this.$.scrollMath.isScrolling() && this.effectScroll(this.startX - this.scrollLeft, this.startY - this.scrollTop), this.thumb && this.updateThumbs());
+},
+effectScroll: function(a, b) {
+var c = a + "px, " + b + "px" + (this.accel ? ",0" : "");
+enyo.dom.transformValue(this.$.client, this.translation, c);
+},
+effectScrollStop: function() {
+if (!this.translateOptimized) {
+var a = "0,0" + (this.accel ? ",0" : ""), b = this.$.scrollMath, c = this._getScrollBounds(), d = Boolean(c.maxTop + b.bottomBoundary || c.maxLeft + b.rightBoundary);
+enyo.dom.transformValue(this.$.client, this.translation, d ? null : a), this.setScrollLeft(this.scrollLeft), this.setScrollTop(this.scrollTop), d && enyo.dom.transformValue(this.$.client, this.translation, a);
+}
+},
+twiddle: function() {
+this.translateOptimized && (this.scrollNode.scrollTop = 1, this.scrollNode.scrollTop = 0);
+},
+down: enyo.nop
+});
+
+// Scroller.js
+
+enyo.kind({
+name: "enyo.Scroller",
+published: {
+horizontal: "default",
+vertical: "default",
+scrollTop: 0,
+scrollLeft: 0,
+maxHeight: null,
+touch: !1,
+strategyKind: "ScrollStrategy",
+thumb: !0
+},
+events: {
+onScrollStart: "",
+onScroll: "",
+onScrollStop: ""
+},
+handlers: {
+onscroll: "domScroll",
+onScrollStart: "scrollStart",
+onScroll: "scroll",
+onScrollStop: "scrollStop"
+},
+classes: "enyo-scroller",
+touchOverscroll: !0,
+preventDragPropagation: !0,
+preventScrollPropagation: !0,
+statics: {
+osInfo: [ {
+os: "android",
+version: 3
+}, {
+os: "ios",
+version: 5
+}, {
+os: "webos",
+version: 1e9
+} ],
+hasTouchScrolling: function() {
+for (var a = 0, b, c; b = this.osInfo[a]; a++) if (enyo.platform[b.os]) return !0;
+},
+hasNativeScrolling: function() {
+for (var a = 0, b, c; b = this.osInfo[a]; a++) if (enyo.platform[b.os] < b.version) return !1;
+return !0;
+},
+getTouchStrategy: function() {
+return enyo.platform.android >= 3 ? "TranslateScrollStrategy" : "TouchScrollStrategy";
+}
+},
+controlParentName: "strategy",
+create: function() {
+this.inherited(arguments), this.horizontalChanged(), this.verticalChanged();
+},
+importProps: function(a) {
+this.inherited(arguments), a && a.strategyKind === undefined && (enyo.Scroller.touchScrolling || this.touch) && (this.strategyKind = enyo.Scroller.getTouchStrategy());
+},
+initComponents: function() {
+this.strategyKindChanged(), this.inherited(arguments);
+},
+teardownChildren: function() {
+this.cacheScrollPosition(), this.inherited(arguments);
+},
+rendered: function() {
+this.inherited(arguments), this.restoreScrollPosition();
+},
+strategyKindChanged: function() {
+this.$.strategy && (this.$.strategy.destroy(), this.controlParent = null), this.createStrategy(), this.hasNode() && this.render();
+},
+createStrategy: function() {
+this.createComponents([ {
+name: "strategy",
+maxHeight: this.maxHeight,
+kind: this.strategyKind,
+thumb: this.thumb,
+preventDragPropagation: this.preventDragPropagation,
+overscroll: this.touchOverscroll,
+isChrome: !0
+} ]);
+},
+getStrategy: function() {
+return this.$.strategy;
+},
+maxHeightChanged: function() {
+this.$.strategy.setMaxHeight(this.maxHeight);
+},
+showingChanged: function() {
+this.showing || (this.cacheScrollPosition(), this.setScrollLeft(0), this.setScrollTop(0)), this.inherited(arguments), this.showing && this.restoreScrollPosition();
+},
+thumbChanged: function() {
+this.$.strategy.setThumb(this.thumb);
+},
+cacheScrollPosition: function() {
+this.cachedPosition = {
+left: this.getScrollLeft(),
+top: this.getScrollTop()
+};
+},
+restoreScrollPosition: function() {
+this.cachedPosition && (this.setScrollLeft(this.cachedPosition.left), this.setScrollTop(this.cachedPosition.top), this.cachedPosition = null);
+},
+horizontalChanged: function() {
+this.$.strategy.setHorizontal(this.horizontal);
+},
+verticalChanged: function() {
+this.$.strategy.setVertical(this.vertical);
+},
+setScrollLeft: function(a) {
+this.scrollLeft = a, this.$.strategy.setScrollLeft(this.scrollLeft);
+},
+setScrollTop: function(a) {
+this.scrollTop = a, this.$.strategy.setScrollTop(a);
+},
+getScrollLeft: function() {
+return this.$.strategy.getScrollLeft();
+},
+getScrollTop: function() {
+return this.$.strategy.getScrollTop();
+},
+getScrollBounds: function() {
+return this.$.strategy.getScrollBounds();
+},
+scrollIntoView: function(a, b) {
+this.$.strategy.scrollIntoView(a, b);
+},
+scrollTo: function(a, b) {
+this.$.strategy.scrollTo(a, b);
+},
+scrollToControl: function(a, b) {
+this.scrollToNode(a.hasNode(), b);
+},
+scrollToNode: function(a, b) {
+this.$.strategy.scrollToNode(a, b);
+},
+domScroll: function(a, b) {
+return this.$.strategy.domScroll && b.originator == this && this.$.strategy.scroll(a, b), this.doScroll(b), !0;
+},
+shouldStopScrollEvent: function(a) {
+return this.preventScrollPropagation && a.originator.owner != this.$.strategy;
+},
+scrollStart: function(a, b) {
+return this.shouldStopScrollEvent(b);
+},
+scroll: function(a, b) {
+return b.dispatchTarget ? this.preventScrollPropagation && b.originator != this && b.originator.owner != this.$.strategy : this.shouldStopScrollEvent(b);
+},
+scrollStop: function(a, b) {
+return this.shouldStopScrollEvent(b);
+},
+scrollToTop: function() {
+this.setScrollTop(0);
+},
+scrollToBottom: function() {
+this.setScrollTop(this.getScrollBounds().maxTop);
+},
+scrollToRight: function() {
+this.setScrollTop(this.getScrollBounds().maxLeft);
+},
+scrollToLeft: function() {
+this.setScrollLeft(0);
+},
+stabilize: function() {
+var a = this.getStrategy();
+a.stabilize && a.stabilize();
+}
+}), enyo.Scroller.hasTouchScrolling() && (enyo.Scroller.prototype.strategyKind = enyo.Scroller.getTouchStrategy());
+
+// Animator.js
+
+enyo.kind({
+name: "enyo.Animator",
+kind: "Component",
+published: {
+duration: 350,
+startValue: 0,
+endValue: 1,
+node: null,
+easingFunction: enyo.easing.cubicOut
+},
+events: {
+onStep: "",
+onEnd: "",
+onStop: ""
+},
+constructed: function() {
+this.inherited(arguments), this._next = enyo.bind(this, "next");
+},
+destroy: function() {
+this.stop(), this.inherited(arguments);
+},
+play: function(a) {
+return this.stop(), this.reversed = !1, a && enyo.mixin(this, a), this.t0 = this.t1 = enyo.now(), this.value = this.startValue, this.job = !0, this.next(), this;
+},
+stop: function() {
+if (this.isAnimating()) return this.cancel(), this.fire("onStop"), this;
+},
+reverse: function() {
+if (this.isAnimating()) {
+this.reversed = !this.reversed;
+var a = this.t1 = enyo.now(), b = a - this.t0;
+this.t0 = a + b - this.duration;
+var c = this.startValue;
+return this.startValue = this.endValue, this.endValue = c, this;
+}
+},
+isAnimating: function() {
+return Boolean(this.job);
+},
+requestNext: function() {
+this.job = enyo.requestAnimationFrame(this._next, this.node);
+},
+cancel: function() {
+enyo.cancelRequestAnimationFrame(this.job), this.node = null, this.job = null;
+},
+shouldEnd: function() {
+return this.dt >= this.duration;
+},
+next: function() {
+this.t1 = enyo.now(), this.dt = this.t1 - this.t0;
+var a = this.fraction = enyo.easedLerp(this.t0, this.duration, this.easingFunction, this.reversed);
+this.value = this.startValue + a * (this.endValue - this.startValue), a >= 1 || this.shouldEnd() ? (this.value = this.endValue, this.fraction = 1, this.fire("onStep"), this.fire("onEnd"), this.cancel()) : (this.fire("onStep"), this.requestNext());
+},
+fire: function(a) {
+var b = this[a];
+enyo.isString(b) ? this.bubble(a) : b && b.call(this.context || window, this);
+}
+});
+
+// BaseLayout.js
+
+enyo.kind({
+name: "enyo.BaseLayout",
+kind: enyo.Layout,
+layoutClass: "enyo-positioned",
+reflow: function() {
+enyo.forEach(this.container.children, function(a) {
+a.fit !== null && a.addRemoveClass("enyo-fit", a.fit);
+}, this);
+}
+});
+
+// Image.js
+
+enyo.kind({
+name: "enyo.Image",
+noEvents: !1,
+tag: "img",
+attributes: {
+onload: enyo.bubbler,
+onerror: enyo.bubbler,
+draggable: "false"
+},
+create: function() {
+this.noEvents && (delete this.attributes.onload, delete this.attributes.onerror), this.inherited(arguments);
+}
+});
+
+// Input.js
+
+enyo.kind({
+name: "enyo.Input",
+published: {
+value: "",
+placeholder: "",
+type: "",
+disabled: !1
+},
+events: {
+onDisabledChange: ""
+},
+defaultFocus: !1,
+tag: "input",
+classes: "enyo-input",
+attributes: {
+onfocus: enyo.bubbler,
+onblur: enyo.bubbler
+},
+handlers: {
+oninput: "input",
+onclear: "clear",
+ondragstart: "dragstart"
+},
+create: function() {
+enyo.platform.ie && (this.handlers.onkeyup = "iekeyup"), this.inherited(arguments), this.placeholderChanged(), this.type && this.typeChanged(), this.valueChanged();
+},
+rendered: function() {
+this.inherited(arguments), this.disabledChanged(), this.defaultFocus && this.focus();
+},
+typeChanged: function() {
+this.setAttribute("type", this.type);
+},
+placeholderChanged: function() {
+this.setAttribute("placeholder", this.placeholder);
+},
+disabledChanged: function() {
+this.setAttribute("disabled", this.disabled), this.bubble("onDisabledChange");
+},
+getValue: function() {
+return this.getNodeProperty("value", this.value);
+},
+valueChanged: function() {
+this.setAttribute("value", this.value), this.setNodeProperty("value", this.value);
+},
+iekeyup: function(a, b) {
+var c = enyo.platform.ie, d = b.keyCode;
+(c <= 8 || c == 9 && (d == 8 || d == 46)) && this.bubble("oninput", b);
+},
+clear: function() {
+this.setValue("");
+},
+focus: function() {
+this.hasNode() && this.node.focus();
+},
+dragstart: function() {
+return !0;
+}
+});
+
+// RichText.js
+
+enyo.kind({
+name: "enyo.RichText",
+classes: "enyo-richtext enyo-selectable",
+published: {
+allowHtml: !0,
+disabled: !1,
+value: ""
+},
+defaultFocus: !1,
+statics: {
+osInfo: [ {
+os: "android",
+version: 3
+}, {
+os: "ios",
+version: 5
+} ],
+hasContentEditable: function() {
+for (var a = 0, b, c; b = enyo.RichText.osInfo[a]; a++) if (enyo.platform[b.os] < b.version) return !1;
+return !0;
+}
+},
+kind: enyo.Input,
+attributes: {
+contenteditable: !0
+},
+handlers: {
+onfocus: "focusHandler",
+onblur: "blurHandler"
+},
+create: function() {
+this.setTag(enyo.RichText.hasContentEditable() ? "div" : "textarea"), this.inherited(arguments);
+},
+focusHandler: function() {
+this._value = this.getValue();
+},
+blurHandler: function() {
+this._value !== this.getValue() && this.bubble("onchange");
+},
+valueChanged: function() {
+this.hasFocus() ? (this.selectAll(), this.insertAtCursor(this.value)) : this.setPropertyValue("content", this.value, "contentChanged");
+},
+getValue: function() {
+if (this.hasNode()) return this.node.innerHTML;
+},
+hasFocus: function() {
+if (this.hasNode()) return document.activeElement === this.node;
+},
+getSelection: function() {
+if (this.hasFocus()) return window.getSelection();
+},
+removeSelection: function(a) {
+var b = this.getSelection();
+b && b[a ? "collapseToStart" : "collapseToEnd"]();
+},
+modifySelection: function(a, b, c) {
+var d = this.getSelection();
+d && d.modify(a || "move", b, c);
+},
+moveCursor: function(a, b) {
+this.modifySelection("move", a, b);
+},
+moveCursorToEnd: function() {
+this.moveCursor("forward", "documentboundary");
+},
+moveCursorToStart: function() {
+this.moveCursor("backward", "documentboundary");
+},
+selectAll: function() {
+this.hasFocus() && document.execCommand("selectAll");
+},
+insertAtCursor: function(a) {
+if (this.hasFocus()) {
+var b = this.allowHtml ? a : enyo.Control.escapeHtml(a).replace(/\n/g, "<br/>");
+document.execCommand("insertHTML", !1, b);
+}
+}
+});
+
+// TextArea.js
+
+enyo.kind({
+name: "enyo.TextArea",
+kind: enyo.Input,
+tag: "textarea",
+classes: "enyo-textarea",
+rendered: function() {
+this.inherited(arguments), this.valueChanged();
+}
+});
+
+// Select.js
+
+enyo.kind({
+name: "enyo.Select",
+published: {
+selected: 0
+},
+handlers: {
+onchange: "change"
+},
+tag: "select",
+defaultKind: "enyo.Option",
+rendered: function() {
+this.inherited(arguments), this.selectedChanged();
+},
+getSelected: function() {
+return Number(this.getNodeProperty("selectedIndex", this.selected));
+},
+setSelected: function(a) {
+this.setPropertyValue("selected", Number(a), "selectedChanged");
+},
+selectedChanged: function() {
+this.setNodeProperty("selectedIndex", this.selected);
+},
+change: function() {
+this.selected = this.getSelected();
+},
+render: function() {
+enyo.platform.ie ? this.parent.render() : this.inherited(arguments);
+},
+getValue: function() {
+if (this.hasNode()) return this.node.value;
+}
+}), enyo.kind({
+name: "enyo.Option",
+published: {
+value: ""
+},
+tag: "option",
+create: function() {
+this.inherited(arguments), this.valueChanged();
+},
+valueChanged: function() {
+this.setAttribute("value", this.value);
+}
+}), enyo.kind({
+name: "enyo.OptionGroup",
+published: {
+label: ""
+},
+tag: "optgroup",
+defaultKind: "enyo.Option",
+create: function() {
+this.inherited(arguments), this.labelChanged();
+},
+labelChanged: function() {
+this.setAttribute("label", this.label);
+}
+});
+
+// Group.js
+
+enyo.kind({
+name: "enyo.Group",
+published: {
+highlander: !0,
+active: null
+},
+handlers: {
+onActivate: "activate"
+},
+activate: function(a, b) {
+this.highlander && (b.originator.active ? this.setActive(b.originator) : b.originator == this.active && this.active.setActive(!0));
+},
+activeChanged: function(a) {
+a && (a.setActive(!1), a.removeClass("active")), this.active && this.active.addClass("active");
+}
+});
+
+// GroupItem.js
+
+enyo.kind({
+name: "enyo.GroupItem",
+published: {
+active: !1
+},
+rendered: function() {
+this.inherited(arguments), this.activeChanged();
+},
+activeChanged: function() {
+this.bubble("onActivate");
+}
+});
+
+// ToolDecorator.js
+
+enyo.kind({
+name: "enyo.ToolDecorator",
+kind: enyo.GroupItem,
+classes: "enyo-tool-decorator"
+});
+
+// Button.js
+
+enyo.kind({
+name: "enyo.Button",
+kind: enyo.ToolDecorator,
+tag: "button",
+published: {
+disabled: !1
+},
+create: function() {
+this.inherited(arguments), this.disabledChanged();
+},
+disabledChanged: function() {
+this.setAttribute("disabled", this.disabled);
+},
+tap: function() {
+this.setActive(!0);
+}
+});
+
+// Checkbox.js
+
+enyo.kind({
+name: "enyo.Checkbox",
+kind: enyo.Input,
+classes: "enyo-checkbox",
+events: {
+onActivate: ""
+},
+published: {
+checked: !1,
+active: !1,
+type: "checkbox"
+},
+kindClasses: "",
+handlers: {
+onchange: "change",
+onclick: "click"
+},
+create: function() {
+this.inherited(arguments);
+},
+rendered: function() {
+this.inherited(arguments), this.active && this.activeChanged(), this.checkedChanged();
+},
+getChecked: function() {
+return Boolean(this.getNodeProperty("checked", this.checked));
+},
+checkedChanged: function() {
+this.setNodeProperty("checked", this.checked), this.setAttribute("checked", this.checked ? "checked" : ""), this.setActive(this.checked);
+},
+activeChanged: function() {
+this.active = Boolean(this.active), this.setChecked(this.active), this.bubble("onActivate");
+},
+setValue: function(a) {
+this.setChecked(Boolean(a));
+},
+getValue: function() {
+return this.getChecked();
+},
+valueChanged: function() {},
+change: function() {
+this.setActive(this.getChecked());
+},
+click: function(a, b) {
+enyo.platform.ie <= 8 && this.bubble("onchange", b);
+}
+});
+
+// Repeater.js
+
+enyo.kind({
+name: "enyo.Repeater",
+published: {
+count: 0
+},
+events: {
+onSetupItem: ""
+},
+create: function() {
+this.inherited(arguments), this.countChanged();
+},
+initComponents: function() {
+this.itemComponents = this.components || this.kindComponents, this.components = this.kindComponents = null, this.inherited(arguments);
+},
+setCount: function(a) {
+this.setPropertyValue("count", a, "countChanged");
+},
+countChanged: function() {
+this.build();
+},
+itemAtIndex: function(a) {
+return this.controlAtIndex(a);
+},
+build: function() {
+this.destroyClientControls();
+for (var a = 0, b; a < this.count; a++) b = this.createComponent({
+kind: "enyo.OwnerProxy",
+index: a
+}), b.createComponents(this.itemComponents), this.doSetupItem({
+index: a,
+item: b
+});
+this.render();
+},
+renderRow: function(a) {
+var b = this.itemAtIndex(a);
+this.doSetupItem({
+index: a,
+item: b
+});
+}
+}), enyo.kind({
+name: "enyo.OwnerProxy",
+tag: null,
+decorateEvent: function(a, b, c) {
+b && (b.index = this.index), this.inherited(arguments);
+},
+delegateEvent: function(a, b, c, d, e) {
+a == this && (a = this.owner.owner), this.inherited(arguments, [ a, b, c, d, e ]);
+}
+});
+
+// DragAvatar.js
+
+enyo.kind({
+name: "enyo._DragAvatar",
+style: "position: absolute; z-index: 10; pointer-events: none; cursor: move;",
+showing: !1,
+showingChanged: function() {
+this.inherited(arguments), document.body.style.cursor = this.showing ? "move" : null;
+}
+}), enyo.kind({
+name: "enyo.DragAvatar",
+kind: enyo.Component,
+published: {
+showing: !1,
+offsetX: 20,
+offsetY: 30
+},
+initComponents: function() {
+this.avatarComponents = this.components, this.components = null, this.inherited(arguments);
+},
+requireAvatar: function() {
+this.avatar || (this.avatar = this.createComponent({
+kind: enyo._DragAvatar,
+parentNode: document.body,
+showing: !1,
+components: this.avatarComponents
+}).render());
+},
+showingChanged: function() {
+this.avatar.setShowing(this.showing), document.body.style.cursor = this.showing ? "move" : null;
+},
+drag: function(a) {
+this.requireAvatar(), this.avatar.setBounds({
+top: a.pageY - this.offsetY,
+left: a.pageX + this.offsetX
+}), this.show();
+},
+show: function() {
+this.setShowing(!0);
+},
+hide: function() {
+this.setShowing(!1);
+}
+});
+
+// FloatingLayer.js
+
+enyo.kind({
+name: "enyo.FloatingLayer",
+create: function() {
+this.inherited(arguments), this.setParent(null);
+},
+render: function() {
+return this.parentNode = document.body, this.inherited(arguments);
+},
+generateInnerHtml: function() {
+return "";
+},
+beforeChildRender: function() {
+this.hasNode() || this.render();
+},
+teardownChildren: function() {}
+}), enyo.floatingLayer = new enyo.FloatingLayer;
+
+// Popup.js
+
+enyo.kind({
+name: "enyo.Popup",
+classes: "enyo-popup",
+published: {
+modal: !1,
+autoDismiss: !0,
+floating: !1,
+centered: !1
+},
+showing: !1,
+handlers: {
+ondown: "down",
+onkeydown: "keydown",
+onfocus: "focus",
+onblur: "blur",
+onRequestShow: "requestShow",
+onRequestHide: "requestHide"
+},
+captureEvents: !0,
+events: {
+onShow: "",
+onHide: ""
+},
+tools: [ {
+kind: "Signals",
+onKeydown: "keydown"
+} ],
+create: function() {
+this.inherited(arguments), this.canGenerate = !this.floating;
+},
+render: function() {
+this.floating && (enyo.floatingLayer.hasNode() || enyo.floatingLayer.render(), this.parentNode = enyo.floatingLayer.hasNode()), this.inherited(arguments);
+},
+destroy: function() {
+this.showing && this.release(), this.inherited(arguments);
+},
+reflow: function() {
+this.updatePosition(), this.inherited(arguments);
+},
+calcViewportSize: function() {
+if (window.innerWidth) return {
+width: window.innerWidth,
+height: window.innerHeight
+};
+var a = document.documentElement;
+return {
+width: a.offsetWidth,
+height: a.offsetHeight
+};
+},
+updatePosition: function() {
+if (this.centered) {
+var a = this.calcViewportSize(), b = this.getBounds();
+this.addStyles("top: " + Math.max((a.height - b.height) / 2, 0) + "px; left: " + Math.max((a.width - b.width) / 2, 0) + "px;");
+}
+},
+showingChanged: function() {
+this.floating && this.showing && !this.hasNode() && this.render(), this.centered && this.applyStyle("visibility", "hidden"), this.inherited(arguments), this.showing ? (this.resized(), this.captureEvents && this.capture()) : this.captureEvents && this.release(), this.centered && this.applyStyle("visibility", null), this.hasNode() && this[this.showing ? "doShow" : "doHide"]();
+},
+capture: function() {
+enyo.dispatcher.capture(this, !this.modal);
+},
+release: function() {
+enyo.dispatcher.release();
+},
+down: function(a, b) {
+this.modal && !b.dispatchTarget.isDescendantOf(this) && b.preventDefault();
+},
+tap: function(a, b) {
+if (this.autoDismiss && !b.dispatchTarget.isDescendantOf(this)) return this.hide(), !0;
+},
+keydown: function(a, b) {
+this.showing && this.autoDismiss && b.keyCode == 27 && this.hide();
+},
+blur: function(a, b) {
+b.dispatchTarget.isDescendantOf(this) && (this.lastFocus = b.originator);
+},
+focus: function(a, b) {
+var c = b.dispatchTarget;
+if (this.modal && !c.isDescendantOf(this)) {
+c.hasNode() && c.node.blur();
+var d = this.lastFocus && this.lastFocus.hasNode() || this.hasNode();
+d && d.focus();
+}
+},
+requestShow: function(a, b) {
+return this.show(), !0;
+},
+requestHide: function(a, b) {
+return this.hide(), !0;
+}
+});
+
+// Selection.js
+
+enyo.kind({
+name: "enyo.Selection",
+kind: enyo.Component,
+published: {
+multi: !1
+},
+events: {
+onSelect: "",
+onDeselect: "",
+onChange: ""
+},
+create: function() {
+this.clear(), this.inherited(arguments);
+},
+multiChanged: function() {
+this.multi || this.clear(), this.doChange();
+},
+highlander: function(a) {
+this.multi || this.deselect(this.lastSelected);
+},
+clear: function() {
+this.selected = {};
+},
+isSelected: function(a) {
+return this.selected[a];
+},
+setByKey: function(a, b, c) {
+if (b) this.selected[a] = c || !0, this.lastSelected = a, this.doSelect({
+key: a,
+data: this.selected[a]
+}); else {
+var d = this.isSelected(a);
+delete this.selected[a], this.doDeselect({
+key: a,
+data: d
+});
+}
+this.doChange();
+},
+deselect: function(a) {
+this.isSelected(a) && this.setByKey(a, !1);
+},
+select: function(a, b) {
+this.multi ? this.setByKey(a, !this.isSelected(a), b) : this.isSelected(a) || (this.highlander(), this.setByKey(a, !0, b));
+},
+toggle: function(a, b) {
+!this.multi && this.lastSelected != a && this.deselect(this.lastSelected), this.setByKey(a, !this.isSelected(a), b);
+},
+getSelected: function() {
+return this.selected;
+},
+remove: function(a) {
+var b = {};
+for (var c in this.selected) c < a ? b[c] = this.selected[c] : c > a && (b[c - 1] = this.selected[c]);
+this.selected = b;
+}
+});
diff --git a/html/images/html5-logo.jpg b/html/images/html5-logo.jpg
new file mode 100644
index 0000000..f8bcfaf
--- /dev/null
+++ b/html/images/html5-logo.jpg
Binary files differ
diff --git a/html/images/liogo.png b/html/images/liogo.png
new file mode 100644
index 0000000..60d7606
--- /dev/null
+++ b/html/images/liogo.png
Binary files differ
diff --git a/html/index.html b/html/index.html
new file mode 100644
index 0000000..e79c5a7
--- /dev/null
+++ b/html/index.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+<head>
+ <title>Enyo Activity</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
+ <link href="enyo/enyo.css" rel="stylesheet" type="text/css" />
+ <script src="enyo/enyo.js" type="text/javascript"></script>
+ <script src="package.js" type="text/javascript"></script>
+ <link href="styles.css" rel="stylesheet" type="text/css" />
+</head>
+<body>
+<script type="text/javascript">
+ new App().write();
+</script>
+</body>
+</html>
diff --git a/html/lib/canvas/Canvas.js b/html/lib/canvas/Canvas.js
new file mode 100644
index 0000000..408047a
--- /dev/null
+++ b/html/lib/canvas/Canvas.js
@@ -0,0 +1,68 @@
+/**
+ _enyo.Canvas_ is a control that generates a &lt;canvas&gt; HTML tag. It may
+ contain other canvas components that are derived not from
+ <a href="#enyo.Control">enyo.Control</a>, but from
+ <a href="#enyo.canvas.Control">enyo.canvas.Control</a>. These aren't true
+ controls in the sense of being DOM elements; they are, rather, shapes drawn
+ into the canvas.
+*/
+enyo.kind({
+ name: "enyo.Canvas",
+ kind: enyo.Control,
+ tag: "canvas",
+ attributes: {
+ //* Width of the canvas element
+ width: 500,
+ //* Height of the canvas element
+ height: 500
+ },
+ defaultKind: "enyo.canvas.Control",
+ //* @protected
+ generateInnerHtml: function() {
+ return '';
+ },
+ teardownChildren: function() {
+ },
+ rendered: function() {
+ this.renderChildren();
+ },
+ /*
+ addChild and removeChild of Control kind assumes children are Controls.
+ CanvasControls are not, so we use UiComponent's version, the superkind of Control
+ */
+ addChild: function() {
+ enyo.UiComponent.prototype.addChild.apply(this, arguments);
+ },
+ removeChild: function() {
+ enyo.UiComponent.prototype.removeChild.apply(this, arguments);
+ },
+ renderChildren: function(inContext) {
+ var ctx = inContext;
+ var canvas = this.hasNode();
+ if (!ctx) {
+ if (canvas.getContext) {
+ ctx = canvas.getContext('2d');
+ }
+ }
+ if (ctx) {
+ for (var i=0, c; c=this.children[i]; i++) {
+ c.render(ctx);
+ }
+ }
+ },
+ //* @public
+ /**
+ Refreshes the canvas context, clears existing drawings, and redraws all
+ of the children.
+ */
+ update: function() {
+ var canvas = this.hasNode();
+ if (canvas.getContext) {
+ var ctx = canvas.getContext('2d');
+ var b = this.getBounds();
+ // clear canvas
+ ctx.clearRect(0, 0, b.width, b.height);
+ this.renderChildren(ctx);
+ }
+ }
+});
diff --git a/html/lib/canvas/CanvasControl.js b/html/lib/canvas/CanvasControl.js
new file mode 100644
index 0000000..ce16446
--- /dev/null
+++ b/html/lib/canvas/CanvasControl.js
@@ -0,0 +1,52 @@
+/**
+ The base kind for items that live inside an
+ <a href="#enyo.Canvas">enyo.Canvas</a> control.
+
+ If you're using this kind directly, you may implement an _onRender_ event
+ handler in the owner to handle drawing into the canvas.
+
+ If you're deriving a new kind based on this one, override the _renderSelf_
+ method and use that for your drawing code.
+*/
+enyo.kind({
+ name: "enyo.canvas.Control",
+ kind: enyo.UiComponent,
+ defaultKind: "enyo.canvas.Control",
+ published: {
+ //* Structure with l (left), t (top), w (width), and h (height) members.
+ //* The default constructor sets those properties to random values.
+ bounds: null
+ },
+ events: {
+ //* Event providing hook to render this control. The event structure
+ //* includes a _context_ member holding the active canvas context.
+ onRender: ""
+ },
+ //* @protected
+ constructor: function() {
+ this.bounds = {l: enyo.irand(400), t: enyo.irand(400), w: enyo.irand(100), h: enyo.irand(100)};
+ this.inherited(arguments);
+ },
+ importProps: function(inProps) {
+ this.inherited(arguments);
+ if (inProps.bounds) {
+ enyo.mixin(this.bounds, inProps.bounds);
+ delete inProps.bounds;
+ }
+ },
+ renderSelf: function(inContext) {
+ this.doRender({context: inContext});
+ },
+ render: function(inContext) {
+ if (this.children.length) {
+ this.renderChildren(inContext);
+ } else {
+ this.renderSelf(inContext);
+ }
+ },
+ renderChildren: function(inContext) {
+ for (var i=0, c; c=this.children[i]; i++) {
+ c.render(inContext);
+ }
+ }
+});
diff --git a/html/lib/canvas/Circle.js b/html/lib/canvas/Circle.js
new file mode 100644
index 0000000..dfe8283
--- /dev/null
+++ b/html/lib/canvas/Circle.js
@@ -0,0 +1,14 @@
+/**
+ Canvas control that draws a circle fitting the parameters specified in the
+ _bounds_ property.
+*/
+enyo.kind({
+ name: "enyo.canvas.Circle",
+ kind: enyo.canvas.Shape,
+ //@ protected
+ renderSelf: function(ctx) {
+ ctx.beginPath();
+ ctx.arc(this.bounds.l, this.bounds.t, this.bounds.w, 0, Math.PI*2);
+ this.draw(ctx);
+ }
+});
diff --git a/html/lib/canvas/Image.js b/html/lib/canvas/Image.js
new file mode 100644
index 0000000..018f275
--- /dev/null
+++ b/html/lib/canvas/Image.js
@@ -0,0 +1,26 @@
+/**
+ Canvas control that draws an image, stretched to fit the rectangle specified
+ by the _bounds_ property.
+*/
+enyo.kind({
+ name: "enyo.canvas.Image",
+ kind: enyo.canvas.Control,
+ published: {
+ //* Source URL for the image
+ src: ""
+ },
+ //* @protected
+ create: function() {
+ this.image = new Image();
+ this.inherited(arguments);
+ this.srcChanged();
+ },
+ srcChanged: function() {
+ if (this.src) {
+ this.image.src = this.src;
+ }
+ },
+ renderSelf: function(ctx) {
+ ctx.drawImage(this.image, this.bounds.l, this.bounds.t);
+ }
+}); \ No newline at end of file
diff --git a/html/lib/canvas/Rectangle.js b/html/lib/canvas/Rectangle.js
new file mode 100644
index 0000000..8f53412
--- /dev/null
+++ b/html/lib/canvas/Rectangle.js
@@ -0,0 +1,25 @@
+/**
+ Canvas control that draws a rectangle fitting the parameters specified in
+ the _bounds_ property.
+*/
+enyo.kind({
+ name: "enyo.canvas.Rectangle",
+ kind: enyo.canvas.Shape,
+ published: {
+ clear: false
+ },
+ //* @protected
+ renderSelf: function(ctx) {
+ if (this.clear) {
+ ctx.clearRect(this.bounds.l, this.bounds.t, this.bounds.w, this.bounds.h);
+ } else {
+ this.draw(ctx);
+ }
+ },
+ fill: function(ctx) {
+ ctx.fillRect(this.bounds.l, this.bounds.t, this.bounds.w, this.bounds.h);
+ },
+ outline: function(ctx) {
+ ctx.strokeRect(this.bounds.l, this.bounds.t, this.bounds.w, this.bounds.h);
+ }
+});
diff --git a/html/lib/canvas/Shape.js b/html/lib/canvas/Shape.js
new file mode 100644
index 0000000..14b0d9a
--- /dev/null
+++ b/html/lib/canvas/Shape.js
@@ -0,0 +1,37 @@
+/**
+ The base kind for shapes that can be drawn into the canvas.
+ This doesn't have a default rendering, but an event handler
+ may call the _draw_ method on it.
+
+ Kinds derived from this one should provide their own implementation of
+ _renderSelf_. If more complex operations are needed for filled mode or
+ outline mode, override the _fill_ or _outline_ methods, respectively.
+*/
+enyo.kind({
+ name: "enyo.canvas.Shape",
+ kind: enyo.canvas.Control,
+ published: {
+ //* Color used to draw the interior of the shape
+ color: "red",
+ //* Color used to draw the outline of the shape
+ outlineColor: ""
+ },
+ //* @protected
+ fill: function(inContext) {
+ inContext.fill();
+ },
+ outline: function(inContext) {
+ inContext.stroke();
+ },
+ //* @public
+ draw: function(inContext) {
+ if (this.color) {
+ inContext.fillStyle = this.color;
+ this.fill(inContext);
+ }
+ if (this.outlineColor) {
+ inContext.strokeStyle = this.outlineColor;
+ this.outline(inContext);
+ }
+ }
+});
diff --git a/html/lib/canvas/Text.js b/html/lib/canvas/Text.js
new file mode 100644
index 0000000..a470be9
--- /dev/null
+++ b/html/lib/canvas/Text.js
@@ -0,0 +1,25 @@
+//* Canvas control that draws a text string.
+enyo.kind({
+ name: "enyo.canvas.Text",
+ kind: enyo.canvas.Shape,
+ published: {
+ //* The text to draw
+ text: "",
+ //* CSS font specification used to select a font for drawing
+ font: "12pt Arial",
+ //* Text alignment within the rectangle specified by the _bounds_ property
+ align: "left"
+ },
+ //* @protected
+ renderSelf: function(ctx) {
+ ctx.textAlign = this.align;
+ ctx.font = this.font;
+ this.draw(ctx);
+ },
+ fill: function(ctx) {
+ ctx.fillText(this.text, this.bounds.l, this.bounds.t);
+ },
+ outline: function(ctx) {
+ ctx.strokeText(this.text, this.bounds.l, this.bounds.t);
+ }
+});
diff --git a/html/lib/canvas/package.js b/html/lib/canvas/package.js
new file mode 100644
index 0000000..0c62c5d
--- /dev/null
+++ b/html/lib/canvas/package.js
@@ -0,0 +1,9 @@
+enyo.depends(
+ "Canvas.js",
+ "CanvasControl.js",
+ "Shape.js",
+ "Circle.js",
+ "Rectangle.js",
+ "Text.js",
+ "Image.js"
+);
diff --git a/html/lib/fu/README.md b/html/lib/fu/README.md
new file mode 100644
index 0000000..96c3956
--- /dev/null
+++ b/html/lib/fu/README.md
@@ -0,0 +1,3 @@
+#fu
+
+> A very simple enyo package. \ No newline at end of file
diff --git a/html/lib/fu/package.js b/html/lib/fu/package.js
new file mode 100644
index 0000000..3d8cebe
--- /dev/null
+++ b/html/lib/fu/package.js
@@ -0,0 +1,3 @@
+enyo.depends(
+ "build/fu.css"
+);
diff --git a/html/lib/fu/source/button.css b/html/lib/fu/source/button.css
new file mode 100644
index 0000000..e478a9d
--- /dev/null
+++ b/html/lib/fu/source/button.css
@@ -0,0 +1,48 @@
+.theme-fu button:focus,
+.theme-fu .button:focus {
+ outline: none;
+}
+
+.theme-fu button,
+.theme-fu .button {
+ position: relative;
+ cursor: pointer;
+ /**/
+ color: #404040;
+ background-color: #CDCDCD;
+ /**/
+ /*margin: 2px 8px 2px 1px;*/
+ padding: 0.5em 0.7em;
+ border: 0;
+ -moz-border-radius: .2em;
+ border-radius: .2em;
+ /**/
+ -webkit-box-shadow: 1px 2px 5px rgba(0, 0, 0, 0.5);
+ -moz-box-shadow: 1px 2px 5px rgba(0, 0, 0, 0.5);
+ box-shadow: 1px 2px 5px rgba(0, 0, 0, 0.5);
+}
+
+.theme-fu.dark button,
+.theme-fu.dark .button {
+ background-color: #484848;
+ color: #ababab;
+}
+
+.theme-fu button:enabled:hover, .theme-fu button:hover,
+.theme-fu .button:enabled:hover, .theme-fu .button:hover {
+ background-color: #C0C0C0;
+}
+
+.theme-fu.dark button:enabled:hover, .theme-fu.dark button:hover,
+.theme-fu.dark .button:enabled:hover, .theme-fu.dark .button:hover {
+ background-color: #383838;
+}
+
+.theme-fu button:enabled:active, .theme-fu button:active,
+.theme-fu .active, .theme-fu .button:enabled:active, .theme-fu .button:active {
+ top: 1px;
+ left: 1px;
+ -webkit-box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.5);
+ -moz-box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.5);
+ box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.5);
+} \ No newline at end of file
diff --git a/html/lib/fu/source/input.css b/html/lib/fu/source/input.css
new file mode 100644
index 0000000..c04e49d
--- /dev/null
+++ b/html/lib/fu/source/input.css
@@ -0,0 +1,30 @@
+/*.theme-fu input:focus,*/
+.theme-fu .input:focus,
+.theme-fu .input.focus {
+ outline: none;
+}
+
+/*.theme-fu input,*/
+.theme-fu .input {
+ padding: 0.6em;
+ border: 0;
+ border-radius: 6px;
+ /**/
+ -webkit-box-shadow: inset 1px 1px 5px rgba(0, 0, 0, 0.5);
+ -moz-box-shadow: inset 1px 1px 5px rgba(0, 0, 0, 0.5);
+ box-shadow: inset 1px 1px 5px rgba(0, 0, 0, 0.5);
+}
+
+/*
+.theme-fu .input:enabled:hover, .theme-fu .input:hover {
+ background-color: #C0C0C0;
+}
+
+.theme-fu active, .theme-fu .input:enabled:active, theme-fu .input:active {
+ top: 1px;
+ left: 1px;
+ -webkit-box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.5);
+ -moz-box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.5);
+ box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.5);
+}
+*/ \ No newline at end of file
diff --git a/html/lib/fu/source/minify/minify.bat b/html/lib/fu/source/minify/minify.bat
new file mode 100644
index 0000000..58480e9
--- /dev/null
+++ b/html/lib/fu/source/minify/minify.bat
@@ -0,0 +1,11 @@
+REM @ECHO OFF
+
+SET ROOT=..\..\..\..
+SET NODE=%ROOT%\tools\node.exe
+SET MINIFY=%ROOT%\tools\minify.js
+SET ENYO=%ROOT%\enyo
+
+%NODE% %MINIFY% depends.js -enyo %ENYO% -output fu
+
+del fu.js
+move /Y fu.css ../../build
diff --git a/html/lib/fu/source/minify/minify.sh b/html/lib/fu/source/minify/minify.sh
new file mode 100644
index 0000000..a8087ee
--- /dev/null
+++ b/html/lib/fu/source/minify/minify.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+ENYO=../../../enyo
+MINIFY=$ENYO/tools/minifier/minify.js
+
+if command -v node >/dev/null 2>&1
+then
+ node $MINIFY ./package.js -enyo $ENYO -output fu
+ mv fu.css ../build
+else
+ echo "No node executable found!"
+ exit 1
+fi
diff --git a/html/lib/fu/source/package.js b/html/lib/fu/source/package.js
new file mode 100644
index 0000000..557ba04
--- /dev/null
+++ b/html/lib/fu/source/package.js
@@ -0,0 +1,5 @@
+enyo.depends(
+ "button.css",
+ "input.css",
+ "tab.css"
+);
diff --git a/html/lib/fu/source/tab.css b/html/lib/fu/source/tab.css
new file mode 100644
index 0000000..425e9f1
--- /dev/null
+++ b/html/lib/fu/source/tab.css
@@ -0,0 +1,47 @@
+.theme-fu .tabbar {
+ /*
+ background-color: #C3C3C3;
+ */
+ white-space: nowrap;
+ position: relative;
+}
+
+.theme-fu .tab.focus, .theme-fu .tab:focus {
+ outline: none;
+}
+
+.theme-fu .tab {
+ cursor: pointer;
+ display: inline-block;
+ white-space: nowrap;
+ /**/
+ color: #606060;
+ background-color: #C3C3C3;
+ /**/
+ margin: 0;
+ padding: 0.5em 0.7em;
+ border: 1px solid rgba(50, 50, 50, 0.2);
+ border-top: none;
+ border-left: none;
+ border-radius: 0 0 5px 5px;
+ /**/
+ box-shadow: inset 0px 0px 2px rgba(0, 0, 0, 0.3);
+}
+
+.theme-fu .tab.hover, .tab:enabled:hover {
+ background-color: #C0C0C0;
+}
+
+.theme-fu .tab.active, .theme-fu .active-tab-bg {
+ background-color: #D7D7D7;
+}
+
+.theme-fu .tab.active {
+ /*
+ background-color: #D7D7D7;
+ Xfont-weight: bold;
+ */
+ box-shadow: none;
+ box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.3);
+ color: Black;
+} \ No newline at end of file
diff --git a/html/lib/layout/fittable/package.js b/html/lib/layout/fittable/package.js
new file mode 100644
index 0000000..3fae95b
--- /dev/null
+++ b/html/lib/layout/fittable/package.js
@@ -0,0 +1,3 @@
+enyo.depends(
+ "source"
+); \ No newline at end of file
diff --git a/html/lib/layout/fittable/source/FittableColumns.js b/html/lib/layout/fittable/source/FittableColumns.js
new file mode 100644
index 0000000..d28b6fe
--- /dev/null
+++ b/html/lib/layout/fittable/source/FittableColumns.js
@@ -0,0 +1,41 @@
+/**
+ _enyo.FittableColumns_ provides a container in which items are laid out in a
+ set of vertical columns, with most items having natural size, but one
+ expanding to fill the remaining space. The one that expands is labeled with
+ the attribute _fit: true_.
+
+ For example, the following code will align three components as columns, with
+ the second filling the available container space between the first and third:
+
+ enyo.kind({
+ kind: "FittableColumns",
+ components: [
+ {content: "1"},
+ {content: "2", fit:true},
+ {content: "3"}
+ ]
+ });
+
+ Alternatively, you may set a kind's _layoutKind_ property to
+ <a href="#enyo.FittableColumnsLayout">enyo.FittableColumnsLayout</a>
+ to use a different base kind while still employing the fittable layout
+ strategy, e.g.:
+
+ enyo.kind({
+ kind: enyo.Control,
+ layoutKind: "FittableColumnsLayout",
+ components: [
+ {content: "1"},
+ {content: "2", fit:true},
+ {content: "3"}
+ ]
+ });
+*/
+
+enyo.kind({
+ name: "enyo.FittableColumns",
+ layoutKind: "FittableColumnsLayout",
+ /** By default, items in columns stretch to fit vertically; set to true to
+ avoid this behavior. */
+ noStretch: false
+});
diff --git a/html/lib/layout/fittable/source/FittableLayout.css b/html/lib/layout/fittable/source/FittableLayout.css
new file mode 100644
index 0000000..cdc24b8
--- /dev/null
+++ b/html/lib/layout/fittable/source/FittableLayout.css
@@ -0,0 +1,69 @@
+.enyo-fittable-rows-layout {
+ position: relative;
+}
+
+.enyo-fittable-rows-layout > * {
+ box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ /* float when not stretched */
+ float: left;
+ clear: both;
+}
+
+/* non-floating when stretched */
+.enyo-fittable-rows-layout.enyo-stretch > * {
+ float: none;
+ clear: none;
+}
+
+/* setting to enforce margin collapsing */
+/* NOTE: rows cannot have margin left/right */
+.enyo-fittable-rows-layout.enyo-stretch.enyo-margin-expand > * {
+ float: left;
+ clear: both;
+ width: 100%;
+ /* note: harsh resets */
+ margin-left: 0 !important;
+ margin-right: 0 !important;
+}
+
+.enyo-fittable-columns-layout {
+ position: relative;
+ text-align: left;
+ white-space: nowrap;
+}
+
+.enyo-fittable-columns-layout.enyo-center {
+ text-align: center;
+}
+
+.enyo-fittable-columns-layout > * {
+ box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ vertical-align: top;
+ display: inline-block;
+ white-space: normal;
+}
+
+.enyo-fittable-columns-layout.enyo-tool-decorator > * {
+ vertical-align: middle;
+}
+
+/* repair clobbered white-space setting for pre, code */
+.enyo-fittable-columns-layout > pre, .enyo-fittable-columns-layout > code {
+ white-space: pre;
+}
+
+.enyo-fittable-columns-layout > .enyo-fittable-columns-layout, .enyo-fittable-columns-layout > .onyx-toolbar-inline {
+ white-space: nowrap;
+}
+
+/* NOTE: columns cannot have margin top/bottom */
+.enyo-fittable-columns-layout.enyo-stretch > * {
+ height: 100%;
+ /* note: harsh resets */
+ margin-top: 0 !important;
+ margin-bottom: 0 !important;
+} \ No newline at end of file
diff --git a/html/lib/layout/fittable/source/FittableLayout.js b/html/lib/layout/fittable/source/FittableLayout.js
new file mode 100644
index 0000000..6045e65
--- /dev/null
+++ b/html/lib/layout/fittable/source/FittableLayout.js
@@ -0,0 +1,265 @@
+/**
+ _enyo.FittableLayout_ provides the base positioning and boundary logic for
+ the fittable layout strategy. The fittable layout strategy is based on
+ laying out items in either a set of rows or a set of columns, with most of
+ the items having natural size, but one item expanding to fill the remaining
+ space. The item that expands is labeled with the attribute _fit: true_.
+
+ For example, in the following kind, the second component fills the available
+ space in the container between the first and third components.
+
+ enyo.kind({
+ kind: "FittableRows",
+ components: [
+ {content: "1"},
+ {content: "2", fit:true},
+ {content: "3"}
+ ]
+ });
+
+ <a href="#enyo.FittableColumnsLayout">enyo.FittableColumnsLayout</a> and
+ <a href="#enyo.FittableRowsLayout">enyo.FittableRowsLayout</a> (or their
+ subkinds) are used for layout rather than _enyo.FittableLayout_ because they
+ specify properties that _enyo.FittableLayout_ expects to be available when
+ laying items out.
+*/
+enyo.kind({
+ name: "enyo.FittableLayout",
+ kind: "Layout",
+ //* @protected
+ calcFitIndex: function() {
+ for (var i=0, c$=this.container.children, c; c=c$[i]; i++) {
+ if (c.fit && c.showing) {
+ return i;
+ }
+ }
+ },
+ getFitControl: function() {
+ var c$=this.container.children;
+ var f = c$[this.fitIndex];
+ if (!(f && f.fit && f.showing)) {
+ this.fitIndex = this.calcFitIndex();
+ f = c$[this.fitIndex];
+ }
+ return f;
+ },
+ getLastControl: function() {
+ var c$=this.container.children;
+ var i = c$.length-1;
+ var c = c$[i];
+ while ((c=c$[i]) && !c.showing) {
+ i--;
+ }
+ return c;
+ },
+ _reflow: function(measure, cMeasure, mAttr, nAttr) {
+ this.container.addRemoveClass("enyo-stretch", !this.container.noStretch);
+ var f = this.getFitControl();
+ // no sizing if nothing is fit.
+ if (!f) {
+ return;
+ }
+ //
+ // determine container size, available space
+ var s=0, a=0, b=0, p;
+ var n = this.container.hasNode();
+ // calculate available space
+ if (n) {
+ // measure 1
+ p = enyo.FittableLayout.calcPaddingExtents(n);
+ // measure 2
+ s = n[cMeasure] - (p[mAttr] + p[nAttr]);
+ //console.log("overall size", s);
+ }
+ //
+ // calculate space above fitting control
+ // measure 3
+ var fb = f.getBounds();
+ // offset - container padding.
+ a = fb[mAttr] - ((p && p[mAttr]) || 0);
+ //console.log("above", a);
+ //
+ // calculate space below fitting control
+ var l = this.getLastControl();
+ if (l) {
+ // measure 4
+ var mb = enyo.FittableLayout.getComputedStyleValue(l.hasNode(), "margin", nAttr) || 0;
+ if (l != f) {
+ // measure 5
+ var lb = l.getBounds();
+ // fit offset + size
+ var bf = fb[mAttr] + fb[measure];
+ // last offset + size + ending margin
+ var bl = lb[mAttr] + lb[measure] + mb;
+ // space below is bottom of last item - bottom of fit item.
+ b = bl - bf;
+ } else {
+ b = mb;
+ }
+ }
+
+ // calculate appropriate size for fit control
+ var fs = s - (a + b);
+ //console.log(f.id, fs);
+ // note: must be border-box;
+ f.applyStyle(measure, fs + "px");
+ },
+ //* @public
+ /**
+ Updates the layout to reflect any changes to contained components or the
+ layout container.
+ */
+ reflow: function() {
+ if (this.orient == "h") {
+ this._reflow("width", "clientWidth", "left", "right");
+ } else {
+ this._reflow("height", "clientHeight", "top", "bottom");
+ }
+ },
+ statics: {
+ //* @protected
+ _ieCssToPixelValue: function(inNode, inValue) {
+ var v = inValue;
+ // From the awesome hack by Dean Edwards
+ // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+ var s = inNode.style;
+ // store style and runtime style values
+ var l = s.left;
+ var rl = inNode.runtimeStyle && inNode.runtimeStyle.left;
+ // then put current style in runtime style.
+ if (rl) {
+ inNode.runtimeStyle.left = inNode.currentStyle.left;
+ }
+ // apply given value and measure its pixel value
+ s.left = v;
+ v = s.pixelLeft;
+ // finally restore previous state
+ s.left = l;
+ if (rl) {
+ s.runtimeStyle.left = rl;
+ }
+ return v;
+ },
+ _pxMatch: /px/i,
+ getComputedStyleValue: function(inNode, inProp, inBoundary, inComputedStyle) {
+ var s = inComputedStyle || enyo.dom.getComputedStyle(inNode);
+ if (s) {
+ return parseInt(s.getPropertyValue(inProp + "-" + inBoundary));
+ } else if (inNode && inNode.currentStyle) {
+ var v = inNode.currentStyle[inProp + enyo.cap(inBoundary)];
+ if (!v.match(this._pxMatch)) {
+ v = this._ieCssToPixelValue(inNode, v);
+ }
+ return parseInt(v);
+ }
+ return 0;
+ },
+ //* Gets the boundaries of a node's margin or padding box.
+ calcBoxExtents: function(inNode, inBox) {
+ var s = enyo.dom.getComputedStyle(inNode);
+ return {
+ top: this.getComputedStyleValue(inNode, inBox, "top", s),
+ right: this.getComputedStyleValue(inNode, inBox, "right", s),
+ bottom: this.getComputedStyleValue(inNode, inBox, "bottom", s),
+ left: this.getComputedStyleValue(inNode, inBox, "left", s)
+ };
+ },
+ //* Gets the calculated padding of a node.
+ calcPaddingExtents: function(inNode) {
+ return this.calcBoxExtents(inNode, "padding");
+ },
+ //* Gets the calculated margin of a node.
+ calcMarginExtents: function(inNode) {
+ return this.calcBoxExtents(inNode, "margin");
+ }
+ }
+});
+
+/**
+ _enyo.FittableColumnsLayout_ provides a container in which items are laid
+ out in a set of vertical columns, with most of the items having natural
+ size, but one expanding to fill the remaining space. The one that expands is
+ labeled with the attribute _fit: true_.
+
+ _enyo.FittableColumnsLayout_ is meant to be used as a value for the
+ _layoutKind_ property of other kinds. _layoutKind_ provides a way to add
+ layout behavior in a pluggable fashion while retaining the ability to use a
+ specific base kind.
+
+ For example, the following code will align three components as columns, with
+ the second filling the available container space between the first and third.
+
+ enyo.kind({
+ kind: enyo.Control,
+ layoutKind: "FittableColumnsLayout",
+ components: [
+ {content: "1"},
+ {content: "2", fit:true},
+ {content: "3"}
+ ]
+ });
+
+ Alternatively, if a specific base kind is not needed, then instead of
+ setting the _layoutKind_ attribute, you can set the base kind to
+ <a href="#enyo.FittableColumns">enyo.FittableColumns</a>:
+
+ enyo.kind({
+ kind: "FittableColumns",
+ components: [
+ {content: "1"},
+ {content: "2", fit:true},
+ {content: "3"}
+ ]
+ });
+*/
+enyo.kind({
+ name: "enyo.FittableColumnsLayout",
+ kind: "FittableLayout",
+ orient: "h",
+ layoutClass: "enyo-fittable-columns-layout"
+});
+
+
+/**
+ _enyo.FittableRowsLayout_ provides a container in which items are laid out
+ in a set of horizontal rows, with most of the items having natural size, but
+ one expanding to fill the remaining space. The one that expands is labeled
+ with the attribute _fit: true_.
+
+ _enyo.FittableRowsLayout_ is meant to be used as a value for the
+ _layoutKind_ property of other kinds. _layoutKind_ provides a way to add
+ layout behavior in a pluggable fashion while retaining the ability to use a
+ specific base kind.
+
+ For example, the following code will align three components as rows, with
+ the second filling the available container space between the first and third.
+
+ enyo.kind({
+ kind: enyo.Control,
+ layoutKind: "FittableRowsLayout",
+ components: [
+ {content: "1"},
+ {content: "2", fit:true},
+ {content: "3"}
+ ]
+ });
+
+ Alternatively, if a specific base kind is not needed, then instead of
+ setting the _layoutKind_ attribute, you can set the base kind to
+ <a href="#enyo.FittableRows">enyo.FittableRows</a>:
+
+ enyo.kind({
+ kind: "FittableRows",
+ components: [
+ {content: "1"},
+ {content: "2", fit:true},
+ {content: "3"}
+ ]
+ });
+*/
+enyo.kind({
+ name: "enyo.FittableRowsLayout",
+ kind: "FittableLayout",
+ layoutClass: "enyo-fittable-rows-layout",
+ orient: "v"
+});
diff --git a/html/lib/layout/fittable/source/FittableRows.js b/html/lib/layout/fittable/source/FittableRows.js
new file mode 100644
index 0000000..985f82d
--- /dev/null
+++ b/html/lib/layout/fittable/source/FittableRows.js
@@ -0,0 +1,40 @@
+/**
+ _enyo.FittableRows_ provides a container in which items are laid out in a
+ set of horizontal rows, with most of the items having natural size, but one
+ expanding to fill the remaining space. The one that expands is labeled with
+ the attribute _fit: true_.
+
+ For example, the following code will align three components as rows, with
+ the second filling the available container space between the first and third.
+
+ enyo.kind({
+ kind: "FittableRows",
+ components: [
+ {content: "1"},
+ {content: "2", fit:true},
+ {content: "3"}
+ ]
+ });
+
+ Alternatively, you may set a kind's _layoutKind_ property to
+ <a href="#enyo.FittableRowsLayout">enyo.FittableRowsLayout</a>
+ to use a different base kind while still employing the fittable layout
+ strategy, e.g.:
+
+ enyo.kind({
+ kind: enyo.Control,
+ layoutKind: "FittableRowsLayout",
+ components: [
+ {content: "1"},
+ {content: "2", fit:true},
+ {content: "3"}
+ ]
+ });
+*/
+enyo.kind({
+ name: "enyo.FittableRows",
+ layoutKind: "FittableRowsLayout",
+ /** By default, items in rows stretch to fit horizontally; set to true to
+ avoid this behavior. */
+ noStretch: false
+});
diff --git a/html/lib/layout/fittable/source/design.js b/html/lib/layout/fittable/source/design.js
new file mode 100644
index 0000000..ab94505
--- /dev/null
+++ b/html/lib/layout/fittable/source/design.js
@@ -0,0 +1,23 @@
+/**
+ Description to make fittable kinds available in Ares.
+*/
+Palette.model.push(
+ {name: "fittable", items: [
+ {name: "FittableRows", title: "Vertical stacked layout", icon: "package_new.png", stars: 4.5, version: 2.0, blurb: "Stack of vertical rows, one of which can be made to fit.",
+ inline: {kind: "FittableRows", style: "height: 80px; position: relative;", padding: 4, components: [
+ {style: "background-color: lightblue; border: 1px dotted blue; height: 15px;"},
+ {style: "background-color: lightblue; border: 1px dotted blue;", fit: true},
+ {style: "background-color: lightblue; border: 1px dotted blue; height: 15px;"}
+ ]},
+ config: {content: "$name", isContainer: true, kind: "FittableRows", padding: 10, margin: 10}
+ },
+ {name: "FittableColumns", title: "Horizontal stacked layout", icon: "package_new.png", stars: 4.5, version: 2.0, blurb: "Stack of horizontal columns, one of which can be made to fit.",
+ inline: {kind: "FittableColumns", style: "height: 60px; position: relative;", padding: 4, components: [
+ {style: "background-color: lightblue; border: 1px dotted blue; width: 20px;"},
+ {style: "background-color: lightblue; border: 1px dotted blue;", fit: true},
+ {style: "background-color: lightblue; border: 1px dotted blue; width: 20px;"}
+ ]},
+ config: {content: "$name", isContainer: true, kind: "FittableColumns", padding: 10, margin: 10}
+ }
+ ]}
+); \ No newline at end of file
diff --git a/html/lib/layout/fittable/source/package.js b/html/lib/layout/fittable/source/package.js
new file mode 100644
index 0000000..74201af
--- /dev/null
+++ b/html/lib/layout/fittable/source/package.js
@@ -0,0 +1,6 @@
+enyo.depends(
+ "FittableLayout.css",
+ "FittableLayout.js",
+ "FittableRows.js",
+ "FittableColumns.js"
+); \ No newline at end of file
diff --git a/html/lib/layout/list/package.js b/html/lib/layout/list/package.js
new file mode 100644
index 0000000..f1de4f2
--- /dev/null
+++ b/html/lib/layout/list/package.js
@@ -0,0 +1,3 @@
+enyo.depends(
+ "source/"
+); \ No newline at end of file
diff --git a/html/lib/layout/list/source/AlphaJumpList.js b/html/lib/layout/list/source/AlphaJumpList.js
new file mode 100644
index 0000000..6b6e326
--- /dev/null
+++ b/html/lib/layout/list/source/AlphaJumpList.js
@@ -0,0 +1,37 @@
+/**
+ A control that presents an alphabetic panel that you can select from, in
+ order to perform actions based on the item selected.
+
+ {kind: "AlphaJumpList", onSetupItem: "setupItem",
+ onAlphaJump: "alphaJump",
+ components: [
+ {name: "divider"},
+ {kind: "onyx.Item"}
+ ]
+ }
+
+*/
+enyo.kind({
+ name: "enyo.AlphaJumpList",
+ kind: "List",
+ //* @protected
+ scrollTools: [
+ {name: "jumper", kind: "AlphaJumper"}
+ ],
+ initComponents: function() {
+ this.createChrome(this.scrollTools);
+ this.inherited(arguments);
+ },
+ rendered: function() {
+ this.inherited(arguments);
+ this.centerJumper();
+ },
+ resizeHandler: function() {
+ this.inherited(arguments);
+ this.centerJumper();
+ },
+ centerJumper: function() {
+ var b = this.getBounds(), sb = this.$.jumper.getBounds();
+ this.$.jumper.applyStyle("top", ((b.height - sb.height) / 2) + "px");
+ }
+}); \ No newline at end of file
diff --git a/html/lib/layout/list/source/AlphaJumper.css b/html/lib/layout/list/source/AlphaJumper.css
new file mode 100644
index 0000000..df1265c
--- /dev/null
+++ b/html/lib/layout/list/source/AlphaJumper.css
@@ -0,0 +1,36 @@
+.enyo-alpha-jumper {
+ text-transform: uppercase;
+ font-size: 11px;
+ xborder-radius: 12px;
+ position: absolute;
+ xbackground: white;
+ text-align: center;
+ right: 10px;
+ z-index: 1;
+ color: #333;
+ padding: 0 14px;
+}
+
+.enyo-alpha-jumper > *:first-child {
+ padding-top: 4px;
+ border-radius: 12px 12px 0 0;
+ border-top: 1px solid #333;
+}
+
+.enyo-alpha-jumper > *:last-child {
+ padding-bottom: 4px;
+ border-radius: 0 0 12px 12px;
+ border-bottom: 1px solid #333;
+}
+
+.enyo-alpha-jumper > * {
+ background: #eee;
+ padding: 1px 4px;
+ border-right: 1px solid #333;
+ border-left: 1px solid #333;
+}
+
+.enyo-alpha-jumper > .active {
+ background: #333;
+ color: white;
+} \ No newline at end of file
diff --git a/html/lib/layout/list/source/AlphaJumper.js b/html/lib/layout/list/source/AlphaJumper.js
new file mode 100644
index 0000000..2f85d00
--- /dev/null
+++ b/html/lib/layout/list/source/AlphaJumper.js
@@ -0,0 +1,70 @@
+enyo.kind({
+ name: "enyo.AlphaJumper",
+ classes: "enyo-alpha-jumper enyo-border-box",
+ published: {
+ marker: null
+ },
+ events: {
+ onAlphaJump: ""
+ },
+ handlers: {
+ ondown: "down",
+ onmove: "move",
+ onup: "up"
+ },
+ initComponents: function() {
+ for (var s="A".charCodeAt(0), i=s; i<s+26; i++) {
+ this.createComponent({content: String.fromCharCode(i)});
+ }
+ this.inherited(arguments);
+ },
+ down: function(inSender, inEvent) {
+ if (this.tracking) {
+ enyo.dispatcher.release();
+ }
+ this.tracking = true;
+ if (this.hasNode()) {
+ var b = this.node.getBoundingClientRect();
+ // IE8 does not return width
+ var w = (b.width === undefined) ? (b.right - b.left) : b.width;
+ this.x = b.left + w/2;
+ }
+ enyo.dispatcher.capture(this);
+ this.track(inEvent);
+ },
+ move: function(inSender, inEvent) {
+ if (this.tracking) {
+ this.track(inEvent);
+ }
+ },
+ up: function() {
+ if (this.tracking) {
+ enyo.dispatcher.release();
+ this.setMarker(null);
+ this.tracking = false;
+ }
+ },
+ track: function(inEvent) {
+ var n = document.elementFromPoint(this.x, inEvent.pageY);
+ var c = this.nodeToControl(n);
+ if (c) {
+ this.setMarker(c);
+ }
+ },
+ nodeToControl: function(inNode) {
+ for (var i=0, c$=this.controls, c; c=c$[i]; i++) {
+ if (inNode == c.hasNode()) {
+ return c;
+ }
+ }
+ },
+ markerChanged: function(inLast) {
+ if (inLast) {
+ inLast.removeClass("active");
+ }
+ if (this.marker) {
+ this.marker.addClass("active");
+ this.doAlphaJump({letter: this.marker.getContent(), index: this.marker.indexInContainer()});
+ }
+ }
+}); \ No newline at end of file
diff --git a/html/lib/layout/list/source/FlyweightRepeater.js b/html/lib/layout/list/source/FlyweightRepeater.js
new file mode 100644
index 0000000..01e6d80
--- /dev/null
+++ b/html/lib/layout/list/source/FlyweightRepeater.js
@@ -0,0 +1,175 @@
+/**
+ A control that displays a repeating list of rows, suitable for displaying
+ medium-sized lists (up to ~100 items). A flyweight strategy is employed to
+ render one set of row controls, as needed, for as many rows as are contained
+ in the repeater.
+
+ The FlyweightRepeater's _components_ block contains the controls to be used
+ for a single row. This set of controls will be rendered for each row. You
+ may customize row rendering by handling the _onSetupItem_ event.
+
+ The controls inside a FlyweightRepeater are non-interactive. This means that
+ calling methods that would normally cause rendering to occur (e.g.,
+ _setContent_) will not do so. However, you can force a row to render by
+ calling _renderRow(inRow)_.
+
+ In addition, you can force a row to be temporarily interactive by calling
+ _prepareRow(inRow)_. Call the _lockRow_ method when the interaction is
+ complete.
+
+ For more information, see the documentation on
+ [Lists](https://github.com/enyojs/enyo/wiki/Lists)
+ in the Enyo Developer Guide.
+*/
+enyo.kind({
+ name: "enyo.FlyweightRepeater",
+ published: {
+ //* Number of rows to render
+ count: 0,
+ //* If true, multiple selections are allowed
+ multiSelect: false,
+ //* If true, the selected item will toggle
+ toggleSelected: false
+ },
+ events: {
+ /**
+ Fires once per row at render time, with event object:
+ _{index: <index of row>, selected: <true if row is selected>}_
+ */
+ onSetupItem: ""
+ },
+ components: [
+ {kind: "Selection", onSelect: "selectDeselect", onDeselect: "selectDeselect"},
+ {name: "client"}
+ ],
+ rowOffset: 0,
+ bottomUp: false,
+ //* @protected
+ create: function() {
+ this.inherited(arguments);
+ this.multiSelectChanged();
+ },
+ multiSelectChanged: function() {
+ this.$.selection.setMulti(this.multiSelect);
+ },
+ setupItem: function(inIndex) {
+ this.doSetupItem({index: inIndex, selected: this.isSelected(inIndex)});
+ },
+ //* Renders the list.
+ generateChildHtml: function() {
+ var h = "";
+ this.index = null;
+ // note: can supply a rowOffset
+ // and indicate if rows should be rendered top down or bottomUp
+ for (var i=0, r=0; i<this.count; i++) {
+ r = this.rowOffset + (this.bottomUp ? this.count - i-1 : i);
+ this.setupItem(r);
+ this.$.client.setAttribute("index", r);
+ h += this.inherited(arguments);
+ this.$.client.teardownRender();
+ }
+ return h;
+ },
+ previewDomEvent: function(inEvent) {
+ var i = this.index = this.rowForEvent(inEvent);
+ inEvent.rowIndex = inEvent.index = i;
+ inEvent.flyweight = this;
+ },
+ decorateEvent: function(inEventName, inEvent, inSender) {
+ // decorate event with index found via dom iff event does not already contain an index.
+ var i = (inEvent && inEvent.index != null) ? inEvent.index : this.index;
+ if (inEvent && i != null) {
+ inEvent.index = i;
+ inEvent.flyweight = this;
+ }
+ this.inherited(arguments);
+ },
+ tap: function(inSender, inEvent) {
+ if (this.toggleSelected) {
+ this.$.selection.toggle(inEvent.index);
+ } else {
+ this.$.selection.select(inEvent.index);
+ }
+ },
+ selectDeselect: function(inSender, inEvent) {
+ this.renderRow(inEvent.key);
+ },
+ //* @public
+ //* Returns the repeater's _selection_ component.
+ getSelection: function() {
+ return this.$.selection;
+ },
+ //* Gets the selection state for the given row index.
+ isSelected: function(inIndex) {
+ return this.getSelection().isSelected(inIndex);
+ },
+ //* Renders the row specified by _inIndex_.
+ renderRow: function(inIndex) {
+ //this.index = null;
+ var node = this.fetchRowNode(inIndex);
+ if (node) {
+ this.setupItem(inIndex);
+ node.innerHTML = this.$.client.generateChildHtml();
+ this.$.client.teardownChildren();
+ }
+ },
+ //* Fetches the DOM node for the given row index.
+ fetchRowNode: function(inIndex) {
+ if (this.hasNode()) {
+ var n$ = this.node.querySelectorAll('[index="' + inIndex + '"]');
+ return n$ && n$[0];
+ }
+ },
+ //* Fetches the DOM node for the given event.
+ rowForEvent: function(inEvent) {
+ var n = inEvent.target;
+ var id = this.hasNode().id;
+ while (n && n.parentNode && n.id != id) {
+ var i = n.getAttribute && n.getAttribute("index");
+ if (i !== null) {
+ return Number(i);
+ }
+ n = n.parentNode;
+ }
+ return -1;
+ },
+ //* Prepares the row specified by _inIndex_ such that changes made to the
+ //* controls inside the repeater will be rendered for the given row.
+ prepareRow: function(inIndex) {
+ var n = this.fetchRowNode(inIndex);
+ enyo.FlyweightRepeater.claimNode(this.$.client, n);
+ },
+ //* Prevents rendering of changes made to controls inside the repeater.
+ lockRow: function() {
+ this.$.client.teardownChildren();
+ },
+ //* Prepares the row specified by _inIndex_ such that changes made to the
+ //* controls in the row will be rendered in the given row; then performs the
+ //* function _inFunc_, and, finally, locks the row.
+ performOnRow: function(inIndex, inFunc, inContext) {
+ if (inFunc) {
+ this.prepareRow(inIndex);
+ enyo.call(inContext || null, inFunc);
+ this.lockRow();
+ }
+ },
+ statics: {
+ //* Associates a flyweight rendered control (_inControl_) with a
+ //* rendering context specified by _inNode_.
+ claimNode: function(inControl, inNode) {
+ var n = inNode && inNode.querySelectorAll("#" + inControl.id);
+ n = n && n[0];
+ // FIXME: consider controls generated if we found a node or tag: null, the later so can teardown render
+ inControl.generated = Boolean(n || !inControl.tag);
+ inControl.node = n;
+ if (inControl.node) {
+ inControl.rendered();
+ } else {
+ //enyo.log("Failed to find node for", inControl.id, inControl.generated);
+ }
+ for (var i=0, c$=inControl.children, c; c=c$[i]; i++) {
+ this.claimNode(c, inNode);
+ }
+ }
+ }
+}); \ No newline at end of file
diff --git a/html/lib/layout/list/source/List.css b/html/lib/layout/list/source/List.css
new file mode 100644
index 0000000..72da968
--- /dev/null
+++ b/html/lib/layout/list/source/List.css
@@ -0,0 +1,15 @@
+.enyo-list {
+ position: relative;
+}
+
+.enyo-list-port {
+ overflow: hidden;
+ position: relative;
+ height: 10000000px;
+}
+
+.enyo-list-page {
+ position: absolute;
+ left: 0;
+ right: 0;
+} \ No newline at end of file
diff --git a/html/lib/layout/list/source/List.js b/html/lib/layout/list/source/List.js
new file mode 100644
index 0000000..c4252b9
--- /dev/null
+++ b/html/lib/layout/list/source/List.js
@@ -0,0 +1,368 @@
+/**
+ A control that displays a scrolling list of rows, suitable for displaying
+ very large lists. _enyo.List_ is optimized such that only a small portion of
+ the list is rendered at a given time. A flyweight pattern is employed, in
+ which controls placed inside the list are created once, but rendered for
+ each list item. For this reason, it's best to use only simple controls in
+ a List, such as <a href="#enyo.Control">enyo.Control</a> and
+ <a href="#enyo.Image">enyo.Image</a>.
+
+ A List's _components_ block contains the controls to be used for a single
+ row. This set of controls will be rendered for each row. You may customize
+ row rendering by handling the _onSetupItem_ event.
+
+ Events fired from within list rows contain the _index_ property, which may
+ be used to identify the row from which the event originated.
+
+ The controls inside a List are non-interactive. This means that calling
+ methods that would normally cause rendering to occur (e.g., _setContent_)
+ will not do so. However, you can force a row to render by calling
+ _renderRow(inRow)_.
+
+ In addition, you can force a row to be temporarily interactive by calling
+ _prepareRow(inRow)_. Call the _lockRow_ method when the interaction is
+ complete.
+
+ For more information, see the documentation on
+ [Lists](https://github.com/enyojs/enyo/wiki/Lists)
+ in the Enyo Developer Guide.
+*/
+enyo.kind({
+ name: "enyo.List",
+ kind: "Scroller",
+ classes: "enyo-list",
+ published: {
+ /**
+ The number of rows contained in the list. Note that as the amount of
+ list data changes, _setRows_ can be called to adjust the number of
+ rows. To re-render the list at the current position when the count
+ has changed, call the _refresh_ method.
+ */
+ count: 0,
+ /**
+ The number of rows to be shown on a given list page segment.
+ There is generally no need to adjust this value.
+ */
+ rowsPerPage: 50,
+ /**
+ If true, renders the list such that row 0 is at the bottom of the
+ viewport and the beginning position of the list is scrolled to the
+ bottom
+ */
+ bottomUp: false,
+ //* If true, multiple selections are allowed
+ multiSelect: false,
+ //* If true, the selected item will toggle
+ toggleSelected: false,
+ //* If true, the list will assume all rows have the same height for optimization
+ fixedHeight: false
+ },
+ events: {
+ /**
+ Fires once per row at render time, with event object:
+ _{index: <index of row>}_
+ */
+ onSetupItem: ""
+ },
+ handlers: {
+ onAnimateFinish: "animateFinish"
+ },
+ //* @protected
+ rowHeight: 0,
+ listTools: [
+ {name: "port", classes: "enyo-list-port enyo-border-box", components: [
+ {name: "generator", kind: "FlyweightRepeater", canGenerate: false, components: [
+ {tag: null, name: "client"}
+ ]},
+ {name: "page0", allowHtml: true, classes: "enyo-list-page"},
+ {name: "page1", allowHtml: true, classes: "enyo-list-page"}
+ ]}
+ ],
+ create: function() {
+ this.pageHeights = [];
+ this.inherited(arguments);
+ this.getStrategy().translateOptimized = true;
+ this.bottomUpChanged();
+ this.multiSelectChanged();
+ this.toggleSelectedChanged();
+ },
+ createStrategy: function() {
+ this.controlParentName = "strategy";
+ this.inherited(arguments);
+ this.createChrome(this.listTools);
+ this.controlParentName = "client";
+ this.discoverControlParent();
+ },
+ rendered: function() {
+ this.inherited(arguments);
+ this.$.generator.node = this.$.port.hasNode();
+ this.$.generator.generated = true;
+ this.reset();
+ },
+ resizeHandler: function() {
+ this.inherited(arguments);
+ this.refresh();
+ },
+ bottomUpChanged: function() {
+ this.$.generator.bottomUp = this.bottomUp;
+ this.$.page0.applyStyle(this.pageBound, null);
+ this.$.page1.applyStyle(this.pageBound, null);
+ this.pageBound = this.bottomUp ? "bottom" : "top";
+ if (this.hasNode()) {
+ this.reset();
+ }
+ },
+ multiSelectChanged: function() {
+ this.$.generator.setMultiSelect(this.multiSelect);
+ },
+ toggleSelectedChanged: function() {
+ this.$.generator.setToggleSelected(this.toggleSelected);
+ },
+ countChanged: function() {
+ if (this.hasNode()) {
+ this.updateMetrics();
+ }
+ },
+ updateMetrics: function() {
+ this.defaultPageHeight = this.rowsPerPage * (this.rowHeight || 100);
+ this.pageCount = Math.ceil(this.count / this.rowsPerPage);
+ this.portSize = 0;
+ for (var i=0; i < this.pageCount; i++) {
+ this.portSize += this.getPageHeight(i);
+ }
+ this.adjustPortSize();
+ },
+ generatePage: function(inPageNo, inTarget) {
+ this.page = inPageNo;
+ var r = this.$.generator.rowOffset = this.rowsPerPage * this.page;
+ var rpp = this.$.generator.count = Math.min(this.count - r, this.rowsPerPage);
+ var html = this.$.generator.generateChildHtml();
+ inTarget.setContent(html);
+ var pageHeight = inTarget.getBounds().height;
+ // if rowHeight is not set, use the height from the first generated page
+ if (!this.rowHeight && pageHeight > 0) {
+ this.rowHeight = Math.floor(pageHeight / rpp);
+ this.updateMetrics();
+ }
+ // update known page heights
+ if (!this.fixedHeight) {
+ var h0 = this.getPageHeight(inPageNo);
+ if (h0 != pageHeight && pageHeight > 0) {
+ this.pageHeights[inPageNo] = pageHeight;
+ this.portSize += pageHeight - h0;
+ }
+ }
+ },
+ update: function(inScrollTop) {
+ var updated = false;
+ // get page info for position
+ var pi = this.positionToPageInfo(inScrollTop);
+ // zone line position
+ var pos = pi.pos + this.scrollerHeight/2;
+ // leap-frog zone position
+ var k = Math.floor(pos/Math.max(pi.height, this.scrollerHeight) + 1/2) + pi.no;
+ // which page number for page0 (even number pages)?
+ var p = k % 2 == 0 ? k : k-1;
+ if (this.p0 != p && this.isPageInRange(p)) {
+ //this.log("update page0", p);
+ this.generatePage(p, this.$.page0);
+ this.positionPage(p, this.$.page0);
+ this.p0 = p;
+ updated = true;
+ }
+ // which page number for page1 (odd number pages)?
+ p = k % 2 == 0 ? Math.max(1, k-1) : k;
+ // position data page 1
+ if (this.p1 != p && this.isPageInRange(p)) {
+ //this.log("update page1", p);
+ this.generatePage(p, this.$.page1);
+ this.positionPage(p, this.$.page1);
+ this.p1 = p;
+ updated = true;
+ }
+ if (updated && !this.fixedHeight) {
+ this.adjustBottomPage();
+ this.adjustPortSize();
+ }
+ },
+ updateForPosition: function(inPos) {
+ this.update(this.calcPos(inPos));
+ },
+ calcPos: function(inPos) {
+ return (this.bottomUp ? (this.portSize - this.scrollerHeight - inPos) : inPos);
+ },
+ adjustBottomPage: function() {
+ var bp = this.p0 >= this.p1 ? this.$.page0 : this.$.page1;
+ this.positionPage(bp.pageNo, bp);
+ },
+ adjustPortSize: function() {
+ this.scrollerHeight = this.getBounds().height;
+ var s = Math.max(this.scrollerHeight, this.portSize);
+ this.$.port.applyStyle("height", s + "px");
+ },
+ positionPage: function(inPage, inTarget) {
+ inTarget.pageNo = inPage;
+ var y = this.pageToPosition(inPage);
+ inTarget.applyStyle(this.pageBound, y + "px");
+ },
+ pageToPosition: function(inPage) {
+ var y = 0;
+ var p = inPage;
+ while (p > 0) {
+ p--;
+ y += this.getPageHeight(p);
+ }
+ return y;
+ },
+ positionToPageInfo: function(inY) {
+ var page = -1;
+ var p = this.calcPos(inY);
+ var h = this.defaultPageHeight;
+ while (p >= 0) {
+ page++;
+ h = this.getPageHeight(page);
+ p -= h;
+ }
+ //page = Math.min(page, this.pageCount-1);
+ return {no: page, height: h, pos: p+h};
+ },
+ isPageInRange: function(inPage) {
+ return inPage == Math.max(0, Math.min(this.pageCount-1, inPage));
+ },
+ getPageHeight: function(inPageNo) {
+ return this.pageHeights[inPageNo] || this.defaultPageHeight;
+ },
+ invalidatePages: function() {
+ this.p0 = this.p1 = null;
+ // clear the html in our render targets
+ this.$.page0.setContent("");
+ this.$.page1.setContent("");
+ },
+ invalidateMetrics: function() {
+ this.pageHeights = [];
+ this.rowHeight = 0;
+ this.updateMetrics();
+ },
+ scroll: function(inSender, inEvent) {
+ var r = this.inherited(arguments);
+ this.update(this.getScrollTop());
+ return r;
+ },
+ //* @public
+ scrollToBottom: function() {
+ this.update(this.getScrollBounds().maxTop);
+ this.inherited(arguments);
+ },
+ setScrollTop: function(inScrollTop) {
+ this.update(inScrollTop);
+ this.inherited(arguments);
+ this.twiddle();
+ },
+ getScrollPosition: function() {
+ return this.calcPos(this.getScrollTop());
+ },
+ setScrollPosition: function(inPos) {
+ this.setScrollTop(this.calcPos(inPos));
+ },
+ //* Scrolls to a specific row.
+ scrollToRow: function(inRow) {
+ var page = Math.floor(inRow / this.rowsPerPage);
+ var pageRow = inRow % this.rowsPerPage;
+ var h = this.pageToPosition(page);
+ // update the page
+ this.updateForPosition(h);
+ // call pageToPosition again and this time should return the right pos since the page info is populated
+ h = this.pageToPosition(page);
+ this.setScrollPosition(h);
+ if (page == this.p0 || page == this.p1) {
+ var rowNode = this.$.generator.fetchRowNode(inRow);
+ if (rowNode) {
+ // calc row offset
+ var offset = rowNode.offsetTop;
+ if (this.bottomUp) {
+ offset = this.getPageHeight(page) - rowNode.offsetHeight - offset;
+ }
+ var y = this.getScrollPosition() + offset;
+ this.setScrollPosition(y);
+ }
+ }
+ },
+ //* Scrolls to the beginning of the list.
+ scrollToStart: function() {
+ this[this.bottomUp ? "scrollToBottom" : "scrollToTop"]();
+ },
+ //* Scrolls to the end of the list.
+ scrollToEnd: function() {
+ this[this.bottomUp ? "scrollToTop" : "scrollToBottom"]();
+ },
+ //* Re-renders the list at the current position.
+ refresh: function() {
+ this.invalidatePages();
+ this.update(this.getScrollTop());
+ this.stabilize();
+
+ //FIXME: Necessary evil for Android 4.0.4 refresh bug
+ if (enyo.platform.android === 4) {
+ this.twiddle();
+ }
+ },
+ //* Re-renders the list from the beginning.
+ reset: function() {
+ this.getSelection().clear();
+ this.invalidateMetrics();
+ this.invalidatePages();
+ this.stabilize();
+ this.scrollToStart();
+ },
+ /**
+ Returns the _selection_ component that manages the selection state for
+ this list.
+ */
+ getSelection: function() {
+ return this.$.generator.getSelection();
+ },
+ //* Sets the selection state for the given row index.
+ select: function(inIndex, inData) {
+ return this.getSelection().select(inIndex, inData);
+ },
+ //* Gets the selection state for the given row index.
+ isSelected: function(inIndex) {
+ return this.$.generator.isSelected(inIndex);
+ },
+ /**
+ Re-renders the specified row. Call after making modifications to a row,
+ to force it to render.
+ */
+ renderRow: function(inIndex) {
+ this.$.generator.renderRow(inIndex);
+ },
+ //* Prepares the row to become interactive.
+ prepareRow: function(inIndex) {
+ this.$.generator.prepareRow(inIndex);
+ },
+ //* Restores the row to being non-interactive.
+ lockRow: function() {
+ this.$.generator.lockRow();
+ },
+ /**
+ Performs a set of tasks by running the function _inFunc_ on a row (which
+ must be interactive at the time the tasks are performed). Locks the row
+ when done.
+ */
+ performOnRow: function(inIndex, inFunc, inContext) {
+ this.$.generator.performOnRow(inIndex, inFunc, inContext);
+ },
+ //* @protected
+ animateFinish: function(inSender) {
+ this.twiddle();
+ return true;
+ },
+ // FIXME: Android 4.04 has issues with nested composited elements; for example, a SwipeableItem,
+ // can incorrectly generate taps on its content when it has slid off the screen;
+ // we address this BUG here by forcing the Scroller to "twiddle" which corrects the bug by
+ // provoking a dom update.
+ twiddle: function() {
+ var s = this.getStrategy();
+ enyo.call(s, "twiddle");
+ }
+});
diff --git a/html/lib/layout/list/source/PulldownList.css b/html/lib/layout/list/source/PulldownList.css
new file mode 100644
index 0000000..89f6da0
--- /dev/null
+++ b/html/lib/layout/list/source/PulldownList.css
@@ -0,0 +1,60 @@
+.enyo-list-pulldown {
+ position: absolute;
+ bottom: 100%;
+ left: 0;
+ right: 0;
+}
+
+.enyo-puller {
+ position: relative;
+ height: 50px;
+ font-size: 22px;
+ color: #444;
+ padding: 20px 0 0px 34px;
+}
+
+.enyo-puller-text {
+ position: absolute;
+ left: 80px;
+ top: 22px;
+}
+
+.enyo-puller-arrow {
+ position: relative;
+ background: #444;
+ width: 7px;
+ height: 28px;
+ transition: transform 0.3s;
+ -webkit-transition: -webkit-transform 0.3s;
+ -moz-transition: -moz-transform 0.3s;
+ -o-transition: -o-transform 0.3s;
+ -ms-transition: -ms-transform 0.3s;
+}
+
+.enyo-puller-arrow:after {
+ content: " ";
+ height: 0;
+ width: 0;
+ position: absolute;
+ border: 10px solid transparent;
+ border-bottom-color: #444;
+ bottom: 100%;
+ left: 50%;
+ margin-left: -10px;
+}
+
+.enyo-puller-arrow-up {
+ transform: rotate(0deg);
+ -webkit-transform: rotate(0deg);
+ -moz-transform: rotate(0deg);
+ -o-transform: rotate(0deg);
+ -ms-transform: rotate(0deg);
+}
+
+.enyo-puller-arrow-down {
+ transform: rotate(180deg);
+ -webkit-transform: rotate(180deg);
+ -moz-transform: rotate(180deg);
+ -o-transform: rotate(180deg);
+ -ms-transform: rotate(180deg);
+} \ No newline at end of file
diff --git a/html/lib/layout/list/source/PulldownList.js b/html/lib/layout/list/source/PulldownList.js
new file mode 100644
index 0000000..8d0dab1
--- /dev/null
+++ b/html/lib/layout/list/source/PulldownList.js
@@ -0,0 +1,180 @@
+/**
+A list that provides a pull-to-refresh feature, which allows new data to be
+retrieved and updated in the list.
+
+PulldownList provides the onPullRelease event to allow an application to start
+retrieving new data. The onPullComplete event indicates that the pull is
+complete and it's time to update the list with the new data.
+
+ {name: "list", kind: "PulldownList", onSetupItem: "setupItem",
+ onPullRelease: "pullRelease", onPullComplete: "pullComplete",
+ components: [
+ {name: "item"}
+ ]}
+
+ pullRelease: function() {
+ this.search();
+ },
+ processSearchResults: function(inRequest, inResponse) {
+ this.results = inResponse.results;
+ this.$.list.setCount(this.results.length);
+ this.$.list.completePull();
+ },
+ pullComplete: function() {
+ this.$.list.reset();
+ }
+*/
+enyo.kind({
+ name: "enyo.PulldownList",
+ kind: "List",
+ touch: true,
+ pully: null,
+ pulldownTools: [
+ {name: "pulldown", classes: "enyo-list-pulldown", components: [
+ {name: "puller", kind: "Puller"}
+ ]}
+ ],
+ events: {
+ onPullStart: "",
+ onPullCancel: "",
+ onPull: "",
+ onPullRelease: "",
+ onPullComplete: ""
+ },
+ handlers: {
+ onScrollStart: "scrollStartHandler",
+ onScroll: "scrollHandler",
+ onScrollStop: "scrollStopHandler",
+ ondragfinish: "dragfinish"
+ },
+ //
+ pullingMessage: "Pull down to refresh...",
+ pulledMessage: "Release to refresh...",
+ loadingMessage: "Loading...",
+ //
+ pullingIconClass: "enyo-puller-arrow enyo-puller-arrow-down",
+ pulledIconClass: "enyo-puller-arrow enyo-puller-arrow-up",
+ loadingIconClass: "",
+ //* @protected
+ create: function() {
+ var p = {kind: "Puller", showing: false, text: this.loadingMessage, iconClass: this.loadingIconClass, onCreate: "setPully"};
+ this.listTools.splice(0, 0, p);
+ this.inherited(arguments);
+ this.setPulling();
+ },
+ initComponents: function() {
+ this.createChrome(this.pulldownTools);
+ this.accel = enyo.dom.canAccelerate();
+ this.translation = this.accel ? "translate3d" : "translate";
+ this.inherited(arguments);
+ },
+ setPully: function(inSender, inEvent) {
+ this.pully = inEvent.originator;
+ },
+ scrollStartHandler: function() {
+ this.firedPullStart = false;
+ this.firedPull = false;
+ this.firedPullCancel = false;
+ },
+ scrollHandler: function(inSender) {
+ if (this.completingPull) {
+ this.pully.setShowing(false);
+ }
+ var s = this.getStrategy().$.scrollMath;
+ var over = s.y;
+ if (s.isInOverScroll() && over > 0) {
+ enyo.dom.transformValue(this.$.pulldown, this.translation, "0," + over + "px" + (this.accel ? ",0" : ""));
+ if (!this.firedPullStart) {
+ this.firedPullStart = true;
+ this.pullStart();
+ this.pullHeight = this.$.pulldown.getBounds().height;
+ }
+ if (over > this.pullHeight && !this.firedPull) {
+ this.firedPull = true;
+ this.firedPullCancel = false;
+ this.pull();
+ }
+ if (this.firedPull && !this.firedPullCancel && over < this.pullHeight) {
+ this.firedPullCancel = true;
+ this.firedPull = false;
+ this.pullCancel();
+ }
+ }
+ },
+ scrollStopHandler: function() {
+ if (this.completingPull) {
+ this.completingPull = false;
+ this.doPullComplete();
+ }
+ },
+ dragfinish: function() {
+ if (this.firedPull) {
+ var s = this.getStrategy().$.scrollMath;
+ s.setScrollY(s.y - this.pullHeight);
+ this.pullRelease();
+ }
+ },
+ //* @public
+ //* To signal that the list should execute pull completion. This usually be called after the application has received the new data.
+ completePull: function() {
+ this.completingPull = true;
+ this.$.strategy.$.scrollMath.setScrollY(this.pullHeight);
+ this.$.strategy.$.scrollMath.start();
+ },
+ //* @protected
+ pullStart: function() {
+ this.setPulling();
+ this.pully.setShowing(false);
+ this.$.puller.setShowing(true);
+ this.doPullStart();
+ },
+ pull: function() {
+ this.setPulled();
+ this.doPull();
+ },
+ pullCancel: function() {
+ this.setPulling();
+ this.doPullCancel();
+ },
+ pullRelease: function() {
+ this.$.puller.setShowing(false);
+ this.pully.setShowing(true);
+ this.doPullRelease();
+ },
+ setPulling: function() {
+ this.$.puller.setText(this.pullingMessage);
+ this.$.puller.setIconClass(this.pullingIconClass);
+ },
+ setPulled: function() {
+ this.$.puller.setText(this.pulledMessage);
+ this.$.puller.setIconClass(this.pulledIconClass);
+ }
+});
+
+enyo.kind({
+ name: "enyo.Puller",
+ classes: "enyo-puller",
+ published: {
+ text: "",
+ iconClass: ""
+ },
+ events: {
+ onCreate: ""
+ },
+ components: [
+ {name: "icon"},
+ {name: "text", tag: "span", classes: "enyo-puller-text"}
+ ],
+ create: function() {
+ this.inherited(arguments);
+ this.doCreate();
+ this.textChanged();
+ this.iconClassChanged();
+ },
+ textChanged: function() {
+ this.$.text.setContent(this.text);
+ },
+ iconClassChanged: function() {
+ this.$.icon.setClasses(this.iconClass);
+ }
+}); \ No newline at end of file
diff --git a/html/lib/layout/list/source/Selection.js b/html/lib/layout/list/source/Selection.js
new file mode 100644
index 0000000..8581f8d
--- /dev/null
+++ b/html/lib/layout/list/source/Selection.js
@@ -0,0 +1,141 @@
+/**
+ _enyo.Selection_ is used to manage row selection state for lists. It
+ provides selection state management for both single-select and multi-select
+ lists.
+
+ // The following is an excerpt from enyo.FlyweightRepeater.
+ enyo.kind({
+ name: "enyo.FlyweightRepeater",
+ ...
+ components: [
+ {kind: "Selection", onSelect: "selectDeselect", onDeselect: "selectDeselect"},
+ ...
+ ],
+ tap: function(inSender, inEvent) {
+ ...
+ // mark the tapped row as selected
+ this.$.selection.select(inEvent.index);
+ ...
+ },
+ selectDeselect: function(inSender, inEvent) {
+ // this is where a row selection highlight might be applied
+ this.renderRow(inEvent.key);
+ }
+ ...
+ })
+*/
+enyo.kind({
+ name: "enyo.Selection",
+ kind: enyo.Component,
+ published: {
+ //* If true, multiple selections are allowed.
+ multi: false
+ },
+ events: {
+ /**
+ Fires when an item is selected.
+
+ {kind: "Selection", onSelect: "selectRow"...
+ ...
+ selectRow: function(inSender, inKey, inPrivateData) {
+ ...
+
+ _inKey_ is whatever key was used to register
+ the selection (usually a row index).
+
+ _inPrivateData_ references data registered
+ with this key by the code that made the original selection.
+ */
+ onSelect: "",
+ /**
+ Fires when an item is deselected.
+
+ {kind: "Selection", onSelect: "deselectRow"...
+ ...
+ deselectRow: function(inSender, inKey, inPrivateData)
+ ...
+
+ _inKey_ is whatever key was used to request
+ the deselection (usually a row index).
+
+ _inPrivateData_ references data registered
+ with this key by the code that made the selection.
+ */
+ onDeselect: "",
+ //* Sent when selection changes (but not when the selection is cleared).
+ onChange: ""
+ },
+ //* @protected
+ create: function() {
+ this.clear();
+ this.inherited(arguments);
+ },
+ multiChanged: function() {
+ if (!this.multi) {
+ this.clear();
+ }
+ this.doChange();
+ },
+ highlander: function(inKey) {
+ if (!this.multi) {
+ this.deselect(this.lastSelected);
+ }
+ },
+ //* @public
+ //* Removes all selections.
+ clear: function() {
+ this.selected = {};
+ },
+ //* Returns true if the _inKey_ row is selected.
+ isSelected: function(inKey) {
+ return this.selected[inKey];
+ },
+ //* Manually sets a row's state to selected or unselected.
+ setByKey: function(inKey, inSelected, inData) {
+ if (inSelected) {
+ this.selected[inKey] = (inData || true);
+ this.lastSelected = inKey;
+ this.doSelect({key: inKey, data: this.selected[inKey]});
+ } else {
+ var was = this.isSelected(inKey);
+ delete this.selected[inKey];
+ this.doDeselect({key: inKey, data: was});
+ }
+ this.doChange();
+ },
+ //* Deselects a row.
+ deselect: function(inKey) {
+ if (this.isSelected(inKey)) {
+ this.setByKey(inKey, false);
+ }
+ },
+ /**
+ Selects a row. If the _multi_ property is set to false, _select_ will
+ also deselect the previous selection.
+ */
+ select: function(inKey, inData) {
+ if (this.multi) {
+ this.setByKey(inKey, !this.isSelected(inKey), inData);
+ } else if (!this.isSelected(inKey)) {
+ this.highlander();
+ this.setByKey(inKey, true, inData);
+ }
+ },
+ /**
+ Toggles selection state for a row. If the _multi_ property is set to
+ false, toggling a selection on will deselect the previous selection.
+ */
+ toggle: function(inKey, inData) {
+ if (!this.multi && this.lastSelected != inKey) {
+ this.deselect(this.lastSelected);
+ }
+ this.setByKey(inKey, !this.isSelected(inKey), inData);
+ },
+ /**
+ Returns the selection as a hash in which each selected item has a value;
+ unselected items are undefined.
+ */
+ getSelected: function() {
+ return this.selected;
+ }
+});
diff --git a/html/lib/layout/list/source/package.js b/html/lib/layout/list/source/package.js
new file mode 100644
index 0000000..391e38e
--- /dev/null
+++ b/html/lib/layout/list/source/package.js
@@ -0,0 +1,7 @@
+enyo.depends(
+ "FlyweightRepeater.js",
+ "List.css",
+ "List.js",
+ "PulldownList.css",
+ "PulldownList.js"
+); \ No newline at end of file
diff --git a/html/lib/layout/list/source/wip-package.js b/html/lib/layout/list/source/wip-package.js
new file mode 100644
index 0000000..bafc093
--- /dev/null
+++ b/html/lib/layout/list/source/wip-package.js
@@ -0,0 +1,6 @@
+enyo.depends(
+ // Add wip controls here
+ "AlphaJumper.css",
+ "AlphaJumper.js",
+ "AlphaJumpList.js"
+); \ No newline at end of file
diff --git a/html/lib/layout/package.js b/html/lib/layout/package.js
new file mode 100644
index 0000000..7f9777f
--- /dev/null
+++ b/html/lib/layout/package.js
@@ -0,0 +1,7 @@
+enyo.depends(
+ "fittable",
+ "list",
+ "slideable",
+ "panels",
+ "tree"
+); \ No newline at end of file
diff --git a/html/lib/layout/panels/package.js b/html/lib/layout/panels/package.js
new file mode 100644
index 0000000..f1de4f2
--- /dev/null
+++ b/html/lib/layout/panels/package.js
@@ -0,0 +1,3 @@
+enyo.depends(
+ "source/"
+); \ No newline at end of file
diff --git a/html/lib/layout/panels/source/Panels.css b/html/lib/layout/panels/source/Panels.css
new file mode 100644
index 0000000..2e79349
--- /dev/null
+++ b/html/lib/layout/panels/source/Panels.css
@@ -0,0 +1,12 @@
+.enyo-panels {
+}
+
+.enyo-panels-fit-narrow {
+}
+
+@media all and (max-width: 800px) {
+ .enyo-panels-fit-narrow > * {
+ min-width: 100%;
+ max-width: 100%;
+ }
+} \ No newline at end of file
diff --git a/html/lib/layout/panels/source/Panels.js b/html/lib/layout/panels/source/Panels.js
new file mode 100644
index 0000000..d2e2ecc
--- /dev/null
+++ b/html/lib/layout/panels/source/Panels.js
@@ -0,0 +1,386 @@
+/**
+The enyo.Panels kind is designed to satisfy a variety of common use cases for
+application layout. Using enyo.Panels, controls may be arranged as (among other
+things) a carousel, a set of collapsing panels, a card stack that fades between
+panels, or a grid.
+
+Any Enyo control may be placed inside an enyo.Panels, but by convention we refer
+to each of these controls as a "panel." From the set of panels in an enyo.Panels,
+one is considered active. The active panel is set by index using the *setIndex*
+method. The actual layout of the panels typically changes each time the active
+panel is set, such that the new active panel has the most prominent position.
+
+For more information, see the [Panels documentation](https://github.com/enyojs/enyo/wiki/Panels)
+in the Enyo Developer Guide.
+*/
+enyo.kind({
+ name: "enyo.Panels",
+ classes: "enyo-panels",
+ published: {
+ /**
+ The index of the active panel. The layout of panels is controlled by
+ the layoutKind, but as a rule, the active panel is displayed in the
+ most prominent position. For example, in the (default) CardArranger
+ layout, the active panel is shown and the other panels are hidden.
+ */
+ index: 0,
+ //* Controls whether the user can drag between panels.
+ draggable: true,
+ //* Controls whether the panels animate when transitioning; for example,
+ //* when _setIndex_ is called.
+ animate: true,
+ //* Controls whether panels "wrap around" when moving past the end. Actual effect depends upon the arranger in use.
+ wrap: false,
+ //* Sets the arranger kind to be used for dynamic layout.
+ arrangerKind: "CardArranger",
+ //* By default, each panel will be sized to fit the Panels' width when
+ //* the screen size is narrow enough (less than ~800px). Set to false
+ //* to avoid this behavior.
+ narrowFit: true
+ },
+ events: {
+ /**
+ Fires at the start of a panel transition.
+ This event fires when _setIndex_ is called and also during dragging.
+ */
+ onTransitionStart: "",
+ /**
+ Fires at the end of a panel transition.
+ This event fires when _setIndex_ is called and also during dragging.
+ */
+ onTransitionFinish: ""
+ },
+ //* @protected
+ handlers: {
+ ondragstart: "dragstart",
+ ondrag: "drag",
+ ondragfinish: "dragfinish"
+ },
+ tools: [
+ {kind: "Animator", onStep: "step", onEnd: "completed"}
+ ],
+ fraction: 0,
+ create: function() {
+ this.transitionPoints = [];
+ this.inherited(arguments);
+ this.arrangerKindChanged();
+ this.avoidFitChanged();
+ this.indexChanged();
+ },
+ initComponents: function() {
+ this.createChrome(this.tools);
+ this.inherited(arguments);
+ },
+ arrangerKindChanged: function() {
+ this.setLayoutKind(this.arrangerKind);
+ },
+ avoidFitChanged: function() {
+ this.addRemoveClass("enyo-panels-fit-narrow", this.narrowFit);
+ },
+ removeControl: function(inControl) {
+ this.inherited(arguments);
+ if (this.controls.length > 1 && this.isPanel(inControl)) {
+ this.setIndex(Math.max(this.index - 1, 0));
+ this.flow();
+ this.reflow();
+ }
+ },
+ isPanel: function() {
+ // designed to be overridden in kinds derived from Panels that have
+ // non-panel client controls
+ return true;
+ },
+ flow: function() {
+ this.arrangements = [];
+ this.inherited(arguments);
+ },
+ reflow: function() {
+ this.arrangements = [];
+ this.inherited(arguments);
+ this.refresh();
+ },
+ //* @public
+ /**
+ Returns an array of contained panels.
+ Subclasses can override this if they don't want the arranger to layout all of their children
+ */
+ getPanels: function() {
+ var p = this.controlParent || this;
+ return p.children;
+ },
+ //* Returns a reference to the active panel--i.e., the panel at the specified index.
+ getActive: function() {
+ var p$ = this.getPanels();
+ return p$[this.index];
+ },
+ /**
+ Returns a reference to the <a href="#enyo.Animator">enyo.Animator</a>
+ instance used to animate panel transitions. The Panels' animator can be used
+ to set the duration of panel transitions, e.g.:
+
+ this.getAnimator().setDuration(1000);
+ */
+ getAnimator: function() {
+ return this.$.animator;
+ },
+ /**
+ Sets the active panel to the panel specified by the given index.
+ Note that if the _animate_ property is set to true, the active panel
+ will animate into view.
+ */
+ setIndex: function(inIndex) {
+ // override setIndex so that indexChanged is called
+ // whether this.index has actually changed or not
+ this.setPropertyValue("index", inIndex, "indexChanged");
+ },
+ /**
+ Sets the active panel to the panel specified by the given index.
+ Regardless of the value of the _animate_ property, the transition to the
+ next panel will not animate and will be immediate.
+ */
+ setIndexDirect: function(inIndex) {
+ this.setIndex(inIndex);
+ this.completed();
+ },
+ //* Transitions to the previous panel--i.e., the panel whose index value is
+ //* one less than that of the current active panel.
+ previous: function() {
+ this.setIndex(this.index-1);
+ },
+ //* Transitions to the next panel--i.e., the panel whose index value is one
+ //* greater than that of the current active panel.
+ next: function() {
+ this.setIndex(this.index+1);
+ },
+ //* @protected
+ clamp: function(inValue) {
+ var l = this.getPanels().length-1;
+ if (this.wrap) {
+ // FIXME: dragging makes assumptions about direction and from->start indexes.
+ //return inValue < 0 ? l : (inValue > l ? 0 : inValue);
+ return inValue;
+ } else {
+ return Math.max(0, Math.min(inValue, l));
+ }
+ },
+ indexChanged: function(inOld) {
+ this.lastIndex = inOld;
+ this.index = this.clamp(this.index);
+ if (!this.dragging) {
+ if (this.$.animator.isAnimating()) {
+ this.completed();
+ }
+ this.$.animator.stop();
+ if (this.hasNode()) {
+ if (this.animate) {
+ this.startTransition();
+ this.$.animator.play({
+ startValue: this.fraction
+ });
+ } else {
+ this.refresh();
+ }
+ }
+ }
+ },
+ step: function(inSender) {
+ this.fraction = inSender.value;
+ this.stepTransition();
+ },
+ completed: function() {
+ if (this.$.animator.isAnimating()) {
+ this.$.animator.stop();
+ }
+ this.fraction = 1;
+ this.stepTransition();
+ this.finishTransition();
+ },
+ dragstart: function(inSender, inEvent) {
+ if (this.draggable && this.layout && this.layout.canDragEvent(inEvent)) {
+ inEvent.preventDefault();
+ this.dragstartTransition(inEvent);
+ this.dragging = true;
+ this.$.animator.stop();
+ return true;
+ }
+ },
+ drag: function(inSender, inEvent) {
+ if (this.dragging) {
+ inEvent.preventDefault();
+ this.dragTransition(inEvent);
+ }
+ },
+ dragfinish: function(inSender, inEvent) {
+ if (this.dragging) {
+ this.dragging = false;
+ inEvent.preventTap();
+ this.dragfinishTransition(inEvent);
+ }
+ },
+ dragstartTransition: function(inEvent) {
+ if (!this.$.animator.isAnimating()) {
+ var f = this.fromIndex = this.index;
+ this.toIndex = f - (this.layout ? this.layout.calcDragDirection(inEvent) : 0);
+ } else {
+ this.verifyDragTransition(inEvent);
+ }
+ this.fromIndex = this.clamp(this.fromIndex);
+ this.toIndex = this.clamp(this.toIndex);
+ //this.log(this.fromIndex, this.toIndex);
+ this.fireTransitionStart();
+ if (this.layout) {
+ this.layout.start();
+ }
+ },
+ dragTransition: function(inEvent) {
+ // note: for simplicity we choose to calculate the distance directly between
+ // the first and last transition point.
+ var d = this.layout ? this.layout.calcDrag(inEvent) : 0;
+ var t$ = this.transitionPoints, s = t$[0], f = t$[t$.length-1];
+ var as = this.fetchArrangement(s);
+ var af = this.fetchArrangement(f);
+ var dx = this.layout ? this.layout.drag(d, s, as, f, af) : 0;
+ var dragFail = d && !dx;
+ if (dragFail) {
+ //this.log(dx, s, as, f, af);
+ }
+ this.fraction += dx;
+ var fr = this.fraction;
+ if (fr > 1 || fr < 0 || dragFail) {
+ if (fr > 0 || dragFail) {
+ this.dragfinishTransition(inEvent);
+ }
+ this.dragstartTransition(inEvent);
+ this.fraction = 0;
+ // FIXME: account for lost fraction
+ //this.dragTransition(inEvent);
+ }
+ this.stepTransition();
+ },
+ dragfinishTransition: function(inEvent) {
+ this.verifyDragTransition(inEvent);
+ this.setIndex(this.toIndex);
+ // note: if we're still dragging, then we're at a transition boundary
+ // and should fire the finish event
+ if (this.dragging) {
+ this.fireTransitionFinish();
+ }
+ },
+ verifyDragTransition: function(inEvent) {
+ var d = this.layout ? this.layout.calcDragDirection(inEvent) : 0;
+ var f = Math.min(this.fromIndex, this.toIndex);
+ var t = Math.max(this.fromIndex, this.toIndex);
+ if (d > 0) {
+ var s = f;
+ f = t;
+ t = s;
+ }
+ if (f != this.fromIndex) {
+ this.fraction = 1 - this.fraction;
+ }
+ //this.log("old", this.fromIndex, this.toIndex, "new", f, t);
+ this.fromIndex = f;
+ this.toIndex = t;
+ },
+ refresh: function() {
+ if (this.$.animator.isAnimating()) {
+ this.$.animator.stop();
+ }
+ this.startTransition();
+ this.fraction = 1;
+ this.stepTransition();
+ this.finishTransition();
+ },
+ startTransition: function() {
+ this.fromIndex = this.fromIndex != null ? this.fromIndex : this.lastIndex || 0;
+ this.toIndex = this.toIndex != null ? this.toIndex : this.index;
+ //this.log(this.id, this.fromIndex, this.toIndex);
+ if (this.layout) {
+ this.layout.start();
+ }
+ this.fireTransitionStart();
+ },
+ finishTransition: function() {
+ if (this.layout) {
+ this.layout.finish();
+ }
+ this.transitionPoints = [];
+ this.fraction = 0;
+ this.fromIndex = this.toIndex = null;
+ this.fireTransitionFinish();
+ },
+ fireTransitionStart: function() {
+ var t = this.startTransitionInfo;
+ if (this.hasNode() && (!t || (t.fromIndex != this.fromIndex || t.toIndex != this.toIndex))) {
+ this.startTransitionInfo = {fromIndex: this.fromIndex, toIndex: this.toIndex};
+ this.doTransitionStart(enyo.clone(this.startTransitionInfo));
+ }
+ },
+ fireTransitionFinish: function() {
+ var t = this.finishTransitionInfo;
+ if (this.hasNode() && (!t || (t.fromIndex != this.lastIndex || t.toIndex != this.index))) {
+ this.finishTransitionInfo = {fromIndex: this.lastIndex, toIndex: this.index};
+ this.doTransitionFinish(enyo.clone(this.finishTransitionInfo));
+ }
+ this.lastIndex=this.index;
+ },
+ // gambit: we interpolate between arrangements as needed.
+ stepTransition: function() {
+ if (this.hasNode()) {
+ // select correct transition points and normalize fraction.
+ var t$ = this.transitionPoints;
+ var r = (this.fraction || 0) * (t$.length-1);
+ var i = Math.floor(r);
+ r = r - i;
+ var s = t$[i], f = t$[i+1];
+ // get arrangements and lerp between them
+ var s0 = this.fetchArrangement(s);
+ var s1 = this.fetchArrangement(f);
+ this.arrangement = s0 && s1 ? enyo.Panels.lerp(s0, s1, r) : (s0 || s1);
+ if (this.arrangement && this.layout) {
+ this.layout.flowArrangement();
+ }
+ }
+ },
+ fetchArrangement: function(inName) {
+ if ((inName != null) && !this.arrangements[inName] && this.layout) {
+ this.layout._arrange(inName);
+ this.arrangements[inName] = this.readArrangement(this.getPanels());
+ }
+ return this.arrangements[inName];
+ },
+ readArrangement: function(inC) {
+ var r = [];
+ for (var i=0, c$=inC, c; (c=c$[i]); i++) {
+ r.push(enyo.clone(c._arranger));
+ }
+ return r;
+ },
+ statics: {
+ isScreenNarrow: function() {
+ return enyo.dom.getWindowWidth() <= 800;
+ },
+ lerp: function(inA0, inA1, inFrac) {
+ var r = [];
+ for (var i=0, k$=enyo.keys(inA0), k; (k=k$[i]); i++) {
+ r.push(this.lerpObject(inA0[k], inA1[k], inFrac));
+ }
+ return r;
+ },
+ lerpObject: function(inNew, inOld, inFrac) {
+ var b = enyo.clone(inNew), n, o;
+ // inOld might be undefined when deleting panels
+ if (inOld) {
+ for (var i in inNew) {
+ n = inNew[i];
+ o = inOld[i];
+ if (n != o) {
+ b[i] = n - (n - o) * inFrac;
+ }
+ }
+ }
+ return b;
+ }
+ }
+});
+
diff --git a/html/lib/layout/panels/source/arrangers/Arranger.css b/html/lib/layout/panels/source/arrangers/Arranger.css
new file mode 100644
index 0000000..4d23be0
--- /dev/null
+++ b/html/lib/layout/panels/source/arrangers/Arranger.css
@@ -0,0 +1,29 @@
+.enyo-arranger {
+ position: relative;
+ overflow: hidden;
+}
+
+.enyo-arranger.enyo-fit {
+ position: absolute;
+}
+
+.enyo-arranger > * {
+ position: absolute;
+ left: 0;
+ top: 0;
+ box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+}
+
+.enyo-arranger-fit > * {
+ /*
+ override any width/height set on panels
+ */
+ width: 100% !important;
+ height: 100% !important;
+ min-width: 0 !important;
+ max-width: auto !important;
+ min-height: 0 !important;
+ max-height: auto !important;
+}
diff --git a/html/lib/layout/panels/source/arrangers/Arranger.js b/html/lib/layout/panels/source/arrangers/Arranger.js
new file mode 100644
index 0000000..b10f6b8
--- /dev/null
+++ b/html/lib/layout/panels/source/arrangers/Arranger.js
@@ -0,0 +1,226 @@
+/**
+ _enyo.Arranger_ is an <a href="#enyo.Layout">enyo.Layout</a> that considers
+ one of the controls it lays out as active. The other controls are placed
+ relative to the active control as makes sense for the layout.
+
+ Arranger supports dynamic layouts, meaning it's possible to transition
+ between its layouts via animation. Typically, arrangers should lay out
+ controls using CSS transforms, since these are optimized for animation. To
+ support this, the controls in an Arranger are absolutely positioned, and
+ the Arranger kind has an `accelerated` property, which marks controls for
+ CSS compositing. The default setting of "auto" ensures that this will occur
+ if enabled by the platform.
+
+ For more information, see the documentation on
+ [Arrangers](https://github.com/enyojs/enyo/wiki/Arrangers)
+ in the Enyo Developer Guide.
+*/
+enyo.kind({
+ name: "enyo.Arranger",
+ kind: "Layout",
+ layoutClass: "enyo-arranger",
+ /**
+ Sets controls being laid out to use CSS compositing. A setting of "auto"
+ will mark controls for compositing if the platform supports it.
+ */
+ accelerated: "auto",
+ //* Property of the drag event used to calculate the amount a drag moves the layout
+ dragProp: "ddx",
+ //* Property of the drag event used to calculate the direction of a drag
+ dragDirectionProp: "xDirection",
+ //* Property of the drag event used to calculate whether a drag should occur
+ canDragProp: "horizontal",
+ /**
+ If set to true, transitions between non-adjacent arrangements will go
+ through the intermediate arrangements. This is useful when direct
+ transitions between arrangements would be visually jarring.
+ */
+ incrementalPoints: false,
+ /**
+ Called when removing an arranger (for example, when switching a Panels
+ control to a different arrangerKind). Subclasses should implement this
+ function to reset whatever properties they've changed on child controls.
+ You *must* call the superclass implementation in your subclass's
+ _destroy_ function.
+ */
+ destroy: function() {
+ var c$ = this.container.getPanels();
+ for (var i=0, c; c=c$[i]; i++) {
+ c._arranger = null;
+ }
+ this.inherited(arguments);
+ },
+ //* @public
+ /**
+ Arranges the given array of controls (_inC_) in the layout specified by
+ _inName_. When implementing this method, rather than apply styling
+ directly to controls, call _arrangeControl(inControl, inArrangement)_
+ with an _inArrangement_ object with styling settings. These will then be
+ applied via the _flowControl(inControl, inArrangement)_ method.
+ */
+ arrange: function(inC, inName) {
+ },
+ /**
+ Sizes the controls in the layout. This method is called only at reflow
+ time. Note that sizing is separated from other layout done in the
+ _arrange_ method because it is expensive and not suitable for dynamic
+ layout.
+ */
+ size: function() {
+ },
+ /**
+ Called when a layout transition begins. Implement this method to perform
+ tasks that should only occur when a transition starts; for example, some
+ controls could be shown or hidden. In addition, the _transitionPoints_
+ array may be set on the container to dictate the named arrangments
+ between which the transition occurs.
+ */
+ start: function() {
+ var f = this.container.fromIndex, t = this.container.toIndex;
+ var p$ = this.container.transitionPoints = [f];
+ // optionally add a transition point for each index between from and to.
+ if (this.incrementalPoints) {
+ var d = Math.abs(t - f) - 2;
+ var i = f;
+ while (d >= 0) {
+ i = i + (t < f ? -1 : 1);
+ p$.push(i);
+ d--;
+ }
+ }
+ p$.push(this.container.toIndex);
+ },
+ /**
+ Called when a layout transition completes. Implement this method to
+ perform tasks that should only occur when a transition ends; for
+ example, some controls could be shown or hidden.
+ */
+ finish: function() {
+ },
+ //* @protected
+ canDragEvent: function(inEvent) {
+ return inEvent[this.canDragProp];
+ },
+ calcDragDirection: function(inEvent) {
+ return inEvent[this.dragDirectionProp];
+ },
+ calcDrag: function(inEvent) {
+ return inEvent[this.dragProp];
+ },
+ drag: function(inDp, inAn, inA, inBn, inB) {
+ var f = this.measureArrangementDelta(-inDp, inAn, inA, inBn, inB);
+ return f;
+ },
+ measureArrangementDelta: function(inX, inI0, inA0, inI1, inA1) {
+ var d = this.calcArrangementDifference(inI0, inA0, inI1, inA1);
+ var s = d ? inX / Math.abs(d) : 0;
+ s = s * (this.container.fromIndex > this.container.toIndex ? -1 : 1);
+ //enyo.log("delta", s);
+ return s;
+ },
+ //* @public
+ /**
+ Called when dragging the layout, this method returns the difference in
+ pixels between the arrangement _inA0_ for layout setting _inI0_ and
+ arrangement _inA1_ for layout setting _inI1_. This data is used to calculate
+ the percentage that a drag should move the layout between two active states.
+ */
+ calcArrangementDifference: function(inI0, inA0, inI1, inA1) {
+ },
+ //* @protected
+ _arrange: function(inIndex) {
+ var c$ = this.getOrderedControls(inIndex);
+ this.arrange(c$, inIndex);
+ },
+ arrangeControl: function(inControl, inArrangement) {
+ inControl._arranger = enyo.mixin(inControl._arranger || {}, inArrangement);
+ },
+ flow: function() {
+ this.c$ = [].concat(this.container.getPanels());
+ this.controlsIndex = 0;
+ for (var i=0, c$=this.container.getPanels(), c; c=c$[i]; i++) {
+ enyo.dom.accelerate(c, this.accelerated);
+ if (enyo.platform.safari) {
+ // On Safari-desktop, sometimes having the panel's direct child set to accelerate isn't sufficient
+ // this is most often the case with Lists contained inside another control, inside a Panels
+ var grands=c.children;
+ for (var j=0, kid; kid=grands[j]; j++) {
+ enyo.dom.accelerate(kid, this.accelerated);
+ }
+ }
+ }
+ },
+ reflow: function() {
+ var cn = this.container.hasNode();
+ this.containerBounds = cn ? {width: cn.clientWidth, height: cn.clientHeight} : {};
+ this.size();
+ },
+ flowArrangement: function() {
+ var a = this.container.arrangement;
+ if (a) {
+ for (var i=0, c$=this.container.getPanels(), c; c=c$[i]; i++) {
+ this.flowControl(c, a[i]);
+ }
+ }
+ },
+ //* @public
+ /**
+ Lays out the control (_inControl_) according to the settings stored in
+ the _inArrangment_ object. By default, _flowControl_ will apply settings
+ of left, top, and opacity. This method should only be implemented to
+ apply other settings made via _arrangeControl_.
+ */
+ flowControl: function(inControl, inArrangement) {
+ enyo.Arranger.positionControl(inControl, inArrangement);
+ var o = inArrangement.opacity;
+ if (o != null) {
+ enyo.Arranger.opacifyControl(inControl, o);
+ }
+ },
+ //* @protected
+ // Gets an array of controls arranged in state order.
+ // note: optimization, dial around a single array.
+ getOrderedControls: function(inIndex) {
+ var whole = Math.floor(inIndex);
+ var a = whole - this.controlsIndex;
+ var sign = a > 0;
+ var c$ = this.c$ || [];
+ for (var i=0; i<Math.abs(a); i++) {
+ if (sign) {
+ c$.push(c$.shift());
+ } else {
+ c$.unshift(c$.pop());
+ }
+ }
+ this.controlsIndex = whole;
+ return c$;
+ },
+ statics: {
+ // Positions a control via transform: translateX/Y if supported and falls back to left/top if not.
+ positionControl: function(inControl, inBounds, inUnit) {
+ var unit = inUnit || "px";
+ if (!this.updating) {
+ if (enyo.dom.canTransform() && !enyo.platform.android) {
+ var l = inBounds.left, t = inBounds.top;
+ var l = enyo.isString(l) ? l : l && (l + unit);
+ var t = enyo.isString(t) ? t : t && (t + unit);
+ enyo.dom.transform(inControl, {translateX: l || null, translateY: t || null});
+ } else {
+ inControl.setBounds(inBounds, inUnit);
+ }
+ }
+ },
+ opacifyControl: function(inControl, inOpacity) {
+ var o = inOpacity;
+ // FIXME: very high/low settings of opacity can cause a control to
+ // blink so cap this here.
+ o = o > .99 ? 1 : (o < .01 ? 0 : o);
+ // note: we only care about ie8
+ if (enyo.platform.ie < 9) {
+ inControl.applyStyle("filter", "progid:DXImageTransform.Microsoft.Alpha(Opacity=" + (o * 100) + ")");
+ } else {
+ inControl.applyStyle("opacity", o);
+ }
+ }
+ }
+});
diff --git a/html/lib/layout/panels/source/arrangers/CardArranger.js b/html/lib/layout/panels/source/arrangers/CardArranger.js
new file mode 100644
index 0000000..28b94b2
--- /dev/null
+++ b/html/lib/layout/panels/source/arrangers/CardArranger.js
@@ -0,0 +1,49 @@
+/**
+ _enyo.CardArranger_ is an <a href="#enyo.Arranger">enyo.Arranger</a> that
+ displays only one active control. The non-active controls are hidden with
+ _setShowing(false)_. Transitions between arrangements are handled by fading
+ from one control to the next.
+*/
+enyo.kind({
+ name: "enyo.CardArranger",
+ kind: "Arranger",
+ layoutClass: "enyo-arranger enyo-arranger-fit",
+ calcArrangementDifference: function(inI0, inA0, inI1, inA1) {
+ return this.containerBounds.width;
+ },
+ arrange: function(inC, inName) {
+ for (var i=0, c, b, v; c=inC[i]; i++) {
+ v = (i == 0) ? 1 : 0;
+ this.arrangeControl(c, {opacity: v});
+ }
+ },
+ start: function() {
+ this.inherited(arguments);
+ var c$ = this.container.getPanels();
+ for (var i=0, c; c=c$[i]; i++) {
+ var wasShowing=c.showing;
+ c.setShowing(i == this.container.fromIndex || i == (this.container.toIndex));
+ if (c.showing && !wasShowing) {
+ c.resized();
+ }
+ }
+
+ },
+ finish: function() {
+ this.inherited(arguments);
+ var c$ = this.container.getPanels();
+ for (var i=0, c; c=c$[i]; i++) {
+ c.setShowing(i == this.container.toIndex);
+ }
+ },
+ destroy: function() {
+ var c$ = this.container.getPanels();
+ for (var i=0, c; c=c$[i]; i++) {
+ enyo.Arranger.opacifyControl(c, 1);
+ if (!c.showing) {
+ c.setShowing(true);
+ }
+ }
+ this.inherited(arguments);
+ }
+});
diff --git a/html/lib/layout/panels/source/arrangers/CardSlideInArranger.js b/html/lib/layout/panels/source/arrangers/CardSlideInArranger.js
new file mode 100644
index 0000000..7700557
--- /dev/null
+++ b/html/lib/layout/panels/source/arrangers/CardSlideInArranger.js
@@ -0,0 +1,62 @@
+/**
+ _enyo.CardSlideInArranger_ is an <a href="#enyo.Arranger">enyo.Arranger</a>
+ that displays only one active control. The non-active controls are hidden
+ with _setShowing(false)_. Transitions between arrangements are handled by
+ sliding the new control over the current one.
+
+ Note that CardSlideInArranger always slides controls in from the right. If
+ you want an arranger that slides to the right and left, try
+ <a href="#enyo.LeftRightArranger">enyo.LeftRightArranger</a>.
+*/
+enyo.kind({
+ name: "enyo.CardSlideInArranger",
+ kind: "CardArranger",
+ start: function() {
+ var c$ = this.container.getPanels();
+ for (var i=0, c; c=c$[i]; i++) {
+ var wasShowing=c.showing;
+ c.setShowing(i == this.container.fromIndex || i == (this.container.toIndex));
+ if (c.showing && !wasShowing) {
+ c.resized();
+ }
+ }
+ var l = this.container.fromIndex;
+ var i = this.container.toIndex;
+ this.container.transitionPoints = [
+ i + "." + l + ".s",
+ i + "." + l + ".f"
+ ];
+ },
+ finish: function() {
+ this.inherited(arguments);
+ var c$ = this.container.getPanels();
+ for (var i=0, c; c=c$[i]; i++) {
+ c.setShowing(i == this.container.toIndex);
+ }
+ },
+ arrange: function(inC, inName) {
+ var p = inName.split(".");
+ var f = p[0], s= p[1], starting = (p[2] == "s");
+ var b = this.containerBounds.width;
+ for (var i=0, c$=this.container.getPanels(), c, v; c=c$[i]; i++) {
+ v = b;
+ if (s == i) {
+ v = starting ? 0 : -b;
+ }
+ if (f == i) {
+ v = starting ? b : 0;
+ }
+ if (s == i && s == f) {
+ v = 0;
+ }
+ this.arrangeControl(c, {left: v});
+ }
+ },
+ destroy: function() {
+ var c$ = this.container.getPanels();
+ for (var i=0, c; c=c$[i]; i++) {
+ enyo.Arranger.positionControl(c, {left: null});
+ }
+ this.inherited(arguments);
+ }
+});
diff --git a/html/lib/layout/panels/source/arrangers/CarouselArranger.js b/html/lib/layout/panels/source/arrangers/CarouselArranger.js
new file mode 100644
index 0000000..3a2f26a
--- /dev/null
+++ b/html/lib/layout/panels/source/arrangers/CarouselArranger.js
@@ -0,0 +1,109 @@
+/**
+ _enyo.CarouselArranger_ is an <a href="#enyo.Arranger">enyo.Arranger</a>
+ that displays the active control, along with some number of inactive
+ controls to fill the available space. The active control is positioned on
+ the left side of the container, and the rest of the views are laid out to
+ the right.
+
+ One of the controls may have _fit: true_ set, in which case it will take up
+ any remaining space after all of the other controls have been sized.
+
+ For best results with CarouselArranger, you should set a minimum width for
+ each control via a CSS style, e.g., _min-width: 25%_ or _min-width: 250px_.
+
+ Transitions between arrangements are handled by sliding the new controls in
+ from the right and sliding the old controls off to the left.
+*/
+enyo.kind({
+ name: "enyo.CarouselArranger",
+ kind: "Arranger",
+ size: function() {
+ var c$ = this.container.getPanels();
+ var padding = this.containerPadding = this.container.hasNode() ? enyo.FittableLayout.calcPaddingExtents(this.container.node) : {};
+ var pb = this.containerBounds;
+ pb.height -= padding.top + padding.bottom;
+ pb.width -= padding.left + padding.right;
+ // used space
+ var fit;
+ for (var i=0, s=0, m, c; c=c$[i]; i++) {
+ m = enyo.FittableLayout.calcMarginExtents(c.hasNode());
+ c.width = c.getBounds().width;
+ c.marginWidth = m.right + m.left;
+ s += (c.fit ? 0 : c.width) + c.marginWidth;
+ if (c.fit) {
+ fit = c;
+ }
+ }
+ if (fit) {
+ var w = pb.width - s;
+ fit.width = w >= 0 ? w : fit.width;
+ }
+ for (var i=0, e=padding.left, m, c; c=c$[i]; i++) {
+ c.setBounds({top: padding.top, bottom: padding.bottom, width: c.fit ? c.width : null});
+ }
+ },
+ arrange: function(inC, inName) {
+ if (this.container.wrap) {
+ this.arrangeWrap(inC, inName);
+ } else {
+ this.arrangeNoWrap(inC, inName);
+ }
+ },
+ arrangeNoWrap: function(inC, inName) {
+ var c$ = this.container.getPanels();
+ var s = this.container.clamp(inName);
+ var nw = this.containerBounds.width;
+ // do we have enough content to fill the width?
+ for (var i=s, cw=0, c; c=c$[i]; i++) {
+ cw += c.width + c.marginWidth;
+ if (cw > nw) {
+ break;
+ }
+ }
+ // if content width is less than needed, adjust starting point index and offset
+ var n = nw - cw;
+ var o = 0;
+ if (n > 0) {
+ var s1 = s;
+ for (var i=s-1, aw=0, c; c=c$[i]; i--) {
+ aw += c.width + c.marginWidth;
+ if (n - aw <= 0) {
+ o = (n - aw);
+ s = i;
+ break;
+ }
+ }
+ }
+ // arrange starting from needed index with detected offset so we fill space
+ for (var i=0, e=this.containerPadding.left + o, w, c; c=c$[i]; i++) {
+ w = c.width + c.marginWidth;
+ if (i < s) {
+ this.arrangeControl(c, {left: -w});
+ } else {
+ this.arrangeControl(c, {left: Math.floor(e)});
+ e += w;
+ }
+ }
+ },
+ arrangeWrap: function(inC, inName) {
+ for (var i=0, e=this.containerPadding.left, m, c; c=inC[i]; i++) {
+ this.arrangeControl(c, {left: e});
+ e += c.width + c.marginWidth;
+ }
+ },
+ calcArrangementDifference: function(inI0, inA0, inI1, inA1) {
+ var i = Math.abs(inI0 % this.c$.length);
+ return inA0[i].left - inA1[i].left;
+ },
+ destroy: function() {
+ var c$ = this.container.getPanels();
+ for (var i=0, c; c=c$[i]; i++) {
+ enyo.Arranger.positionControl(c, {left: null, top: null});
+ c.applyStyle("top", null);
+ c.applyStyle("bottom", null);
+ c.applyStyle("left", null);
+ c.applyStyle("width", null);
+ }
+ this.inherited(arguments);
+ }
+});
diff --git a/html/lib/layout/panels/source/arrangers/CollapsingArranger.js b/html/lib/layout/panels/source/arrangers/CollapsingArranger.js
new file mode 100644
index 0000000..799c94b
--- /dev/null
+++ b/html/lib/layout/panels/source/arrangers/CollapsingArranger.js
@@ -0,0 +1,80 @@
+/**
+ _enyo.CollapsingArranger_ is an <a href="#enyo.Arranger">enyo.Arranger</a>
+ that displays the active control, along with some number of inactive
+ controls to fill the available space. The active control is positioned on
+ the left side of the container and the rest of the views are laid out to the
+ right. The last control, if visible, will expand to fill whatever space is
+ not taken up by the previous controls.
+
+ For best results with CollapsingArranger, you should set a minimum width
+ for each control via a CSS style, e.g., _min-width: 25%_ or
+ _min-width: 250px_.
+
+ Transitions between arrangements are handled by sliding the new control in
+ from the right and collapsing the old control to the left.
+*/
+enyo.kind({
+ name: "enyo.CollapsingArranger",
+ kind: "CarouselArranger",
+ size: function() {
+ this.clearLastSize();
+ this.inherited(arguments);
+ },
+ //* @protected
+ // clear size from last if it's not actually the last
+ // (required for adding another control)
+ clearLastSize: function() {
+ for (var i=0, c$=this.container.getPanels(), c; c=c$[i]; i++) {
+ if (c._fit && i != c$.length-1) {
+ c.applyStyle("width", null);
+ c._fit = null;
+ }
+ }
+ },
+ //* @public
+ arrange: function(inC, inIndex) {
+ var c$ = this.container.getPanels();
+ for (var i=0, e=this.containerPadding.left, m, c; c=c$[i]; i++) {
+ this.arrangeControl(c, {left: e});
+ if (i >= inIndex) {
+ e += c.width + c.marginWidth;
+ }
+ // FIXME: overdragging-ish
+ if (i == c$.length - 1 && inIndex < 0) {
+ this.arrangeControl(c, {left: e - inIndex});
+ }
+ }
+ },
+ calcArrangementDifference: function(inI0, inA0, inI1, inA1) {
+ var i = this.container.getPanels().length-1;
+ return Math.abs(inA1[i].left - inA0[i].left);
+ },
+ flowControl: function(inControl, inA) {
+ this.inherited(arguments);
+ if (this.container.realtimeFit) {
+ var c$ = this.container.getPanels();
+ var l = c$.length-1;
+ var last = c$[l];
+ if (inControl == last) {
+ this.fitControl(inControl, inA.left);
+ }
+ }
+
+ },
+ finish: function() {
+ this.inherited(arguments);
+ if (!this.container.realtimeFit && this.containerBounds) {
+ var c$ = this.container.getPanels();
+ var a$ = this.container.arrangement;
+ var l = c$.length-1;
+ var c = c$[l];
+ this.fitControl(c, a$[l].left);
+ }
+ },
+ //* @protected
+ fitControl: function(inControl, inOffset) {
+ inControl._fit = true;
+ inControl.applyStyle("width", (this.containerBounds.width - inOffset) + "px");
+ inControl.resized();
+ }
+});
diff --git a/html/lib/layout/panels/source/arrangers/OtherArrangers.js b/html/lib/layout/panels/source/arrangers/OtherArrangers.js
new file mode 100644
index 0000000..533bac3
--- /dev/null
+++ b/html/lib/layout/panels/source/arrangers/OtherArrangers.js
@@ -0,0 +1,202 @@
+/**
+ _enyo.LeftRightArranger_ is an <a href="#enyo.Arranger">enyo.Arranger</a>
+ that displays the active control and some of the previous and next controls.
+ The active control is centered horizontally in the container, and the
+ previous and next controls are laid out to the left and right, respectively.
+
+ Transitions between arrangements are handled by sliding the new control
+ in from the right and sliding the active control out to the left.
+*/
+enyo.kind({
+ name: "enyo.LeftRightArranger",
+ kind: "Arranger",
+ //* The margin width (i.e., how much of the previous and next controls
+ //* are visible) in pixels
+ margin: 40,
+ //* @protected
+ axisSize: "width",
+ offAxisSize: "height",
+ axisPosition: "left",
+ constructor: function() {
+ this.inherited(arguments);
+ this.margin = this.container.margin != null ? this.container.margin : this.margin;
+ },
+ //* @public
+ size: function() {
+ var c$ = this.container.getPanels();
+ var port = this.containerBounds[this.axisSize];
+ var box = port - this.margin -this.margin;
+ for (var i=0, b, c; c=c$[i]; i++) {
+ b = {};
+ b[this.axisSize] = box;
+ b[this.offAxisSize] = "100%";
+ c.setBounds(b);
+ }
+ },
+ arrange: function(inC, inIndex) {
+ var o = Math.floor(this.container.getPanels().length/2);
+ var c$ = this.getOrderedControls(Math.floor(inIndex)-o);
+ var box = this.containerBounds[this.axisSize] - this.margin -this.margin;
+ var e = this.margin - box * o;
+ var m = (c$.length - 1) / 2;
+ for (var i=0, c, b, v; c=c$[i]; i++) {
+ b = {};
+ b[this.axisPosition] = e;
+ b.opacity = (i == 0 || i == c$.length-1) ? 0 : 1;
+ this.arrangeControl(c, b);
+ e += box;
+ }
+ },
+ calcArrangementDifference: function(inI0, inA0, inI1, inA1) {
+ var i = Math.abs(inI0 % this.c$.length);
+ //enyo.log(inI0, inI1);
+ return inA0[i][this.axisPosition] - inA1[i][this.axisPosition];
+ },
+ destroy: function() {
+ var c$ = this.container.getPanels();
+ for (var i=0, c; c=c$[i]; i++) {
+ enyo.Arranger.positionControl(c, {left: null, top: null});
+ enyo.Arranger.opacifyControl(c, 1);
+ c.applyStyle("left", null);
+ c.applyStyle("top", null);
+ c.applyStyle("height", null);
+ c.applyStyle("width", null);
+ }
+ this.inherited(arguments);
+ }
+});
+
+/**
+ _enyo.TopBottomArranger_ is an <a href="#enyo.Arranger">enyo.Arranger</a>
+ that displays the active control and some of the previous and next controls.
+ The active control is centered vertically in the container, and the previous
+ and next controls are laid out above and below, respectively.
+
+ Transitions between arrangements are handled by sliding the new control
+ in from the bottom and sliding the active control out the top.
+*/
+enyo.kind({
+ name: "enyo.TopBottomArranger",
+ kind: "LeftRightArranger",
+ dragProp: "ddy",
+ dragDirectionProp: "yDirection",
+ canDragProp: "vertical",
+ //* @protected
+ axisSize: "height",
+ offAxisSize: "width",
+ axisPosition: "top"
+});
+
+/**
+ _enyo.SpiralArranger_ is an <a href="#enyo.Arranger">enyo.Arranger</a> that
+ arranges controls in a spiral. The active control is positioned on top and
+ the other controls are laid out in a spiral pattern below.
+
+ Transitions between arrangements are handled by rotating the new control
+ up from below and rotating the active control down to the end of the spiral.
+*/
+enyo.kind({
+ name: "enyo.SpiralArranger",
+ kind: "Arranger",
+ //* Always go through incremental arrangements when transitioning
+ incrementalPoints: true,
+ //* The amount of space between successive controls
+ inc: 20,
+ size: function() {
+ var c$ = this.container.getPanels();
+ var b = this.containerBounds;
+ var w = this.controlWidth = b.width/3;
+ var h = this.controlHeight = b.height/3;
+ for (var i=0, c; c=c$[i]; i++) {
+ c.setBounds({width: w, height: h});
+ }
+ },
+ arrange: function(inC, inName) {
+ var s = this.inc;
+ for (var i=0, l=inC.length, c; c=inC[i]; i++) {
+ var x = Math.cos(i/l * 2*Math.PI) * i * s + this.controlWidth;
+ var y = Math.sin(i/l * 2*Math.PI) * i * s + this.controlHeight;
+ this.arrangeControl(c, {left: x, top: y});
+ }
+ },
+ start: function() {
+ this.inherited(arguments);
+ var c$ = this.getOrderedControls(this.container.toIndex);
+ for (var i=0, c; c=c$[i]; i++) {
+ c.applyStyle("z-index", c$.length - i);
+ }
+ },
+ calcArrangementDifference: function(inI0, inA0, inI1, inA1) {
+ return this.controlWidth;
+ },
+ destroy: function() {
+ var c$ = this.container.getPanels();
+ for (var i=0, c; c=c$[i]; i++) {
+ c.applyStyle("z-index", null);
+ enyo.Arranger.positionControl(c, {left: null, top: null});
+ c.applyStyle("left", null);
+ c.applyStyle("top", null);
+ c.applyStyle("height", null);
+ c.applyStyle("width", null);
+ }
+ this.inherited(arguments);
+ }
+});
+
+
+/**
+ _enyo.GridArranger_ is an <a href="#enyo.Arranger">enyo.Arranger</a> that
+ arranges controls in a grid. The active control is positioned at the
+ top-left of the grid and the other controls are laid out from left to right
+ and then from top to bottom.
+
+ Transitions between arrangements are handled by moving the active control to
+ the end of the grid and shifting the other controls to the left, or up to
+ the previous row, to fill the space.
+*/
+enyo.kind({
+ name: "enyo.GridArranger",
+ kind: "Arranger",
+ //* Always go through incremental arrangements when transitioning
+ incrementalPoints: true,
+ //* @public
+ //* Column width
+ colWidth: 100,
+ //* Column height
+ colHeight: 100,
+ size: function() {
+ var c$ = this.container.getPanels();
+ var w=this.colWidth, h=this.colHeight;
+ for (var i=0, c; c=c$[i]; i++) {
+ c.setBounds({width: w, height: h});
+ }
+ },
+ arrange: function(inC, inIndex) {
+ var w=this.colWidth, h=this.colHeight;
+ var cols = Math.floor(this.containerBounds.width / w);
+ var c;
+ for (var y=0, i=0; i<inC.length; y++) {
+ for (var x=0; (x<cols) && (c=inC[i]); x++, i++) {
+ this.arrangeControl(c, {left: w*x, top: h*y});
+ }
+ }
+ },
+ flowControl: function(inControl, inA) {
+ this.inherited(arguments);
+ enyo.Arranger.opacifyControl(inControl, inA.top % this.colHeight != 0 ? 0.25 : 1);
+ },
+ calcArrangementDifference: function(inI0, inA0, inI1, inA1) {
+ return this.colWidth;
+ },
+ destroy: function() {
+ var c$ = this.container.getPanels();
+ for (var i=0, c; c=c$[i]; i++) {
+ enyo.Arranger.positionControl(c, {left: null, top: null});
+ c.applyStyle("left", null);
+ c.applyStyle("top", null);
+ c.applyStyle("height", null);
+ c.applyStyle("width", null);
+ }
+ this.inherited(arguments);
+ }
+});
diff --git a/html/lib/layout/panels/source/arrangers/package.js b/html/lib/layout/panels/source/arrangers/package.js
new file mode 100644
index 0000000..135b754
--- /dev/null
+++ b/html/lib/layout/panels/source/arrangers/package.js
@@ -0,0 +1,9 @@
+enyo.depends(
+ "Arranger.js",
+ "Arranger.css",
+ "CardArranger.js",
+ "CardSlideInArranger.js",
+ "CarouselArranger.js",
+ "CollapsingArranger.js",
+ "OtherArrangers.js"
+); \ No newline at end of file
diff --git a/html/lib/layout/panels/source/package.js b/html/lib/layout/panels/source/package.js
new file mode 100644
index 0000000..270146f
--- /dev/null
+++ b/html/lib/layout/panels/source/package.js
@@ -0,0 +1,5 @@
+enyo.depends(
+ "arrangers",
+ "Panels.css",
+ "Panels.js"
+); \ No newline at end of file
diff --git a/html/lib/layout/slideable/package.js b/html/lib/layout/slideable/package.js
new file mode 100644
index 0000000..f8b32e7
--- /dev/null
+++ b/html/lib/layout/slideable/package.js
@@ -0,0 +1,3 @@
+enyo.depends(
+ "source"
+);
diff --git a/html/lib/layout/slideable/source/Slideable.js b/html/lib/layout/slideable/source/Slideable.js
new file mode 100644
index 0000000..4ef0a08
--- /dev/null
+++ b/html/lib/layout/slideable/source/Slideable.js
@@ -0,0 +1,312 @@
+/**
+ Slideable is a control that can be dragged either horizontally or vertically
+ between a minimum and a maximum value. When released from dragging, a
+ Slideable will animate to its minimum or maximum position, depending on the
+ direction of the drag.
+
+ The *min* value specifies a position left of or above the initial position,
+ to which the Slideable may be dragged.
+ The *max* value specifies a position right of or below the initial position,
+ to which the Slideable may be dragged.
+ The *value* property specifies the current position of the Slideable,
+ between the minimum and maximum positions.
+
+ *min*, *max*, and *value* may be specified in units of "px" or "%".
+
+ The *axis* property controls whether the Slideable slides left-right (h) or
+ up-down (v).
+
+ The following control is placed 90% off the screen to the right, and slides
+ to its natural position.
+
+ {kind: "enyo.Slideable", value: -90, min: -90, unit: "%",
+ classes: "enyo-fit", style: "width: 300px;",
+ components: [
+ {content: "stuff"}
+ ]
+ }
+*/
+enyo.kind({
+ name: "enyo.Slideable",
+ kind: "Control",
+ published: {
+ //* Direction of sliding; can be "h" or "v"
+ axis: "h",
+ //* A value between min and max to position the Slideable
+ value: 0,
+ //* Unit for min, max, and value; can be "px" or "%"
+ unit: "px",
+ //* A minimum value to slide to
+ min: 0,
+ //* A maximum value to slide to
+ max: 0,
+ accelerated: "auto",
+ //* Set to false to prevent the Slideable from dragging with elasticity past its min/max value.
+ overMoving: true,
+ //* Set to false to disable dragging.
+ draggable: true
+ },
+ events: {
+ //* Fires when the Slideable finishes animating.
+ onAnimateFinish: "",
+ onChange: ""
+ },
+ // Set to true to prevent a drag from bubbling beyond the Slideable.
+ preventDragPropagation: false,
+ //* @protected
+ tools: [
+ {kind: "Animator", onStep: "animatorStep", onEnd: "animatorComplete"}
+ ],
+ handlers: {
+ ondragstart: "dragstart",
+ ondrag: "drag",
+ ondragfinish: "dragfinish"
+ },
+ kDragScalar: 1,
+ dragEventProp: "dx",
+ unitModifier: false,
+ canTransform: false,
+ //* @protected
+ create: function() {
+ this.inherited(arguments);
+ this.acceleratedChanged();
+ this.transformChanged();
+ this.axisChanged();
+ this.valueChanged();
+ this.addClass("enyo-slideable");
+ },
+ initComponents: function() {
+ this.createComponents(this.tools);
+ this.inherited(arguments);
+ },
+ rendered: function() {
+ this.inherited(arguments);
+ this.canModifyUnit();
+ this.updateDragScalar();
+ },
+ resizeHandler: function() {
+ this.inherited(arguments);
+ this.updateDragScalar();
+ },
+ canModifyUnit: function() {
+ if (!this.canTransform) {
+ var b = this.getInitialStyleValue(this.hasNode(), this.boundary);
+ // If inline style of "px" exists, while unit is "%"
+ if (b.match(/px/i) && (this.unit === "%")) {
+ // Set unitModifier - used to over-ride "%"
+ this.unitModifier = this.getBounds()[this.dimension];
+ }
+ }
+ },
+ getInitialStyleValue: function(inNode, inBoundary) {
+ var s = enyo.dom.getComputedStyle(inNode);
+ if (s) {
+ return s.getPropertyValue(inBoundary);
+ } else if (inNode && inNode.currentStyle) {
+ return inNode.currentStyle[inBoundary];
+ }
+ return "0";
+ },
+ updateBounds: function(inValue, inDimensions) {
+ var inBounds = {};
+ inBounds[this.boundary] = inValue;
+ this.setBounds(inBounds, this.unit);
+
+ this.setInlineStyles(inValue, inDimensions);
+ },
+ updateDragScalar: function() {
+ if (this.unit == "%") {
+ var d = this.getBounds()[this.dimension];
+ this.kDragScalar = d ? 100 / d : 1;
+
+ if (!this.canTransform) {
+ this.updateBounds(this.value, 100);
+ }
+ }
+ },
+ transformChanged: function() {
+ this.canTransform = enyo.dom.canTransform();
+ },
+ acceleratedChanged: function() {
+ if (!(enyo.platform.android > 2)) {
+ enyo.dom.accelerate(this, this.accelerated);
+ }
+ },
+ axisChanged: function() {
+ var h = this.axis == "h";
+ this.dragMoveProp = h ? "dx" : "dy";
+ this.shouldDragProp = h ? "horizontal" : "vertical";
+ this.transform = h ? "translateX" : "translateY";
+ this.dimension = h ? "width" : "height";
+ this.boundary = h ? "left" : "top";
+ },
+ setInlineStyles: function(inValue, inDimensions) {
+ var inBounds = {};
+
+ if (this.unitModifier) {
+ inBounds[this.boundary] = this.percentToPixels(inValue, this.unitModifier);
+ inBounds[this.dimension] = this.unitModifier;
+ this.setBounds(inBounds);
+ } else {
+ if (inDimensions) {
+ inBounds[this.dimension] = inDimensions;
+ } else {
+ inBounds[this.boundary] = inValue;
+ }
+ this.setBounds(inBounds, this.unit);
+ }
+ },
+ valueChanged: function(inLast) {
+ var v = this.value;
+ if (this.isOob(v) && !this.isAnimating()) {
+ this.value = this.overMoving ? this.dampValue(v) : this.clampValue(v);
+ }
+ // FIXME: android cannot handle nested compositing well so apply acceleration only if needed
+ // desktop chrome doesn't like this code path so avoid...
+ if (enyo.platform.android > 2) {
+ if (this.value) {
+ if (inLast === 0 || inLast === undefined) {
+ enyo.dom.accelerate(this, this.accelerated);
+ }
+ } else {
+ enyo.dom.accelerate(this, false);
+ }
+ }
+
+ // If platform supports transforms
+ if (this.canTransform) {
+ enyo.dom.transformValue(this, this.transform, this.value + this.unit);
+ // else update inline styles
+ } else {
+ this.setInlineStyles(this.value, false);
+ }
+ this.doChange();
+ },
+ getAnimator: function() {
+ return this.$.animator;
+ },
+ isAtMin: function() {
+ return this.value <= this.calcMin();
+ },
+ isAtMax: function() {
+ return this.value >= this.calcMax();
+ },
+ calcMin: function() {
+ return this.min;
+ },
+ calcMax: function() {
+ return this.max;
+ },
+ clampValue: function(inValue) {
+ var min = this.calcMin();
+ var max = this.calcMax();
+ return Math.max(min, Math.min(inValue, max));
+ },
+ dampValue: function(inValue) {
+ return this.dampBound(this.dampBound(inValue, this.min, 1), this.max, -1);
+ },
+ dampBound: function(inValue, inBoundary, inSign) {
+ var v = inValue;
+ if (v * inSign < inBoundary * inSign) {
+ v = inBoundary + (v - inBoundary) / 4;
+ }
+ return v;
+ },
+ percentToPixels: function(value, dimension) {
+ return Math.floor((dimension / 100) * value);
+ },
+ pixelsToPercent: function(value) {
+ var boundary = this.unitModifier ? this.getBounds()[this.dimension] : this.container.getBounds()[this.dimension];
+ return (value / boundary) * 100;
+ },
+ // dragging
+ shouldDrag: function(inEvent) {
+ return this.draggable && inEvent[this.shouldDragProp];
+ },
+ isOob: function(inValue) {
+ return inValue > this.calcMax() || inValue < this.calcMin();
+ },
+ dragstart: function(inSender, inEvent) {
+ if (this.shouldDrag(inEvent)) {
+ inEvent.preventDefault();
+ this.$.animator.stop();
+ inEvent.dragInfo = {};
+ this.dragging = true;
+ this.drag0 = this.value;
+ this.dragd0 = 0;
+ return this.preventDragPropagation;
+ }
+ },
+ drag: function(inSender, inEvent) {
+ if (this.dragging) {
+ inEvent.preventDefault();
+ var d = this.canTransform ? inEvent[this.dragMoveProp] * this.kDragScalar : this.pixelsToPercent(inEvent[this.dragMoveProp]);
+ var v = this.drag0 + d;
+ var dd = d - this.dragd0;
+ this.dragd0 = d;
+ if (dd) {
+ inEvent.dragInfo.minimizing = dd < 0;
+ }
+ this.setValue(v);
+ return this.preventDragPropagation;
+ }
+ },
+ dragfinish: function(inSender, inEvent) {
+ if (this.dragging) {
+ this.dragging = false;
+ this.completeDrag(inEvent);
+ inEvent.preventTap();
+ return this.preventDragPropagation;
+ }
+ },
+ completeDrag: function(inEvent) {
+ if (this.value !== this.calcMax() && this.value != this.calcMin()) {
+ this.animateToMinMax(inEvent.dragInfo.minimizing);
+ }
+ },
+ // animation
+ isAnimating: function() {
+ return this.$.animator.isAnimating();
+ },
+ play: function(inStart, inEnd) {
+ this.$.animator.play({
+ startValue: inStart,
+ endValue: inEnd,
+ node: this.hasNode()
+ });
+ },
+ //* @public
+ //* Animates to the given value.
+ animateTo: function(inValue) {
+ this.play(this.value, inValue);
+ },
+ //* Animates to the minimum value.
+ animateToMin: function() {
+ this.animateTo(this.calcMin());
+ },
+ //* Animates to the maximum value.
+ animateToMax: function() {
+ this.animateTo(this.calcMax());
+ },
+ //* @protected
+ animateToMinMax: function(inMin) {
+ if (inMin) {
+ this.animateToMin();
+ } else {
+ this.animateToMax();
+ }
+ },
+ animatorStep: function(inSender) {
+ this.setValue(inSender.value);
+ return true;
+ },
+ animatorComplete: function(inSender) {
+ this.doAnimateFinish(inSender);
+ return true;
+ },
+ //* @public
+ //* Toggles between min and max with animation.
+ toggleMinMax: function() {
+ this.animateToMinMax(!this.isAtMin());
+ }
+});
diff --git a/html/lib/layout/slideable/source/package.js b/html/lib/layout/slideable/source/package.js
new file mode 100644
index 0000000..8c4ec43
--- /dev/null
+++ b/html/lib/layout/slideable/source/package.js
@@ -0,0 +1,3 @@
+enyo.depends(
+ "Slideable.js"
+);
diff --git a/html/lib/layout/tree/package.js b/html/lib/layout/tree/package.js
new file mode 100644
index 0000000..1ec0cd0
--- /dev/null
+++ b/html/lib/layout/tree/package.js
@@ -0,0 +1,3 @@
+enyo.depends(
+ "source"
+); \ No newline at end of file
diff --git a/html/lib/layout/tree/source/Node.css b/html/lib/layout/tree/source/Node.css
new file mode 100644
index 0000000..718a2e6
--- /dev/null
+++ b/html/lib/layout/tree/source/Node.css
@@ -0,0 +1,28 @@
+.enyo-node {
+ cursor: default;
+ padding: 4px;
+}
+
+.enyo-node img {
+ vertical-align: middle;
+ padding-right: 6px;
+}
+
+.enyo-node-box {
+ overflow: hidden;
+}
+
+.enyo-node-client {
+ position: relative;
+}
+
+.enyo-animate .enyo-node-box, .enyo-animate .enyo-node-client {
+ transition-property: height, top;
+ transition-duration: 0.2s, 0.2s;
+ -moz-transition-property: height, top;
+ -moz-transition-duration: 0.2s, 0.2s;
+ -o-transition-property: height, top;
+ -o-transition-duration: 0.2s, 0.2s;
+ -webkit-transition-property: height, top;
+ -webkit-transition-duration: 0.2s, 0.2s;
+}
diff --git a/html/lib/layout/tree/source/Node.js b/html/lib/layout/tree/source/Node.js
new file mode 100644
index 0000000..ff79c9a
--- /dev/null
+++ b/html/lib/layout/tree/source/Node.js
@@ -0,0 +1,269 @@
+/**
+ _enyo.Node_ is a control that creates structured trees based on Enyo's child
+ component hierarchy format, e.g.:
+
+ {kind: "Node", icon: "images/folder-open.png", content: "Tree",
+ expandable: true, expanded: true, components: [
+ {icon: "images/file.png", content: "Alpha"},
+ {icon: "images/folder-open.png", content: "Bravo",
+ expandable: true, expanded: false, components: [
+ {icon: "images/file.png", content: "Bravo-Alpha"},
+ {icon: "images/file.png", content: "Bravo-Bravo"},
+ {icon: "images/file.png", content: "Bravo-Charlie"}
+ ]
+ }
+ ]
+ }
+
+ The default kind of components within a node is itself _enyo.Node_, so only
+ the top-level node of the tree needs to be explicitly defined as such.
+
+ When an expandable tree node expands, an _onExpand_ event is sent; when it
+ is tapped, a _nodeTap_ event is sent.
+
+ When the optional property _onlyIconExpands_ is set to true, expandable
+ nodes may only be opened by tapping the icon; tapping the content label
+ will fire the _nodeTap_ event, but will not expand the node.
+*/
+
+enyo.kind({
+ name: "enyo.Node",
+ published: {
+ //* @public
+ //* Whether or not the Node is expandable and has child branches
+ expandable: false,
+ //* Open/closed state of the current Node
+ expanded: false,
+ //* Path to image to be used as the icon for this Node
+ icon: "",
+ /**
+ Optional flag that, when true, causes the Node to expand only when
+ the icon is tapped; not when the contents are tapped
+ */
+ onlyIconExpands: false,
+ //* @protected
+ //* Adds or removes the Enyo-selected CSS class.
+ selected: false
+ },
+ style: "padding: 0 0 0 16px;",
+ content: "Node",
+ defaultKind: "Node",
+ classes: "enyo-node",
+ components: [
+ {name: "icon", kind: "Image", showing: false},
+ {kind: "Control", name: "caption", Xtag: "span", style: "display: inline-block; padding: 4px;", allowHtml: true},
+ {kind: "Control", name: "extra", tag: 'span', allowHtml: true}
+ ],
+ childClient: [
+ {kind: "Control", name: "box", classes: "enyo-node-box", Xstyle: "border: 1px solid orange;", components: [
+ {kind: "Control", name: "client", classes: "enyo-node-client", Xstyle: "border: 1px solid lightblue;"}
+ ]}
+ ],
+ handlers: {
+ ondblclick: "dblclick"
+ },
+ events: {
+ //* @public
+ //* Fired when the Node is tapped
+ onNodeTap: "nodeTap",
+ //* Fired when the Node is double-clicked
+ onNodeDblClick: "nodeDblClick",
+ /**
+ Fired when the Node expands or contracts, as indicated by the
+ 'expanded' property in the event data
+ */
+ onExpand: "nodeExpand",
+ //* Fired when the Node is destroyed
+ onDestroyed: "nodeDestroyed"
+ },
+ //
+ //* @protected
+ create: function() {
+ this.inherited(arguments);
+ //this.expandedChanged();
+ //this.levelChanged();
+ this.selectedChanged();
+ this.iconChanged();
+ },
+ destroy: function() {
+ this.doDestroyed();
+ this.inherited(arguments);
+ },
+ initComponents: function() {
+ // TODO: optimize to create the childClient on demand
+ //this.hasChildren = this.components;
+ if (this.expandable) {
+ this.kindComponents = this.kindComponents.concat(this.childClient);
+ }
+ this.inherited(arguments);
+ },
+ //
+ contentChanged: function() {
+ //this.$.caption.setContent((this.expandable ? (this.expanded ? "-" : "+") : "") + this.content);
+ this.$.caption.setContent(this.content);
+ },
+ iconChanged: function() {
+ this.$.icon.setSrc(this.icon);
+ this.$.icon.setShowing(Boolean(this.icon));
+ },
+ selectedChanged: function() {
+ this.addRemoveClass("enyo-selected", this.selected);
+ },
+ rendered: function() {
+ this.inherited(arguments);
+ if (this.expandable && !this.expanded) {
+ this.quickCollapse();
+ }
+ },
+ //
+ addNodes: function(inNodes) {
+ this.destroyClientControls();
+ for (var i=0, n; n=inNodes[i]; i++) {
+ this.createComponent(n);
+ }
+ this.$.client.render();
+ },
+ addTextNodes: function(inNodes) {
+ this.destroyClientControls();
+ for (var i=0, n; n=inNodes[i]; i++) {
+ this.createComponent({content: n});
+ }
+ this.$.client.render();
+ },
+ //
+ tap: function(inSender, inEvent) {
+ if(!this.onlyIconExpands) {
+ this.toggleExpanded();
+ this.doNodeTap();
+ } else {
+ if((inEvent.target==this.$.icon.hasNode())) {
+ this.toggleExpanded();
+ } else {
+ this.doNodeTap();
+ }
+ }
+ return true;
+ },
+ dblclick: function(inSender, inEvent) {
+ this.doNodeDblClick();
+ return true;
+ },
+ //
+ toggleExpanded: function() {
+ this.setExpanded(!this.expanded);
+ },
+ quickCollapse: function() {
+ this.removeClass("enyo-animate");
+ this.$.box.applyStyle("height", "0");
+ var h = this.$.client.getBounds().height;
+ this.$.client.setBounds({top: -h});
+ },
+ _expand: function() {
+ this.addClass("enyo-animate");
+ var h = this.$.client.getBounds().height;
+ this.$.box.setBounds({height: h});
+ this.$.client.setBounds({top: 0});
+ setTimeout(enyo.bind(this, function() {
+ // things may have happened in the interim, make sure
+ // we only fix height if we are still expanded
+ if (this.expanded) {
+ this.removeClass("enyo-animate");
+ this.$.box.applyStyle("height", "auto");
+ }
+ }), 225);
+ },
+ _collapse: function() {
+ // disable transitions
+ this.removeClass("enyo-animate");
+ // fix the height of our box (rather than 'auto'), this
+ // gives webkit something to lerp from
+ var h = this.$.client.getBounds().height;
+ this.$.box.setBounds({height: h});
+ // yield the thead so DOM can make those changes (without transitions)
+ setTimeout(enyo.bind(this, function() {
+ // enable transitions
+ this.addClass("enyo-animate");
+ // shrink our box to 0
+ this.$.box.applyStyle("height", "0");
+ // slide the contents up
+ this.$.client.setBounds({top: -h});
+ }), 25);
+ },
+ expandedChanged: function(inOldExpanded) {
+ if (!this.expandable) {
+ this.expanded = false;
+ } else {
+ var event = {expanded: this.expanded};
+ this.doExpand(event);
+ if (!event.wait) {
+ this.effectExpanded();
+ }
+ }
+ },
+ effectExpanded: function() {
+ if (this.$.client) {
+ if (!this.expanded) {
+ this._collapse();
+ } else {
+ this._expand();
+ }
+ }
+ //this.contentChanged();
+ }/*,
+ //
+ //
+ levelChanged: function() {
+ this.applyStyle("padding-left", 16 + "px");
+ },
+ toggleChildren: function() {
+ if (this.$.list) {
+ this.$.list.setShowing(this.expanded);
+ }
+ },
+ renderNodes: function(inNodes) {
+ var list = this.createComponent({name: "list", container: this});
+ for (var i=0, n; n=inNodes[i]; i++) {
+ n.setLevel(this.level + 1);
+ n.setContainer(list);
+ n.render();
+ }
+ list.render();
+ },
+ //* @public
+ addNodes: function(inNodes) {
+ this.renderNodes(inNodes);
+ this.toggleChildren();
+ },
+ removeNodes: function() {
+ if (this.$.list) {
+ this.$.list.destroy();
+ }
+ },
+ hasVisibleChildren: function() {
+ return this.expanded && this.$.list && this.$.list.controls.length > 0;
+ },
+ fetchParent: function() {
+ return this.level > 0 && this.container.container;
+ },
+ fetchChildren: function() {
+ return this.$.list && this.$.list.controls;
+ },
+ fetchFirstChild: function() {
+ return this.$.list && this.$.list.controls[0];
+ },
+ fetchLastChild: function() {
+ return this.$.list && this.$.list.controls[this.$.list.controls.length-1];
+ },
+ fetchPrevSibling: function() {
+ var i = this.container.controls.indexOf(this);
+ return this.level > 0 && this.container.controls[i-1];
+ },
+ fetchNextSibling: function() {
+ var i = this.container.controls.indexOf(this);
+ return this.level > 0 && this.container.controls[i+1];
+ },
+ getVisibleBounds: function() {
+ return this.$.client.getBounds();
+ }
+ */
+}); \ No newline at end of file
diff --git a/html/lib/layout/tree/source/package.js b/html/lib/layout/tree/source/package.js
new file mode 100644
index 0000000..5986bea
--- /dev/null
+++ b/html/lib/layout/tree/source/package.js
@@ -0,0 +1,4 @@
+enyo.depends(
+ "Node.css",
+ "Node.js"
+); \ No newline at end of file
diff --git a/html/lib/onyx/design/inline-controlled.html b/html/lib/onyx/design/inline-controlled.html
new file mode 100644
index 0000000..b233d19
--- /dev/null
+++ b/html/lib/onyx/design/inline-controlled.html
@@ -0,0 +1,230 @@
+<!doctype html>
+<html>
+<head>
+ <title>controlled inline: onyx toolbar design</title>
+ <link href="../source/css/onyx.css" rel="stylesheet" type="text/css" />
+ <style>
+ /*
+ the 'inline' class sets up a left-to-right container, with vertically centered children
+ */
+ .inline {
+ white-space: nowrap;
+ }
+ .inline > * {
+ display: inline-block;
+ vertical-align: middle;
+ }
+ .icon {
+ width: 32px;
+ height: 32px;
+ background-image: url(menu-icon-bookmark.png);
+ }
+ .bord {
+ border: 1px solid lightblue;
+ }
+ .big {
+ font-size: 48px;
+ }
+ .pad {
+ padding: 8px;
+ }
+ .bg {
+ background: orange;
+ }
+ /*
+ We can control line-height so that vertical-align: middle
+ becomes true center. Otherwise, when line-height is in effect
+ (i.e. if the actual height is less than line height),
+ user agents pad the line unevenly.
+ */
+ .lhc {
+ line-height: 0;
+ }
+ /*
+ Defeat line-height control above for children
+ */
+ .lhc > * {
+ line-height: normal;
+ }
+ /*
+ Enforce line-height 0 for an inline child of an inline (FIXME: fiddly)
+ */
+ .lhc .lhc {
+ line-height: 0px;
+ }
+ /**/
+ .h {
+ height: 64px;
+ }
+ /*
+ It's useful to be able to abut toolbars in an inline context
+ and not worry about varying heights.
+ Generally icons are the largest elements in a toolbar, and will
+ drive the height to this size.
+ We can't set a min-size directly on the toolbar because it
+ breaks centering (due to interation with line-height).
+ For some scenarios a developer may want to override the height value.
+ */
+ .stent {
+ /*
+ the point of the stent is to force the box height to some minimum, similar
+ to line-height, but with proper vertical centering
+ */
+ height: 72px;
+ /* the width is 0, but it adds some size to the inline because of ? */
+ width: 0px;
+ visibility: hidden;
+ }
+ .stent-fix {
+ /* stent imparts mystery width to it's parent */
+ /* setting font-size doesn't affect the mystery width */
+ /*font-size: 0px;*/
+ /* floating the stent prevents it from stenting */
+ /*float: left;*/
+ /* on non-mozilla, mystery size can be removed this way, but the actual pixels needed depends on font size */
+ /*margin-right: -6px;*/
+ /* on non-mozilla, this gets us within a few pixels for tested fonts */
+ margin-right: -0.25em;
+ }
+ /* */
+ .stenty > * {
+ min-height: 56px;
+ }
+ </style>
+</head>
+<body>
+ <label>"inline" tests</label>
+ <br /><br />
+
+ <div>
+ <label>combo content: when the text is larger than non-text objects, vertical centering goes wonky</label>
+ <div class="inline bord big">
+ <div>Text in Div</div>
+ <input />
+ <div class="icon"></div>
+ </div>
+ <br />
+
+ <label>... it's possible to control line-height in such a way to repair centering</label>
+ <div class="inline bord big lhc">
+ <div>Text in Div</div>
+ <input />
+ <div class="icon"></div>
+ </div>
+ <br />
+
+ <hr />
+
+ <label>but now setting height or min-height on toolbar breaks centering</label>
+ <div class="inline bord lhc bg big h">
+ <div>Text in Div</div>
+ <input />
+ <div class="icon"></div>
+ </div>
+ <br />
+
+ <div class="inline bord big lhc">
+ <div class="inline bord lhc bg h">
+ <div>Text in Div</div>
+ <input />
+ <div class="icon"></div>
+ </div>
+ <div class="inline bord lhc bg h">
+ <input />
+ <div class="icon"></div>
+ </div>
+ </div>
+ <br />
+
+ <hr />
+
+ <label>control height on children instead of toolbar</label>
+ <div class="inline bord big lhc">
+ <div class="inline bord lhc bg">
+ <div>Text in Div</div>
+ <input />
+ <div class="icon"></div>
+ </div>
+ <div class="inline bord lhc bg">
+ <input />
+ <div class="icon"></div>
+ </div>
+ </div>
+ <br />
+ <div class="inline bord big lhc">
+ <div class="inline bord lhc bg stenty">
+ <div>Text in Div</div>
+ <input />
+ <div class="icon"></div>
+ </div>
+ <div class="inline bord lhc bg stenty">
+ <input />
+ <div class="icon"></div>
+ </div>
+ </div>
+ <br />
+
+ <hr />
+
+ <label>it's possible to use a stent to effect min-height, but for unknown reasons, even a 0-width stent affects parent width</label>
+ <div class="inline bord big lhc">
+ <div class="inline bord lhc bg">
+ <div>Text in Div</div>
+ <input />
+ <div class="icon"></div>
+ </div>
+ <div class="inline bord lhc bg">
+ <input />
+ <div class="icon"></div>
+ </div>
+ </div>
+ <br />
+ <div class="inline bord big lhc">
+ <div class="inline bord lhc bg">
+ <div class="stent"></div>
+ <div>Text in Div</div>
+ <input />
+ <div class="icon"></div>
+ </div>
+ <div class="inline bord lhc bg">
+ <div class="stent"></div>
+ <input />
+ <div class="icon"></div>
+ </div>
+ </div>
+ <br />
+
+ <label>using img as stent node makes no difference</label>
+ <div class="inline bord big lhc">
+ <div class="inline bord lhc bg">
+ <img class="stent" />
+ <div>Text in Div</div>
+ <input />
+ <div class="icon"></div>
+ </div>
+ <div class="inline bord lhc bg">
+ <img class="stent" />
+ <input />
+ <div class="icon"></div>
+ </div>
+ </div>
+ <br />
+
+ <label>using negative margin on stent can approximate 0 width</label>
+ <div class="inline bord big lhc">
+ <div class="inline bord lhc bg">
+ <div class="stent stent-fix"></div>
+ <div>Text in Div</div>
+ <input />
+ <div class="icon"></div>
+ </div>
+ <div class="inline bord lhc bg">
+ <div class="stent stent-fix"></div>
+ <input />
+ <div class="icon"></div>
+ </div>
+ </div>
+ <br />
+ </div>
+</body>
+</html>
diff --git a/html/lib/onyx/design/inline.html b/html/lib/onyx/design/inline.html
new file mode 100644
index 0000000..41466e8
--- /dev/null
+++ b/html/lib/onyx/design/inline.html
@@ -0,0 +1,102 @@
+<!doctype html>
+<html>
+<head>
+ <title>inline: onyx toolbar design</title>
+ <link href="../source/css/onyx.css" rel="stylesheet" type="text/css" />
+ <style>
+ /*
+ the 'inline' class sets up a left-to-right container, with vertically centered children
+ */
+ .inline {
+ white-space: nowrap;
+ }
+ .inline > * {
+ display: inline-block;
+ vertical-align: middle;
+ }
+ .icon {
+ width: 32px;
+ height: 32px;
+ background-image: url(menu-icon-bookmark.png);
+ }
+ .bord {
+ border: 1px solid lightblue;
+ }
+ .big {
+ font-size: 48px;
+ }
+ .pad {
+ padding: 8px;
+ }
+ .mh {
+ min-height: 124px;
+ }
+ .lh {
+ line-height: 86px;
+ }
+ </style>
+</head>
+<body>
+ <label>"inline" tests</label>
+ <br /><br />
+ <div>
+ <label>no content</label>
+ <div class="inline bord"></div>
+ <br />
+
+ <label>text only content</label>
+ <div class="inline bord">Text</div>
+ <br />
+
+ <label>div content</label>
+ <div class="inline bord">
+ <div>Text in Div</div>
+ </div>
+ <br />
+
+ <label>input content</label>
+ <div class="inline bord">
+ <input class="" />
+ </div>
+ <br />
+
+ <label>icon content</label>
+ <div class="inline bord">
+ <div class="icon"></div>
+ </div>
+ <br />
+
+ <label>combo content</label>
+ <div class="inline bord">
+ <div>Text in Div</div>
+ <input class="" />
+ <div class="icon"></div>
+ </div>
+ <br />
+
+ <label>combo content: when the text is larger than non-text objects, vertical centering goes wonky</label>
+ <div class="inline bord big">
+ <div>Text in Div</div>
+ <input class="" />
+ <div class="icon"></div>
+ </div>
+ <br />
+
+ <label>controlling height</label>
+ <div class="inline bord big mh">
+ <div>Text in Div</div>
+ <input class="" />
+ <div class="icon"></div>
+ </div>
+ <br />
+
+ <label>controlling line-height</label>
+ <div class="inline bord big lh pad">
+ <div>Text in Div</div>
+ <input class="" />
+ <div class="icon"></div>
+ </div>
+ <br />
+ </div>
+</body>
+</html>
diff --git a/html/lib/onyx/design/menu-icon-bookmark.png b/html/lib/onyx/design/menu-icon-bookmark.png
new file mode 100644
index 0000000..81d2344
--- /dev/null
+++ b/html/lib/onyx/design/menu-icon-bookmark.png
Binary files differ
diff --git a/html/lib/onyx/design/toolbar-extended.html b/html/lib/onyx/design/toolbar-extended.html
new file mode 100644
index 0000000..edbfda1
--- /dev/null
+++ b/html/lib/onyx/design/toolbar-extended.html
@@ -0,0 +1,243 @@
+
+<!doctype html>
+<html>
+<head>
+ <title>onyx toolbar design</title>
+ <link href="../source/css/onyx.css" rel="stylesheet" type="text/css" />
+ <style>
+ /*
+ the 'inline' class attempts to setup a left-to-right container, with vertically centered children
+ */
+ .toolbar > *, .inline > * {
+ display: inline-block;
+ vertical-align: middle;
+ }
+ .toolbar, .inline {
+ white-space: nowrap;
+ /*
+ We want to control line-height so that vertical-align: middle
+ becomes true center. Otherwise, when line-height is in effect
+ (i.e. if the actual height is less than line height),
+ user agents pad the line unevenly.
+ */
+ line-height: 0;
+ }
+ .toolbar > *, .inline > * {
+ /*
+ Defeat line-height control above for children
+ */
+ line-height: normal;
+ }
+ .toolbar .inline, .inline .inline {
+ /*
+ Enforce line-height 0 for an inline child of an inline (FIXME: fiddly)
+ */
+ line-height: 0px;
+ }
+ /*
+ toolbar is just an inline context with specific styling
+ */
+ .toolbar {
+ padding: 8px;
+ /**/
+ box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ }
+ /*
+ It's useful to be able to abut toolbars in an inline context
+ and not worry about varying heights.
+ Generally icons are the largest elements in a toolbar, and will
+ drive the height to this size.
+ We can't set a min-size directly on the toolbar because it
+ breaks centering (due to interation with line-height).
+ For some scenarios a developer may want to override the height value.
+ */
+ .stent {
+ visibility: none;
+ width: 0px;
+ height: 34px;
+ }
+ /* */
+ .onyx .toolbar {
+ padding: 8px;
+ /**/
+ border: 1px solid #3A3A3A;
+ background: #4C4C4C url(../images/gradient.png) repeat-x 0 bottom;
+ color: white;
+ }
+ /* */
+ button {
+ /* only needed for IE */
+ line-height: normal !important;
+ }
+ .icon {
+ width: 32px;
+ height: 32px;
+ background-image: url(menu-icon-bookmark.png);
+ }
+ /* */
+ /*
+ for display/debug only, not part of the design
+ */
+ .big {
+ font-size: 42px;
+ }
+ .bord {
+ border: 1px solid lightblue;
+ }
+ .bg {
+ background-color: Red;
+ }
+ .bg .toolbar {
+ background-color: white;
+ }
+ .lh0 {
+ line-height: 0px;
+ }
+ .lhn {
+ line-height: normal;
+ }
+ </style>
+</head>
+<body>
+ <div class="onyx">
+ <label>no content</label>
+ <div class="toolbar"></div>
+ <br />
+ <label>text only content (padding is ignored due to line height control)</label>
+ <div class="toolbar">Text</div>
+ <br />
+ <label>div content</label>
+ <div class="toolbar">
+ <div>Text in Div</div>
+ </div>
+ <br />
+ <label>input content</label>
+ <div class="toolbar">
+ <input class="" />
+ </div>
+ <br />
+ <label>input content</label>
+ <div class="toolbar">
+ <div class="icon"></div>
+ </div>
+ <br />
+ <label>input content</label>
+ <div class="toolbar">
+ <img src="menu-icon-bookmark.png" height="32"/>
+ </div>
+ <br />
+ <label>div content with min-height on toolbar (centering improper on tested user agents)</label>
+ <div class="toolbar" style="min-height: 50px;">
+ <div>Text in Div</div>
+ </div>
+ <br />
+ <label>div content with stent</label>
+ <div class="toolbar">
+ <div class="stent"></div>
+ <div>Text in Div</div>
+ </div>
+ <br />
+ <label>stented toolbars abutting in inline context, as long as the stent is the largest item the bars align. There should be no red color visible above or below each toolbar.</label>
+ <div class="inline" style="background-color: Red;">
+ <div class="toolbar">
+ <div class="stent"></div>
+ <div>Text in Div</div>
+ </div>
+ <div class="toolbar">
+ <div class="stent"></div>
+ <input class="" />
+ </div>
+ <div class="toolbar">
+ <div class="stent"></div>
+ <div class="icon"></div>
+ </div>
+ <div class="toolbar">
+ <div class="stent"></div>
+ <img src="menu-icon-bookmark.png" height="32"/>
+ </div>
+ </div>
+ </div>
+ <hr />
+ <div class="bord toolbar">
+ <div class="bord">div</div>
+ <input class="bord" />
+ <div class="bord icon"></div>
+ </div>
+ <br />
+ <div class="bord toolbar big">
+ <div class="bord">div</div>
+ <input class="bord" />
+ <div class="bord icon"></div>
+ </div>
+ <br />
+ <div class="bord toolbar big lh0">
+ <div class="bord lhn">div</div>
+ <input class="bord" />
+ <div class="bord icon"></div>
+ </div>
+ <br />
+ <div class="bord toolbar big lh0">
+ <div class="bord lhn">div</div>
+ <input class="bord" />
+ </div>
+ <br />
+ <br />
+ <div class="toolbar bord">
+ <div class="bord">div</div>
+ <input class="bord" />
+ <div class="bord inline">
+ <div class="bord">div</div>
+ <input class="bord" />
+ <div class="bord icon"></div>
+ </div>
+ <button>Button</button>
+ <div class="bord icon"></div>
+ <img class="bord" src="menu-icon-bookmark.png" height="32"/>
+ </div>
+ <br />
+ <div class="toolbar bord big">
+ <div class="bord">div</div>
+ <div class="bord inline">
+ <div class="bord">div</div>
+ <input class="bord" />
+ <div class="bord icon"></div>
+ </div>
+ <button>Button</button>
+ <div class="bord icon"></div>
+ <img class="bord"src="menu-icon-bookmark.png" height="32"/>
+ </div>
+ <br />
+ <div class="toolbar big bord">
+ <div class="bord">div</div>
+ </div>
+ <br />
+ <div class="inline bord big bg">
+ <div class="toolbar bord">
+ <div class="bord">div</div>
+ </div>
+ <div class="toolbar bord">
+ <div class="icon"></div>
+ </div>
+ <div class="toolbar bord">
+ <div class="inline">
+ <div class="icon"></div>
+ </div>
+ </div>
+ <div class="toolbar bord">
+ <div class="bord">div</div>
+ <input class="bord" />
+ <div class="inline">
+ <div class="bord">div</div>
+ <input class="bord" />
+ <div class="icon"></div>
+ </div>
+ <button>Button</button>
+ <div class="icon"></div>
+ <img src="menu-icon-bookmark.png" height="32"/>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/html/lib/onyx/design/toolbar.html b/html/lib/onyx/design/toolbar.html
new file mode 100644
index 0000000..495524d
--- /dev/null
+++ b/html/lib/onyx/design/toolbar.html
@@ -0,0 +1,110 @@
+<!doctype html>
+<html>
+<head>
+ <title>inline: onyx toolbar design</title>
+ <link href="../source/css/onyx.css" rel="stylesheet" type="text/css" />
+ <style>
+ /*
+ the 'inline' class sets up a left-to-right container, with vertically centered children
+ */
+ .inline {
+ white-space: nowrap;
+ }
+ .inline > * {
+ display: inline-block;
+ vertical-align: middle;
+ }
+ /**/
+ .toolbar {
+ padding: 8px;
+ border: 1px solid #3A3A3A;
+ background: #4C4C4C url(../images/gradient.png) repeat-x 0 bottom;
+ color: white;
+ }
+ /**/
+ .icon {
+ width: 32px;
+ height: 32px;
+ background-image: url(menu-icon-bookmark.png);
+ }
+ .bord {
+ border: 1px solid lightblue;
+ }
+ .big {
+ font-size: 48px;
+ }
+ .pad {
+ padding: 8px;
+ }
+ .mh {
+ min-height: 124px;
+ }
+ .lh {
+ line-height: 86px;
+ }
+ </style>
+</head>
+<body>
+ <label>"toolbar" tests</label>
+ <br /><br />
+ <div>
+ <label>no content</label>
+ <div class="inline toolbar"></div>
+ <br />
+
+ <label>text only content</label>
+ <div class="inline toolbar">Text</div>
+ <br />
+
+ <label>div content</label>
+ <div class="inline toolbar">
+ <div>Text in Div</div>
+ </div>
+ <br />
+
+ <label>input content</label>
+ <div class="inline toolbar">
+ <input class="" />
+ </div>
+ <br />
+
+ <label>icon content</label>
+ <div class="inline toolbar">
+ <div class="icon"></div>
+ </div>
+ <br />
+
+ <label>combo content</label>
+ <div class="inline toolbar">
+ <div>Text in Div</div>
+ <input class="" />
+ <div class="icon"></div>
+ </div>
+ <br />
+
+ <label>combo content: when the text is larger than non-text objects, vertical centering goes wonky</label>
+ <div class="inline toolbar big">
+ <div>Text in Div</div>
+ <input class="" />
+ <div class="icon"></div>
+ </div>
+ <br />
+
+ <label>controlling height</label>
+ <div class="inline toolbar big mh">
+ <div>Text in Div</div>
+ <input class="" />
+ <div class="icon"></div>
+ </div>
+ <br />
+
+ <label>controlling line-height</label>
+ <div class="inline toolbar big lh pad">
+ <div>Text in Div</div>
+ <input class="" />
+ <div class="icon"></div>
+ </div>
+ <br />
+ </div>
+</body>
+</html>
diff --git a/html/lib/onyx/images/checkbox.png b/html/lib/onyx/images/checkbox.png
new file mode 100644
index 0000000..9176591
--- /dev/null
+++ b/html/lib/onyx/images/checkbox.png
Binary files differ
diff --git a/html/lib/onyx/images/grabbutton.png b/html/lib/onyx/images/grabbutton.png
new file mode 100644
index 0000000..66ee64d
--- /dev/null
+++ b/html/lib/onyx/images/grabbutton.png
Binary files differ
diff --git a/html/lib/onyx/images/gradient-invert.png b/html/lib/onyx/images/gradient-invert.png
new file mode 100644
index 0000000..44ab5ac
--- /dev/null
+++ b/html/lib/onyx/images/gradient-invert.png
Binary files differ
diff --git a/html/lib/onyx/images/gradient.png b/html/lib/onyx/images/gradient.png
new file mode 100644
index 0000000..5856258
--- /dev/null
+++ b/html/lib/onyx/images/gradient.png
Binary files differ
diff --git a/html/lib/onyx/images/more.png b/html/lib/onyx/images/more.png
new file mode 100644
index 0000000..4ad77a3
--- /dev/null
+++ b/html/lib/onyx/images/more.png
Binary files differ
diff --git a/html/lib/onyx/images/progress-button-cancel.png b/html/lib/onyx/images/progress-button-cancel.png
new file mode 100644
index 0000000..2d2306c
--- /dev/null
+++ b/html/lib/onyx/images/progress-button-cancel.png
Binary files differ
diff --git a/html/lib/onyx/images/search-input-cancel.png b/html/lib/onyx/images/search-input-cancel.png
new file mode 100644
index 0000000..8056b4a
--- /dev/null
+++ b/html/lib/onyx/images/search-input-cancel.png
Binary files differ
diff --git a/html/lib/onyx/images/search-input-search.png b/html/lib/onyx/images/search-input-search.png
new file mode 100644
index 0000000..36bf1b5
--- /dev/null
+++ b/html/lib/onyx/images/search-input-search.png
Binary files differ
diff --git a/html/lib/onyx/images/slider-handle.png b/html/lib/onyx/images/slider-handle.png
new file mode 100644
index 0000000..09e12fb
--- /dev/null
+++ b/html/lib/onyx/images/slider-handle.png
Binary files differ
diff --git a/html/lib/onyx/images/spinner-dark.gif b/html/lib/onyx/images/spinner-dark.gif
new file mode 100644
index 0000000..ee78695
--- /dev/null
+++ b/html/lib/onyx/images/spinner-dark.gif
Binary files differ
diff --git a/html/lib/onyx/images/spinner-light.gif b/html/lib/onyx/images/spinner-light.gif
new file mode 100644
index 0000000..ddb7ff1
--- /dev/null
+++ b/html/lib/onyx/images/spinner-light.gif
Binary files differ
diff --git a/html/lib/onyx/package.js b/html/lib/onyx/package.js
new file mode 100644
index 0000000..82636d4
--- /dev/null
+++ b/html/lib/onyx/package.js
@@ -0,0 +1,8 @@
+enyo.depends(
+ // $lib/onxy loads the debug code for now, probably it should load the built code as below
+ "source"
+ /*
+ "build/onyx.css",
+ "build/onyx.js"
+ */
+); \ No newline at end of file
diff --git a/html/lib/onyx/source/Button.js b/html/lib/onyx/source/Button.js
new file mode 100644
index 0000000..b13c334
--- /dev/null
+++ b/html/lib/onyx/source/Button.js
@@ -0,0 +1,18 @@
+/**
+ A button in the onyx style. The color of the button may be customized by
+ applying a background color.
+
+ The *onyx-affirmative*, *onyx-negative*, and *onyx-blue* classes provide
+ some built-in presets.
+
+ {kind: "onyx.Button", content: "Button"},
+ {kind: "onyx.Button", content: "Affirmative", classes: "onyx-affirmative"},
+ {kind: "onyx.Button", content: "Negative", classes: "onyx-negative"},
+ {kind: "onyx.Button", content: "Blue", classes: "onyx-blue"},
+ {kind: "onyx.Button", content: "Custom", style: "background-color: purple; color: #F1F1F1;"}
+*/
+enyo.kind({
+ name: "onyx.Button",
+ kind: "enyo.Button",
+ classes: "onyx-button enyo-unselectable"
+}); \ No newline at end of file
diff --git a/html/lib/onyx/source/Checkbox.js b/html/lib/onyx/source/Checkbox.js
new file mode 100644
index 0000000..2b6143b
--- /dev/null
+++ b/html/lib/onyx/source/Checkbox.js
@@ -0,0 +1,35 @@
+/**
+ A box that shows or hides a check mark when clicked.
+ The onChange event is fired when it is clicked. Use getValue() to fetch
+ the checked status.
+
+ {kind: "onyx.Checkbox", onchange: "checkboxClicked"}
+
+ checkboxClicked: function(inSender) {
+ if (inSender.getValue()) {
+ this.log("I've been checked!");
+ }
+ }
+*/
+enyo.kind({
+ name: "onyx.Checkbox",
+ classes: "onyx-checkbox",
+ //* @protected
+ kind: enyo.Checkbox,
+ tag: "div",
+ handlers: {
+ ondown:"downHandler",
+ // prevent double onchange bubble in IE
+ onclick: ""
+ },
+ downHandler: function(inSender, e) {
+ if (!this.disabled) {
+ this.setChecked(!this.getChecked());
+ this.bubble("onchange");
+ }
+ return true;
+ },
+ tap: function(inSender, e) {
+ return !this.disabled;
+ }
+});
diff --git a/html/lib/onyx/source/Drawer.js b/html/lib/onyx/source/Drawer.js
new file mode 100644
index 0000000..3db5afd
--- /dev/null
+++ b/html/lib/onyx/source/Drawer.js
@@ -0,0 +1,75 @@
+/**
+ A control that appears or disappears based on its _open_ property.
+ It appears or disappears with a sliding animation whose direction is
+ determined by the _orient_ property.
+*/
+enyo.kind({
+ name: "onyx.Drawer",
+ published: {
+ //* The visibility state of the drawer's associated control
+ open: true,
+ //* "v" for vertical animation; "h" for horizontal animation
+ orient: "v"
+ },
+ style: "overflow: hidden; position: relative;",
+ tools: [
+ {kind: "Animator", onStep: "animatorStep", onEnd: "animatorEnd"},
+ {name: "client", style: "position: relative;", classes: "enyo-border-box"}
+ ],
+ create: function() {
+ this.inherited(arguments);
+ this.openChanged();
+ },
+ initComponents: function() {
+ this.createChrome(this.tools);
+ this.inherited(arguments);
+ },
+ openChanged: function() {
+ this.$.client.show();
+ if (this.hasNode()) {
+ if (this.$.animator.isAnimating()) {
+ this.$.animator.reverse();
+ } else {
+ var v = this.orient == "v";
+ var d = v ? "height" : "width";
+ var p = v ? "top" : "left";
+ // unfixing the height/width is needed to properly
+ // measure the scrollHeight/Width DOM property, but
+ // can cause a momentary flash of content on some browsers
+ this.applyStyle(d, null);
+ var s = this.hasNode()[v ? "scrollHeight" : "scrollWidth"];
+ this.$.animator.play({
+ startValue: this.open ? 0 : s,
+ endValue: this.open ? s : 0,
+ dimension: d,
+ position: p
+ });
+ }
+ } else {
+ this.$.client.setShowing(this.open);
+ }
+ },
+ animatorStep: function(inSender) {
+ if (this.hasNode()) {
+ var d = inSender.dimension;
+ this.node.style[d] = this.domStyles[d] = inSender.value + "px";
+ }
+ var cn = this.$.client.hasNode();
+ if (cn) {
+ var p = inSender.position;
+ var o = (this.open ? inSender.endValue : inSender.startValue);
+ cn.style[p] = this.$.client.domStyles[p] = (inSender.value - o) + "px";
+ }
+ if (this.container) {
+ this.container.resized();
+ }
+ },
+ animatorEnd: function() {
+ if (!this.open) {
+ this.$.client.hide();
+ }
+ if (this.container) {
+ this.container.resized();
+ }
+ }
+}); \ No newline at end of file
diff --git a/html/lib/onyx/source/FlyweightPicker.js b/html/lib/onyx/source/FlyweightPicker.js
new file mode 100644
index 0000000..4adfab4
--- /dev/null
+++ b/html/lib/onyx/source/FlyweightPicker.js
@@ -0,0 +1,110 @@
+/**
+ _onyx.FlyweightPicker_, a subkind of <a href="#onyx.Picker">onyx.Picker</a>,
+ is a picker that employs the flyweight pattern. It is used to display a
+ large list of selectable items. As with
+ <a href="#enyo.FlyweightRepeater">enyo.FlyweightRepeater</a>,
+ the _onSetupItem_ event allows for customization of item rendering.
+
+ To initialize the FlyweightPicker to a particular value, call _setSelected_
+ with the index of the item you wish to select, and call _setContent_ with
+ the item that should be shown in the activator button.
+
+ FlyweightPicker will send an _onSelect_ event with a selected item's
+ information. This can be received by a client application to determine which
+ item was selected.
+
+ enyo.kind({
+ handlers: {
+ onSelect: "itemSelected"
+ },
+ components: [
+ {kind: "onyx.PickerDecorator", components: [
+ {},
+ {name: "yearPicker", kind: "onyx.FlyweightPicker", count: 200,
+ onSetupItem: "setupYear", components: [
+ {name: "year"}
+ ]
+ }
+ ]}
+ ],
+ create: function() {
+ var d = new Date();
+ var y = d.getYear();
+ this.$.yearPicker.setSelected(y);
+ this.$.year.setContent(1900+y);
+ },
+ setupYear: function(inSender, inEvent) {
+ this.$.year.setContent(1900+inEvent.index);
+ },
+ itemSelected: function(inSender, inEvent) {
+ enyo.log("Picker Item Selected: " + inEvent.selected.content);
+ }
+ })
+ */
+enyo.kind({
+ name: "onyx.FlyweightPicker",
+ kind: "onyx.Picker",
+ classes: "onyx-flyweight-picker",
+ published: {
+ //* How many rows to render
+ count: 0
+ },
+ events: {
+ //* Sends the row index, and the row control, for decoration.
+ onSetupItem: "",
+ onSelect: ""
+ },
+ handlers: {
+ onSelect: "itemSelect"
+ },
+ components: [
+ {name: "scroller", kind: "enyo.Scroller", strategyKind: "TouchScrollStrategy", components: [
+ {name: "client", kind: "FlyweightRepeater", ontap: "itemTap"}
+ ]}
+ ],
+ scrollerName: "scroller",
+ create: function() {
+ this.inherited(arguments);
+ this.countChanged();
+ },
+ rendered: function() {
+ this.inherited(arguments);
+ this.selectedChanged();
+ },
+ scrollToSelected: function() {
+ var n = this.$.client.fetchRowNode(this.selected);
+ this.getScroller().scrollToNode(n, !this.menuUp);
+ },
+ countChanged: function() {
+ this.$.client.count = this.count;
+ },
+ processActivatedItem: function(inItem) {
+ this.item = inItem;
+ },
+ selectedChanged: function(inOld) {
+ if (!this.item) {
+ return;
+ }
+ if (inOld !== undefined) {
+ this.item.removeClass("selected");
+ this.$.client.renderRow(inOld);
+ }
+ this.item.addClass("selected");
+ this.$.client.renderRow(this.selected);
+ // need to remove the class from control to make sure it won't apply to other rows
+ this.item.removeClass("selected");
+ var n = this.$.client.fetchRowNode(this.selected);
+ this.doChange({selected: this.selected, content: n && n.textContent || this.item.content});
+ },
+ itemTap: function(inSender, inEvent) {
+ this.setSelected(inEvent.rowIndex);
+ //Send the select event that we want the client to receive.
+ this.doSelect({selected: this.item, content: this.item.content})
+ },
+ itemSelect: function(inSender, inEvent) {
+ //Block all select events that aren't coming from this control. This is to prevent select events from MenuItems since they won't have the correct value in a Flyweight context.
+ if (inEvent.originator != this) {
+ return true;
+ }
+ }
+});
diff --git a/html/lib/onyx/source/Grabber.js b/html/lib/onyx/source/Grabber.js
new file mode 100644
index 0000000..0fd74f5
--- /dev/null
+++ b/html/lib/onyx/source/Grabber.js
@@ -0,0 +1,19 @@
+/**
+ A control styled to indicate that an object can be grabbed and moved. It
+ should only be used in this limited context--to indicate that dragging the
+ object will result in movement.
+
+ {kind: "onyx.Toolbar", components: [
+ {kind: "onyx.Grabber", ondragstart: "grabberDragstart",
+ ondrag: "grabberDrag", ondragfinish: "grabberDragFinish"},
+ {kind: "onyx.Button", content: "More stuff"}
+ ]}
+
+ When using a Grabber inside a Fittable control, be sure to set "noStretch: true"
+ on the Fittable or else give it an explicit height. Otherwise, the Grabber
+ may not be visible.
+*/
+enyo.kind({
+ name: "onyx.Grabber",
+ classes: "onyx-grabber"
+}); \ No newline at end of file
diff --git a/html/lib/onyx/source/Groupbox.js b/html/lib/onyx/source/Groupbox.js
new file mode 100644
index 0000000..77a0ada
--- /dev/null
+++ b/html/lib/onyx/source/Groupbox.js
@@ -0,0 +1,37 @@
+/**
+ A Groupbox displays controls as a vertically stacked group.
+
+ A header may be added by placing an <a href="#onyx.GroupboxHeader">onyx.GroupboxHeader</a> as the first control in the Groupbox.
+
+ {kind: "onyx.Groupbox", components: [
+ {kind: "onyx.GroupboxHeader", content: "Sounds"},
+ {components: [
+ {content: "System Sounds"},
+ {kind: "onyx.ToggleButton", value: true}
+ ]},
+ {kind: "onyx.InputDecorator", components: [
+ {kind: "onyx.Input"}
+ ]}
+ ]}
+ ]}
+
+*/
+enyo.kind({
+ name: "onyx.Groupbox",
+ classes: "onyx-groupbox"
+});
+
+/**
+ A GroupboxHeader is designed to be placed inside an <a href="#onyx.Groupbox">onyx.Groupbox</a>. When a header for a group is desired,
+ make a GroupboxHeader the first control inside a Groupbox.
+
+ {kind: "onyx.Groupbox", components: [
+ {kind: "onyx.GroupboxHeader", content: "Sounds"},
+ {content: "Yawn"},
+ {content: "Beep"}
+ ]}
+*/
+enyo.kind({
+ name: "onyx.GroupboxHeader",
+ classes: "onyx-groupbox-header"
+}); \ No newline at end of file
diff --git a/html/lib/onyx/source/Icon.js b/html/lib/onyx/source/Icon.js
new file mode 100644
index 0000000..ea10a99
--- /dev/null
+++ b/html/lib/onyx/source/Icon.js
@@ -0,0 +1,31 @@
+/**
+ A control that displays an icon. The icon image is specified by setting the
+ *src* property to a URL.
+
+ In onyx, icons have a size of 32x32 pixels. Since the icon image is applied
+ as a CSS background, the height and width of an icon must be set if an image
+ of a different size is used.
+
+ {kind: "onyx.Icon", src: "images/search.png"}
+
+ When an icon should act like a button, use an <a href="#onyx.IconButton">onyx.IconButton</a>.
+
+*/
+enyo.kind({
+ name: "onyx.Icon",
+ published: {
+ // url path specifying the icon image
+ src: ""
+ },
+ classes: "onyx-icon",
+ //* @protected
+ create: function() {
+ this.inherited(arguments);
+ if (this.src) {
+ this.srcChanged();
+ }
+ },
+ srcChanged: function() {
+ this.applyStyle("background-image", "url(" + enyo.path.rewrite(this.src) + ")");
+ }
+}); \ No newline at end of file
diff --git a/html/lib/onyx/source/IconButton.js b/html/lib/onyx/source/IconButton.js
new file mode 100644
index 0000000..7d852c5
--- /dev/null
+++ b/html/lib/onyx/source/IconButton.js
@@ -0,0 +1,38 @@
+/**
+ An icon that acts like a button. The icon image is specified by setting the
+ *src* property to a URL.
+
+ {kind: "onyx.IconButton", src: "images/search.png", ontap: "buttonTap"}
+
+ If you want to combine an icon with text inside a button, use an
+ <a href="#onyx.Icon">onyx.Icon</a> inside an
+ <a href="#onyx.Button">onyx.Button</a>, e.g.:
+
+ {kind: "onyx.Button", ontap: "buttonTap", components: [
+ {kind: "onyx.Icon", src: "images/search.png"},
+ {content: "Button"}
+ ]}
+
+ The image associated with the *src* property of the IconButton is assumed
+ to be 32x64-pixel strip with the top half showing the button's normal state
+ and the bottom half showing its state when hovered-over or active.
+*/
+enyo.kind({
+ name: "onyx.IconButton",
+ kind: "onyx.Icon",
+ published: {
+ active: false
+ },
+ classes: "onyx-icon-button",
+ //* @protected
+ rendered: function() {
+ this.inherited(arguments);
+ this.activeChanged();
+ },
+ tap: function() {
+ this.setActive(true);
+ },
+ activeChanged: function() {
+ this.bubble("onActivate");
+ }
+});
diff --git a/html/lib/onyx/source/Input.js b/html/lib/onyx/source/Input.js
new file mode 100644
index 0000000..a1ebe53
--- /dev/null
+++ b/html/lib/onyx/source/Input.js
@@ -0,0 +1,20 @@
+/**
+ An onyx-styled input control. In addition to the features of
+ <a href="#enyo.Input">enyo.Input</a>, onyx.Input has a *defaultFocus*
+ property that can be set to true to focus the input when it's rendered.
+ Only one input should be set as the *defaultFocus*.
+
+ Typically, an onyx.Input is placed inside an
+ <a href="#onyx.InputDecorator">onyx.InputDecorator</a>, which provides
+ styling, e.g.:
+
+ {kind: "onyx.InputDecorator", components: [
+ {kind: "onyx.Input", placeholder: "Enter some text...", onchange: "inputChange"}
+ ]}
+
+*/
+enyo.kind({
+ name: "onyx.Input",
+ kind: "enyo.Input",
+ classes: "onyx-input"
+});
diff --git a/html/lib/onyx/source/InputDecorator.js b/html/lib/onyx/source/InputDecorator.js
new file mode 100644
index 0000000..7dded2d
--- /dev/null
+++ b/html/lib/onyx/source/InputDecorator.js
@@ -0,0 +1,46 @@
+/**
+ _onyx.InputDecorator_ is a control that provides input styling. Any controls
+ in the InputDecorator will appear to be inside an area styled as an input.
+ Usually, an InputDecorator surrounds an <a href="#onyx.Input">onyx.Input</a>.
+
+ {kind: "onyx.InputDecorator", components: [
+ {kind: "onyx.Input"}
+ ]}
+
+ Other controls, such as buttons, may be placed to the right or left of the
+ input control, e.g.:
+
+ {kind: "onyx.InputDecorator", components: [
+ {kind: "onyx.IconButton", src: "search.png"},
+ {kind: "onyx.Input"},
+ {kind: "onyx.IconButton", src: "cancel.png"}
+ ]}
+
+ Note that the InputDecorator fits around the content inside it. If the
+ decorator is sized, then its contents will likely need to be sized as well.
+
+ {kind: "onyx.InputDecorator", style: "width: 500px;", components: [
+ {kind: "onyx.Input", style: "width: 100%;"}
+ ]}
+*/
+enyo.kind({
+ name: "onyx.InputDecorator",
+ kind: "enyo.ToolDecorator",
+ tag: "label",
+ classes: "onyx-input-decorator",
+ //* @protected
+ handlers: {
+ onDisabledChange: "disabledChange",
+ onfocus: "receiveFocus",
+ onblur: "receiveBlur"
+ },
+ receiveFocus: function() {
+ this.addClass("onyx-focused");
+ },
+ receiveBlur: function() {
+ this.removeClass("onyx-focused");
+ },
+ disabledChange: function(inSender, inEvent) {
+ this.addRemoveClass("onyx-disabled", inEvent.originator.disabled);
+ }
+}); \ No newline at end of file
diff --git a/html/lib/onyx/source/Item.js b/html/lib/onyx/source/Item.js
new file mode 100644
index 0000000..377fb5b
--- /dev/null
+++ b/html/lib/onyx/source/Item.js
@@ -0,0 +1,60 @@
+/**
+ A control designed to display a group of stacked items, typically used in
+ lists. Items are displayed with small guide lines between them; by default,
+ they are highlighted when tapped. Set *tapHighlight* to false to prevent the
+ highlighting.
+
+ {kind: "onyx.Item", tapHighlight: false}
+*/
+enyo.kind({
+ name: "onyx.Item",
+ classes: "onyx-item",
+ tapHighlight: true,
+ handlers: {
+ onhold: "hold",
+ onrelease: "release"
+ },
+ //* @public
+ hold: function(inSender, inEvent) {
+ if (this.tapHighlight) {
+ onyx.Item.addFlyweightClass(this.controlParent || this, "onyx-highlight", inEvent);
+ }
+ },
+ //* @public
+ release: function(inSender, inEvent) {
+ if (this.tapHighlight) {
+ onyx.Item.removeFlyweightClass(this.controlParent || this, "onyx-highlight", inEvent);
+ }
+ },
+ //* @protected
+ statics: {
+ addFlyweightClass: function(inControl, inClass, inEvent, inIndex) {
+ var flyweight = inEvent.flyweight;
+ if (flyweight) {
+ var index = inIndex != undefined ? inIndex : inEvent.index;
+ flyweight.performOnRow(index, function() {
+ if (!inControl.hasClass(inClass)) {
+ inControl.addClass(inClass);
+ } else {
+ inControl.setClassAttribute(inControl.getClassAttribute());
+ }
+ });
+ inControl.removeClass(inClass);
+ }
+ },
+ // FIXME: dry
+ removeFlyweightClass: function(inControl, inClass, inEvent, inIndex) {
+ var flyweight = inEvent.flyweight;
+ if (flyweight) {
+ var index = inIndex != undefined ? inIndex : inEvent.index;
+ flyweight.performOnRow(index, function() {
+ if (!inControl.hasClass(inClass)) {
+ inControl.setClassAttribute(inControl.getClassAttribute());
+ } else {
+ inControl.removeClass(inClass);
+ }
+ });
+ }
+ }
+ }
+}); \ No newline at end of file
diff --git a/html/lib/onyx/source/Menu.js b/html/lib/onyx/source/Menu.js
new file mode 100644
index 0000000..53353a0
--- /dev/null
+++ b/html/lib/onyx/source/Menu.js
@@ -0,0 +1,130 @@
+/**
+ _onyx.Menu_ is a subkind of <a href="#onyx.Popup">onyx.Popup</a> that
+ displays a list of <a href="#onyx.MenuItems">onyx.MenuItems</a> and looks
+ like a popup menu. It is meant to be used in conjunction with an
+ <a href="#onyx.MenuDecorator">onyx.MenuDecorator</a>. The decorator couples
+ the menu with an activating control, which may be a button or any other
+ control with an _onActivate_ event. When the control is activated, the menu
+ shows itself in the correct position relative to the activator.
+
+ {kind: "onyx.MenuDecorator", components: [
+ {content: "Show menu"},
+ {kind: "onyx.Menu", components: [
+ {content: "1"},
+ {content: "2"},
+ {classes: "onyx-menu-divider"},
+ {content: "3"},
+ ]}
+ ]}
+
+ A menu may be floated by setting the _floating_ property to true. When a
+ menu is not floating (the default), it will scroll with the activating
+ control, but may be obscured by surrounding content with a higher z-index.
+ When floating, it will never be obscured, but it will not scroll with the
+ activating button.
+ */
+enyo.kind({
+ name: "onyx.Menu",
+ kind: "onyx.Popup",
+ modal: true,
+ defaultKind: "onyx.MenuItem",
+ classes: "onyx-menu",
+ showOnTop: false,
+ handlers: {
+ onActivate: "itemActivated",
+ onRequestShowMenu: "requestMenuShow",
+ onRequestHideMenu: "requestHide"
+ },
+ itemActivated: function(inSender, inEvent) {
+ inEvent.originator.setActive(false);
+ return true;
+ },
+ showingChanged: function() {
+ this.inherited(arguments);
+ this.adjustPosition(true);
+ },
+ requestMenuShow: function(inSender, inEvent) {
+ if (this.floating) {
+ var n = inEvent.activator.hasNode();
+ if (n) {
+ var r = this.activatorOffset = this.getPageOffset(n);
+ this.applyPosition({top: r.top + (this.showOnTop ? 0 : r.height), left: r.left, width: r.width});
+ }
+ }
+ this.show();
+ return true;
+ },
+ applyPosition: function(inRect) {
+ var s = ""
+ for (n in inRect) {
+ s += (n + ":" + inRect[n] + (isNaN(inRect[n]) ? "; " : "px; "));
+ }
+ this.addStyles(s);
+ },
+ getPageOffset: function(inNode) {
+ // getBoundingClientRect returns top/left values which are relative to the viewport and not absolute
+ var r = inNode.getBoundingClientRect();
+
+ // IE8 doesn't return window.page{X/Y}Offset & r.{height/width}
+ // FIXME: Perhaps use an alternate universal method instead of conditionals
+ var pageYOffset = (window.pageYOffset === undefined) ? document.documentElement.scrollTop : window.pageYOffset;
+ var pageXOffset = (window.pageXOffset === undefined) ? document.documentElement.scrollLeft : window.pageXOffset;
+ var rHeight = (r.height === undefined) ? (r.bottom - r.top) : r.height;
+ var rWidth = (r.width === undefined) ? (r.right - r.left) : r.width;
+
+ return {top: r.top + pageYOffset, left: r.left + pageXOffset, height: rHeight, width: rWidth};
+ },
+ //* @protected
+ /* Adjusts the menu position to fit inside the current window size.
+ belowActivator determines whether to position the top of the menu below or on top of the activator
+ */
+ adjustPosition: function(belowActivator) {
+ if (this.showing && this.hasNode()) {
+ this.removeClass("onyx-menu-up");
+
+ //reset the left position before we get the bounding rect for proper horizontal calculation
+ this.floating ? enyo.noop : this.applyPosition({left: "auto"});
+
+ var b = this.node.getBoundingClientRect();
+ var bHeight = (b.height === undefined) ? (b.bottom - b.top) : b.height;
+ var innerHeight = (window.innerHeight === undefined) ? document.documentElement.clientHeight : window.innerHeight;
+ var innerWidth = (window.innerWidth === undefined) ? document.documentElement.clientWidth : window.innerWidth;
+
+ //position the menu above the activator if it's getting cut off, but only if there's more room above
+ this.menuUp = (b.top + bHeight > innerHeight) && ((innerHeight - b.bottom) < (b.top - bHeight));
+ this.addRemoveClass("onyx-menu-up", this.menuUp);
+
+ //if floating, adjust the vertical positioning
+ if (this.floating) {
+ var r = this.activatorOffset;
+ //if the menu doesn't fit below the activator, move it up
+ if (this.menuUp) {
+ this.applyPosition({top: (r.top - bHeight + (this.showOnTop ? r.height : 0)), bottom: "auto"});
+ }
+ else {
+ //if the top of the menu is above the top of the activator and there's room to move it down, do so
+ if ((b.top < r.top) && (r.top + (belowActivator ? r.height : 0) + bHeight < innerHeight))
+ {
+ this.applyPosition({top: r.top + (this.showOnTop ? 0 : r.height), bottom: "auto"});
+ }
+ }
+ }
+
+ //adjust the horizontal positioning to keep the menu from being cut off on the right
+ if ((b.right) > innerWidth) {
+ if (this.floating){
+ this.applyPosition({left: r.left-(b.left + b.width - innerWidth)});
+ } else {
+ this.applyPosition({left: -(b.right - innerWidth)});
+ }
+ }
+ }
+ },
+ resizeHandler: function() {
+ this.inherited(arguments);
+ this.adjustPosition(true);
+ },
+ requestHide: function(){
+ this.setShowing(false);
+ }
+}); \ No newline at end of file
diff --git a/html/lib/onyx/source/MenuDecorator.js b/html/lib/onyx/source/MenuDecorator.js
new file mode 100644
index 0000000..e348821
--- /dev/null
+++ b/html/lib/onyx/source/MenuDecorator.js
@@ -0,0 +1,60 @@
+/**
+ A control that activates an <a href="#onyx.Menu">onyx.Menu</a>. It loosely
+ couples the Menu with an activating control, which may be a button or any
+ other control with an _onActivate_ event. The decorator must surround both
+ the activating control and the menu itself. When the control is activated,
+ the menu shows itself in the correct position relative to the activator.
+
+ {kind: "onyx.MenuDecorator", components: [
+ {content: "Show menu"},
+ {kind: "onyx.Menu", components: [
+ {content: "1"},
+ {content: "2"},
+ {classes: "onyx-menu-divider"},
+ {content: "3"},
+ ]}
+ ]}
+ */
+enyo.kind({
+ name: "onyx.MenuDecorator",
+ kind: "onyx.TooltipDecorator",
+ defaultKind: "onyx.Button",
+ // selection on ios prevents tap events, so avoid.
+ classes: "onyx-popup-decorator enyo-unselectable",
+ handlers: {
+ onActivate: "activated",
+ onHide: "menuHidden"
+ },
+ activated: function(inSender, inEvent) {
+ this.requestHideTooltip();
+ if (inEvent.originator.active) {
+ this.menuActive = true;
+ this.activator = inEvent.originator;
+ this.activator.addClass("active");
+ this.requestShowMenu();
+ }
+ },
+ requestShowMenu: function() {
+ this.waterfallDown("onRequestShowMenu", {activator: this.activator});
+ },
+ requestHideMenu: function() {
+ this.waterfallDown("onRequestHideMenu");
+ },
+ menuHidden: function() {
+ this.menuActive = false;
+ if (this.activator) {
+ this.activator.setActive(false);
+ this.activator.removeClass("active");
+ }
+ },
+ enter: function(inSender) {
+ if (!this.menuActive) {
+ this.inherited(arguments);
+ }
+ },
+ leave: function(inSender, inEvent) {
+ if (!this.menuActive) {
+ this.inherited(arguments);
+ }
+ }
+}); \ No newline at end of file
diff --git a/html/lib/onyx/source/MenuItem.js b/html/lib/onyx/source/MenuItem.js
new file mode 100644
index 0000000..1272292
--- /dev/null
+++ b/html/lib/onyx/source/MenuItem.js
@@ -0,0 +1,41 @@
+/**
+ _MenuItem_ is a button styled to look like a menu item, intended for use in
+ an <a href="#onyx.Menu">onyx.Menu</a>. When the MenuItem is tapped, it
+ tells the menu to hide itself and sends an _onSelect_ event with its
+ content and a reference to itself. This event and its properties may be
+ received by a client application to determine which menu item was selected.
+
+ enyo.kind({
+ handlers: {
+ onSelect: "itemSelected"
+ },
+ components: [
+ {kind: "onyx.MenuDecorator", components: [
+ {content: "Open Menu (floating)"},
+ {kind: "onyx.Menu", floating: true, components: [
+ {content: "1"},
+ {content: "2"},
+ {classes: "onyx-menu-divider"},
+ {content: "3"},
+ ]}
+ ]}
+ ],
+ itemSelected: function(inSender, inEvent) {
+ enyo.log("Menu Item Selected: " + inEvent.originator.content);
+ }
+ })
+ */
+enyo.kind({
+ name: "onyx.MenuItem",
+ kind: "enyo.Button",
+ tag: "div",
+ classes: "onyx-menu-item",
+ events: {
+ onSelect: ""
+ },
+ tap: function(inSender) {
+ this.inherited(arguments);
+ this.bubble("onRequestHideMenu");
+ this.doSelect({selected:this, content:this.content});
+ }
+}); \ No newline at end of file
diff --git a/html/lib/onyx/source/MoreToolbar.js b/html/lib/onyx/source/MoreToolbar.js
new file mode 100644
index 0000000..83afb68
--- /dev/null
+++ b/html/lib/onyx/source/MoreToolbar.js
@@ -0,0 +1,141 @@
+/**
+ onyx.MoreToolbar is a kind of <a href="#onyx.Toolbar">onyx.Toolbar</a> that can adapt to different screen sizes by moving overflowing controls and content into an <a href="#onyx.Menu">onyx.Menu</a>
+
+ {kind: "onyx.MoreToolbar", components: [
+ {content: "More Toolbar", unmoveable: true},
+ {kind: "onyx.Button", content: "Alpha"},
+ {kind: "onyx.Button", content: "Beta"},
+ {kind: "onyx.Button", content: "Gamma", unmoveable: true},
+ {kind: "onyx.Button", content: "Epsilon"}
+ ]},
+
+ A control can be forced to never move to the menu by setting the optional unmovable property to true (default is false).
+
+ Optionally you can specify a class to be applied to the menu via the menuClass property. You can also specify a class for items that have been moved via the the movedClass property.
+*/
+
+enyo.kind({
+ name: "onyx.MoreToolbar",
+ //* @public
+ classes: "onyx-toolbar onyx-more-toolbar",
+ //* style class to be applied to the menu
+ menuClass: "",
+ //* style class to be applied to individual controls moved from the toolbar to the menu
+ movedClass: "",
+ //* @protected
+ layoutKind: "FittableColumnsLayout",
+ noStretch: true,
+ handlers: {
+ onHide: "reflow"
+ },
+ tools: [
+ {name: "client", fit: true, classes: "onyx-toolbar-inline"},
+ {name: "nard", kind: "onyx.MenuDecorator", showing: false, onActivate: "activated", components: [
+ {kind: "onyx.IconButton", classes: "onyx-more-button"},
+ {name: "menu", kind: "onyx.Menu", classes: "onyx-more-menu", prepend: true}
+ ]}
+ ],
+ initComponents: function() {
+ if(this.menuClass && this.menuClass.length>0 && !this.$.menu.hasClass(this.menuClass)) {
+ this.$.menu.addClass(this.menuClass);
+ }
+ this.createChrome(this.tools);
+ this.inherited(arguments);
+ },
+ reflow: function() {
+ this.inherited(arguments);
+ if (this.isContentOverflowing()) {
+ this.$.nard.show();
+ if (this.popItem()) {
+ this.reflow();
+ }
+ } else if (this.tryPushItem()) {
+ this.reflow();
+ } else if (!this.$.menu.children.length) {
+ this.$.nard.hide();
+ this.$.menu.hide();
+ }
+ },
+ activated: function(inSender, inEvent) {
+ this.addRemoveClass("active",inEvent.originator.active);
+ },
+ popItem: function() {
+ var c = this.findCollapsibleItem();
+ if (c) {
+ //apply movedClass is needed
+ if(this.movedClass && this.movedClass.length>0 && !c.hasClass(this.movedClass)) {
+ c.addClass(this.movedClass);
+ }
+ this.$.menu.addChild(c);
+ var p = this.$.menu.hasNode();
+ if (p && c.hasNode()) {
+ c.insertNodeInParent(p);
+ }
+ return true;
+ }
+ },
+ pushItem: function() {
+ var c$ = this.$.menu.children;
+ var c = c$[0];
+ if (c) {
+ //remove any applied movedClass
+ if(this.movedClass && this.movedClass.length>0 && c.hasClass(this.movedClass)) {
+ c.removeClass(this.movedClass);
+ }
+ this.$.client.addChild(c);
+ var p = this.$.client.hasNode();
+ if (p && c.hasNode()) {
+ var nextChild = undefined;
+ var currIndex;
+ for(var i=0; i<this.$.client.children.length; i++) {
+ var curr = this.$.client.children[i];
+ if(curr.toolbarIndex!=undefined && curr.toolbarIndex!=i) {
+ nextChild = curr;
+ currIndex = i;
+ break;
+ }
+ }
+ if(nextChild && nextChild.hasNode()) {
+ c.insertNodeInParent(p, nextChild.node);
+ var newChild = this.$.client.children.pop();
+ this.$.client.children.splice(currIndex, 0, newChild);
+ } else {
+ c.appendNodeToParent(p);
+ }
+ }
+ return true;
+ }
+ },
+ tryPushItem: function() {
+ if (this.pushItem()) {
+ if (!this.isContentOverflowing()) {
+ return true;
+ } else {
+ this.popItem();
+ }
+ }
+ },
+ isContentOverflowing: function() {
+ if (this.$.client.hasNode()) {
+ var c$ = this.$.client.children;
+ var n = c$[c$.length-1].hasNode();
+ if(n) {
+ //Workaround: scrollWidth value not working in Firefox, so manually compute
+ //return (this.$.client.node.scrollWidth > this.$.client.node.clientWidth);
+ return ((n.offsetLeft + n.offsetWidth) > this.$.client.node.clientWidth);
+ }
+ }
+ },
+ findCollapsibleItem: function() {
+ var c$ = this.$.client.children;
+ for (var i=c$.length-1; c=c$[i]; i--) {
+ if (!c.unmoveable) {
+ return c;
+ } else {
+ if(c.toolbarIndex==undefined) {
+ c.toolbarIndex = i;
+ }
+ }
+ }
+ }
+});
diff --git a/html/lib/onyx/source/Picker.js b/html/lib/onyx/source/Picker.js
new file mode 100644
index 0000000..7e758c0
--- /dev/null
+++ b/html/lib/onyx/source/Picker.js
@@ -0,0 +1,87 @@
+/**
+ _onyx.Picker_, a subkind of <a href="#onyx.Menu">onyx.Menu</a>, is used to
+ display a list of items that can be selected. It is meant to be used in
+ conjunction with an <a href="#onyx.PickerDecorator">onyx.PickerDecorator</a>.
+ The decorator loosely couples the Picker with an
+ <a href="#onyx.PickerButton">onyx.PickerButton</a>--a button that, when
+ tapped, shows the picker. Once an item is selected, the list of items closes,
+ but the item stays selected and the PickerButton displays the choice that
+ was made.
+
+ To initialize the Picker to a particular value, set the _active_ property to
+ true for the item that should be selected.
+
+ {kind: "onyx.PickerDecorator", components: [
+ {}, //this uses the defaultKind property of PickerDecorator to inherit from PickerButton
+ {kind: "onyx.Picker", components: [
+ {content: "Gmail", active: true},
+ {content: "Yahoo"},
+ {content: "Outlook"},
+ {content: "Hotmail"}
+ ]}
+ ]}
+
+ Each item in the list is an <a href="#onyx.MenuItem">onyx.MenuItem</a>, so
+ an _onSelect_ event with the item can be listened to by a client application
+ to determine which picker item was selected.
+ */
+enyo.kind({
+ name: "onyx.Picker",
+ kind: "onyx.Menu",
+ classes: "onyx-picker enyo-unselectable",
+ published: {
+ selected: null,
+ maxHeight: "200px"
+ },
+ events: {
+ onChange: ""
+ },
+ components: [
+ {name: "client", kind: "enyo.Scroller", strategyKind: "TouchScrollStrategy"}
+ ],
+ floating: true,
+ showOnTop: true,
+ scrollerName: "client",
+ create: function() {
+ this.inherited(arguments);
+ this.maxHeightChanged();
+ },
+ getScroller: function() {
+ return this.$[this.scrollerName];
+ },
+ maxHeightChanged: function() {
+ this.getScroller().setMaxHeight(this.maxHeight);
+ },
+ showingChanged: function() {
+ this.getScroller().setShowing(this.showing);
+ this.inherited(arguments);
+ if (this.showing && this.selected) {
+ this.scrollToSelected();
+ }
+ },
+ scrollToSelected: function() {
+ this.getScroller().scrollToControl(this.selected, !this.menuUp);
+ },
+ itemActivated: function(inSender, inEvent) {
+ this.processActivatedItem(inEvent.originator)
+ return this.inherited(arguments);
+ },
+ processActivatedItem: function(inItem) {
+ if (inItem.active) {
+ this.setSelected(inItem);
+ }
+ },
+ selectedChanged: function(inOld) {
+ if (inOld) {
+ inOld.removeClass("selected");
+ }
+ if (this.selected) {
+ this.selected.addClass("selected");
+ this.doChange({selected: this.selected, content: this.selected.content});
+ };
+ },
+ resizeHandler: function() {
+ this.inherited(arguments);
+ this.adjustPosition(false);
+ }
+});
diff --git a/html/lib/onyx/source/PickerButton.js b/html/lib/onyx/source/PickerButton.js
new file mode 100644
index 0000000..c5f5730
--- /dev/null
+++ b/html/lib/onyx/source/PickerButton.js
@@ -0,0 +1,16 @@
+/**
+ _onyx.PickerButton_ is a button that, when tapped, shows an
+ <a href="#onyx.Picker">onyx.Picker</a>. Once an item is selected, the list
+ of items closes, but the item stays selected and the PickerButton displays
+ the choice that was made.
+ */
+enyo.kind({
+ name: "onyx.PickerButton",
+ kind: "onyx.Button",
+ handlers: {
+ onChange: "change"
+ },
+ change: function(inSender, inEvent) {
+ this.setContent(inEvent.content);
+ }
+}); \ No newline at end of file
diff --git a/html/lib/onyx/source/PickerDecorator.js b/html/lib/onyx/source/PickerDecorator.js
new file mode 100644
index 0000000..bf16109
--- /dev/null
+++ b/html/lib/onyx/source/PickerDecorator.js
@@ -0,0 +1,30 @@
+/**
+ A control that activates an <a href="#onyx.Picker">onyx.Picker</a>. It
+ loosely couples the Picker with an activating
+ <a href="#onyx.PickerButton">onyx.PickerButton</a>. The decorator must
+ surround both the activating button and the picker itself. When the button
+ is activated, the picker shows itself in the correct position relative to
+ the activator.
+
+ {kind: "onyx.PickerDecorator", components: [
+ {}, //this uses the defaultKind property of PickerDecorator to inherit from PickerButton
+ {kind: "onyx.Picker", components: [
+ {content: "Gmail", active: true},
+ {content: "Yahoo"},
+ {content: "Outlook"},
+ {content: "Hotmail"}
+ ]}
+ ]}
+ */
+enyo.kind({
+ name: "onyx.PickerDecorator",
+ kind: "onyx.MenuDecorator",
+ classes: "onyx-picker-decorator",
+ defaultKind: "onyx.PickerButton",
+ handlers: {
+ onChange: "change"
+ },
+ change: function(inSender, inEvent) {
+ this.waterfallDown("onChange", inEvent);
+ }
+});
diff --git a/html/lib/onyx/source/Popup.js b/html/lib/onyx/source/Popup.js
new file mode 100644
index 0000000..105431a
--- /dev/null
+++ b/html/lib/onyx/source/Popup.js
@@ -0,0 +1,87 @@
+/**
+ An enhanced popup with built-in scrim and z-index handling. To avoid
+ obscuring popup contents, scrims require the dialog to be floating;
+ otherwise, they won't render. Modal popups get a transparent scrim by
+ default, unless the modal popup isn't floating. To get a translucent scrim
+ when modal, specify _scrim: true, scrimWhenModal: false_.
+*/
+enyo.kind({
+ name: "onyx.Popup",
+ kind: "Popup",
+ classes: "onyx-popup",
+ published: {
+ /**
+ Determines whether a scrim will appear when the dialog is modal.
+ Note that modal scrims are transparent, so you won't see them.
+ */
+ scrimWhenModal: true,
+ //* Determines whether or not to display a scrim. Only displays scrims
+ //* when floating.
+ scrim: false,
+ /**
+ Optional class name to apply to the scrim. Be aware that the scrim
+ is a singleton and you will be modifying the scrim instance used for
+ other popups.
+ */
+ scrimClassName: ""
+ },
+ //* @protected
+ statics: { count: 0 },
+ defaultZ: 120,
+ showingChanged: function() {
+ if(this.showing) {
+ onyx.Popup.count++;
+ this.applyZIndex();
+ }
+ else {
+ if(onyx.Popup.count > 0) {
+ onyx.Popup.count--;
+ }
+ }
+ this.showHideScrim(this.showing);
+ this.inherited(arguments);
+ },
+ showHideScrim: function(inShow) {
+ if (this.floating && (this.scrim || (this.modal && this.scrimWhenModal))) {
+ var scrim = this.getScrim();
+ if (inShow) {
+ // move scrim to just under the popup to obscure rest of screen
+ var i = this.getScrimZIndex();
+ this._scrimZ = i;
+ scrim.showAtZIndex(i);
+ } else {
+ scrim.hideAtZIndex(this._scrimZ);
+ }
+ enyo.call(scrim, "addRemoveClass", [this.scrimClassName, scrim.showing]);
+ }
+ },
+ getScrimZIndex: function() {
+ // Position scrim directly below popup
+ return this.findZIndex()-1;
+ },
+ getScrim: function() {
+ // show a transparent scrim for modal popups if scrimWhenModal is true
+ // if scrim is true, then show a regular scrim.
+ if (this.modal && this.scrimWhenModal && !this.scrim) {
+ return onyx.scrimTransparent.make();
+ }
+ return onyx.scrim.make();
+ },
+ applyZIndex: function() {
+ // Adjust the zIndex so that popups will properly stack on each other.
+ this._zIndex = onyx.Popup.count * 2 + this.findZIndex() + 1;
+ // leave room for scrim
+ this.applyStyle("z-index", this._zIndex);
+ },
+ findZIndex: function() {
+ // a default z value
+ var z = this.defaultZ;
+ if (this._zIndex) {
+ z = this._zIndex;
+ } else if (this.hasNode()) {
+ // Re-use existing zIndex if it has one
+ z = Number(enyo.dom.getComputedStyleValue(this.node, "z-index")) || z;
+ }
+ return (this._zIndex = z);
+ }
+}); \ No newline at end of file
diff --git a/html/lib/onyx/source/ProgressBar.js b/html/lib/onyx/source/ProgressBar.js
new file mode 100644
index 0000000..5a41fac
--- /dev/null
+++ b/html/lib/onyx/source/ProgressBar.js
@@ -0,0 +1,92 @@
+/**
+ A control that shows the current progress of a process in a horizontal bar.
+
+ {kind: "onyx.ProgressBar", progress: 10}
+
+ To animate progress changes, call the *animateProgressTo* method:
+
+ this.$.progressBar.animateProgressTo(50);
+
+ You may customize the color of the bar by applying a style via the
+ *barClasses* property, e.g.:
+
+ {kind: "onyx.ProgressBar", barClasses: "onyx-dark"}
+
+ When the *showStripes* property is true (the default), stripes are shown in
+ the progress bar; when *animateStripes* is true (also the default), these
+ stripes are animated. The animated stripes use CSS3 gradients and animation
+ to produce the effects. In browsers that don't support these features, the
+ effects will not be visible.
+*/
+enyo.kind({
+ name: "onyx.ProgressBar",
+ classes: "onyx-progress-bar",
+ published: {
+ progress: 0,
+ min: 0,
+ max: 100,
+ barClasses: "",
+ showStripes: true,
+ animateStripes: true
+ },
+ events: {
+ onAnimateProgressFinish: ""
+ },
+ components: [
+ {name: "progressAnimator", kind: "Animator", onStep: "progressAnimatorStep", onEnd: "progressAnimatorComplete"},
+ {name: "bar", classes: "onyx-progress-bar-bar"}
+ ],
+ //* @protected
+ create: function() {
+ this.inherited(arguments);
+ this.progressChanged();
+ this.barClassesChanged();
+ this.showStripesChanged();
+ this.animateStripesChanged();
+ },
+ barClassesChanged: function(inOld) {
+ this.$.bar.removeClass(inOld);
+ this.$.bar.addClass(this.barClasses);
+ },
+ showStripesChanged: function() {
+ this.$.bar.addRemoveClass("striped", this.showStripes);
+ },
+ animateStripesChanged: function() {
+ this.$.bar.addRemoveClass("animated", this.animateStripes);
+ },
+ progressChanged: function() {
+ this.progress = this.clampValue(this.min, this.max, this.progress);
+ var p = this.calcPercent(this.progress);
+ this.updateBarPosition(p);
+ },
+ clampValue: function(inMin, inMax, inValue) {
+ return Math.max(inMin, Math.min(inValue, inMax));
+ },
+ calcRatio: function(inValue) {
+ return (inValue - this.min) / (this.max - this.min);
+ },
+ calcPercent: function(inValue) {
+ return this.calcRatio(inValue) * 100;
+ },
+ updateBarPosition: function(inPercent) {
+ this.$.bar.applyStyle("width", inPercent + "%");
+ },
+ //* @public
+ //* Animates progress to the given value.
+ animateProgressTo: function(inValue) {
+ this.$.progressAnimator.play({
+ startValue: this.progress,
+ endValue: inValue,
+ node: this.hasNode()
+ });
+ },
+ //* @protected
+ progressAnimatorStep: function(inSender) {
+ this.setProgress(inSender.value);
+ return true;
+ },
+ progressAnimatorComplete: function(inSender) {
+ this.doAnimateProgressFinish(inSender);
+ return true;
+ }
+}); \ No newline at end of file
diff --git a/html/lib/onyx/source/ProgressButton.js b/html/lib/onyx/source/ProgressButton.js
new file mode 100644
index 0000000..05779e0
--- /dev/null
+++ b/html/lib/onyx/source/ProgressButton.js
@@ -0,0 +1,28 @@
+/**
+ A progress bar that can have controls inside of it and has a cancel button on the right.
+
+ {kind: "onyx.ProgressButton"},
+ {kind: "onyx.ProgressButton", barClasses: "onyx-light", progress: 20, components: [
+ {content: "0"},
+ {content: "100", style: "float: right;"}
+ ]}
+
+*/
+enyo.kind({
+ name: "onyx.ProgressButton",
+ kind: "onyx.ProgressBar",
+ classes: "onyx-progress-button",
+ events: {
+ onCancel: ""
+ },
+ components: [
+ {name: "progressAnimator", kind: "Animator", onStep: "progressAnimatorStep", onEnd: "progressAnimatorComplete"},
+ {name: "bar", classes: "onyx-progress-bar-bar onyx-progress-button-bar"},
+ {name: "client", classes: "onyx-progress-button-client"},
+ {kind: "onyx.Icon", src: "$lib/onyx/images/progress-button-cancel.png", classes: "onyx-progress-button-icon", ontap: "cancelTap"}
+ ],
+ //* @protected
+ cancelTap: function() {
+ this.doCancel();
+ }
+});
diff --git a/html/lib/onyx/source/RadioButton.js b/html/lib/onyx/source/RadioButton.js
new file mode 100644
index 0000000..972f23f
--- /dev/null
+++ b/html/lib/onyx/source/RadioButton.js
@@ -0,0 +1,8 @@
+/**
+ A radio button designed for use within an <a href="#onyx.RadioGroup">onyx.RadioGroup</a>.
+*/
+enyo.kind({
+ name: "onyx.RadioButton",
+ kind: "Button",
+ classes: "onyx-radiobutton"
+});
diff --git a/html/lib/onyx/source/RadioGroup.js b/html/lib/onyx/source/RadioGroup.js
new file mode 100644
index 0000000..50f0124
--- /dev/null
+++ b/html/lib/onyx/source/RadioGroup.js
@@ -0,0 +1,18 @@
+/**
+ A group of <a href="#onyx.RadioButton">onyx.RadioButton</a> objects
+ laid out horizontally. Within the same radio group, tapping on one radio button
+ will release any previously tapped radio button.
+
+ {kind: "onyx.RadioGroup", components: [
+ {content: "foo", active: true},
+ {content: "bar"},
+ {content: "baz"}
+ ]}
+*/
+enyo.kind({
+ name: "onyx.RadioGroup",
+ kind: "Group",
+ highlander: true,
+ defaultKind: "onyx.RadioButton"
+});
+
diff --git a/html/lib/onyx/source/RichText.js b/html/lib/onyx/source/RichText.js
new file mode 100644
index 0000000..8a7f388
--- /dev/null
+++ b/html/lib/onyx/source/RichText.js
@@ -0,0 +1,22 @@
+/**
+ An onyx-styled RichText control. In addition to the features of
+ <a href="#enyo.RichText">enyo.RichText</a>, onyx.RichText has a
+ *defaultFocus* property that can be set to true to focus the RichText when
+ it's rendered. Only one RichText should be set as the *defaultFocus*.
+
+ Typically, an onyx.RichText is placed inside an
+ <a href="#onyx.InputDecorator">onyx.InputDecorator</a>, which provides
+ styling, e.g.:
+
+ {kind: "onyx.InputDecorator", components: [
+ {kind: "onyx.RichText", style: "width: 100px;", onchange: "inputChange"}
+ ]}
+
+ Note that RichTexts must be explicitly sized for width. In addition,
+ RichText is not supported on Android < 3.
+*/
+enyo.kind({
+ name: "onyx.RichText",
+ kind: "enyo.RichText",
+ classes: "onyx-richtext"
+});
diff --git a/html/lib/onyx/source/Scrim.js b/html/lib/onyx/source/Scrim.js
new file mode 100644
index 0000000..e2cdb29
--- /dev/null
+++ b/html/lib/onyx/source/Scrim.js
@@ -0,0 +1,93 @@
+enyo.kind({
+ name: "onyx.Scrim",
+ showing: false,
+ classes: "onyx-scrim enyo-fit",
+ floating: false,
+ //* @protected
+ create: function() {
+ this.inherited(arguments);
+ this.zStack = [];
+ if (this.floating) {
+ this.setParent(enyo.floatingLayer);
+ }
+ },
+ showingChanged: function() {
+ // auto render when shown.
+ if (this.floating && this.showing && !this.hasNode()) {
+ this.render();
+ }
+ this.inherited(arguments);
+ //this.addRemoveClass(this.showingClassName, this.showing);
+ },
+ //* @protected
+ addZIndex: function(inZIndex) {
+ if (enyo.indexOf(inZIndex, this.zStack) < 0) {
+ this.zStack.push(inZIndex);
+ }
+ },
+ removeZIndex: function(inControl) {
+ enyo.remove(inControl, this.zStack);
+ },
+ //* @public
+ //* Show Scrim at the specified ZIndex. Note: If you use showAtZIndex you
+ //* must call hideAtZIndex to properly unwind the zIndex stack
+ showAtZIndex: function(inZIndex) {
+ this.addZIndex(inZIndex);
+ if (inZIndex !== undefined) {
+ this.setZIndex(inZIndex);
+ }
+ this.show();
+ },
+ //* Hide Scrim at the specified ZIndex
+ hideAtZIndex: function(inZIndex) {
+ this.removeZIndex(inZIndex);
+ if (!this.zStack.length) {
+ this.hide();
+ } else {
+ var z = this.zStack[this.zStack.length-1];
+ this.setZIndex(z);
+ }
+ },
+ //* @protected
+ // Set scrim to show at `inZIndex`
+ setZIndex: function(inZIndex) {
+ this.zIndex = inZIndex;
+ this.applyStyle("z-index", inZIndex);
+ },
+ make: function() {
+ return this;
+ }
+});
+
+//* @protected
+//
+// Scrim singleton exposing a subset of Scrim API.
+// is replaced with a proper enyo.Scrim instance.
+//
+enyo.kind({
+ name: "onyx.scrimSingleton",
+ kind: null,
+ constructor: function(inName, inProps) {
+ this.instanceName = inName;
+ enyo.setObject(this.instanceName, this);
+ this.props = inProps || {};
+ },
+ make: function() {
+ var s = new onyx.Scrim(this.props);
+ enyo.setObject(this.instanceName, s);
+ return s;
+ },
+ showAtZIndex: function(inZIndex) {
+ var s = this.make();
+ s.showAtZIndex(inZIndex);
+ },
+ // in case somebody does this out of order
+ hideAtZIndex: enyo.nop,
+ show: function() {
+ var s = this.make();
+ s.show();
+ }
+});
+
+new onyx.scrimSingleton("onyx.scrim", {floating: true, classes: "onyx-scrim-translucent"});
+new onyx.scrimSingleton("onyx.scrimTransparent", {floating: true, classes: "onyx-scrim-transparent"});
diff --git a/html/lib/onyx/source/Slider.js b/html/lib/onyx/source/Slider.js
new file mode 100644
index 0000000..037adb0
--- /dev/null
+++ b/html/lib/onyx/source/Slider.js
@@ -0,0 +1,109 @@
+/**
+ A control that presents a range of selection options in the form of a
+ horizontal slider with a control knob. The knob may be tapped and dragged
+ to the desired location.
+
+ {kind: "onyx.Slider", value: 30}
+
+ The *onChanging* event is fired when dragging the control knob.
+ The *onChange* event is fired when the position is set, either by finishing
+ a drag or by tapping the bar.
+*/
+enyo.kind({
+ name: "onyx.Slider",
+ kind: "onyx.ProgressBar",
+ classes: "onyx-slider",
+ published: {
+ value: 0,
+ lockBar: true,
+ tappable: true
+ },
+ events: {
+ onChange: "",
+ onChanging: "",
+ onAnimateFinish: ""
+ },
+ showStripes: false,
+ //* @protected
+ handlers: {
+ ondragstart: "dragstart",
+ ondrag: "drag",
+ ondragfinish: "dragfinish"
+ },
+ moreComponents: [
+ {kind: "Animator", onStep: "animatorStep", onEnd: "animatorComplete"},
+ {classes: "onyx-slider-taparea"},
+ {name: "knob", classes: "onyx-slider-knob"}
+ ],
+ create: function() {
+ this.inherited(arguments);
+ this.createComponents(this.moreComponents);
+ this.valueChanged();
+ },
+ valueChanged: function() {
+ this.value = this.clampValue(this.min, this.max, this.value);
+ var p = this.calcPercent(this.value);
+ this.updateKnobPosition(p);
+ if (this.lockBar) {
+ this.setProgress(this.value);
+ }
+ },
+ updateKnobPosition: function(inPercent) {
+ this.$.knob.applyStyle("left", inPercent + "%");
+ },
+ calcKnobPosition: function(inEvent) {
+ var x = inEvent.clientX - this.hasNode().getBoundingClientRect().left;
+ return (x / this.getBounds().width) * (this.max - this.min) + this.min;
+ },
+ dragstart: function(inSender, inEvent) {
+ if (inEvent.horizontal) {
+ inEvent.preventDefault();
+ this.dragging = true;
+ return true;
+ }
+ },
+ drag: function(inSender, inEvent) {
+ if (this.dragging) {
+ var v = this.calcKnobPosition(inEvent);
+ this.setValue(v);
+ this.doChanging({value: this.value});
+ return true;
+ }
+ },
+ dragfinish: function(inSender, inEvent) {
+ this.dragging = false;
+ inEvent.preventTap();
+ this.doChange({value: this.value});
+ return true;
+ },
+ tap: function(inSender, inEvent) {
+ if (this.tappable) {
+ var v = this.calcKnobPosition(inEvent);
+ this.tapped = true;
+ this.animateTo(v);
+ return true;
+ }
+ },
+ //* @public
+ //* Animates to the given value.
+ animateTo: function(inValue) {
+ this.$.animator.play({
+ startValue: this.value,
+ endValue: inValue,
+ node: this.hasNode()
+ });
+ },
+ //* @protected
+ animatorStep: function(inSender) {
+ this.setValue(inSender.value);
+ return true;
+ },
+ animatorComplete: function(inSender) {
+ if (this.tapped) {
+ this.tapped = false;
+ this.doChange({value: this.value});
+ }
+ this.doAnimateFinish(inSender);
+ return true;
+ }
+}); \ No newline at end of file
diff --git a/html/lib/onyx/source/Spinner.js b/html/lib/onyx/source/Spinner.js
new file mode 100644
index 0000000..3bb923e
--- /dev/null
+++ b/html/lib/onyx/source/Spinner.js
@@ -0,0 +1,31 @@
+/**
+ A control that displays a spinner animation to indicate that activity is
+ taking place. By default, onyx.Spinner will display a light spinner,
+ suitable for displaying against a dark background. To render a dark spinner
+ to be shown on a lighter background, add the "onyx-light" class to the
+ spinner:
+
+ {kind: "onyx.Spinner", classes: "onyx-light"}
+
+ Typically, a spinner is shown to indicate activity and hidden to indicate
+ that the activity has ended. The spinner animation will automatically start
+ when a spinner is shown. If you wish, you may control the animation directly
+ by calling the *start*, *stop*, and *toggle* methods.
+*/
+enyo.kind({
+ name: "onyx.Spinner",
+ classes: "onyx-spinner",
+ //* @public
+ //* Stops the spinner animation.
+ stop: function() {
+ this.setShowing(false);
+ },
+ //* Starts the spinner animation.
+ start: function() {
+ this.setShowing(true);
+ },
+ //* Toggles the spinner animation on or off.
+ toggle: function() {
+ this.setShowing(!this.getShowing());
+ }
+});
diff --git a/html/lib/onyx/source/SwipeableItem.js b/html/lib/onyx/source/SwipeableItem.js
new file mode 100644
index 0000000..efa8811
--- /dev/null
+++ b/html/lib/onyx/source/SwipeableItem.js
@@ -0,0 +1,142 @@
+/**
+ An item that slides to the left to reveal Delete and Cancel buttons. Pressing the Cancel button will slide the item back into place and generate
+ an onCancel event. Pressing the Delete button will immediately position the content back in place and generate an onDelete event.
+
+ A SwipeableItem contains methods for styling its content and these should be used to effect styling on the row content. Add css classes via
+ the contentClasses property and the methods add|remove|has|addRemove<ContentClass>. Alter css styles via the applyContentStyle method.
+
+ {kind: "onyx.SwipeableItem", onCancel: "canceled", onDelete: "deleted"}
+
+*/
+enyo.kind({
+ name: "onyx.SwipeableItem",
+ kind: "onyx.Item",
+ classes: "onyx-swipeable-item",
+ published: {
+ //* Add custom CSS classes via the contentClasses property to style the SwipeableItem's content
+ contentClasses: "",
+ //* Set to true to prevent a drag from bubbling beyond the SwipeableItem.
+ preventDragPropagation: false
+ },
+ defaultContentClasses: "onyx-swipeable-item-content",
+ handlers: {
+ ondown: "down"
+ },
+ events: {
+ /**
+ Fires when a SwipeableItem's delete button has been triggered
+ This event does not fire when programatically deleting a SwipeableItem instance
+ */
+ onDelete: "",
+ /**
+ Fires when a SwipeableItem's cancel button has been triggered
+ This event does not fire when selecting a second SwipeableItem, causing the first to cancel itself programatically
+ */
+ onCancel: ""
+ },
+ components: [
+ {name: "client", kind: "Slideable", min: -100, unit: "%", ondragstart: "clientDragStart"},
+ {name: "confirm", kind: "onyx.Toolbar", canGenerate: false, classes: "onyx-swipeable-item-confirm enyo-fit", style: "text-align: center;", ontap: "confirmTap", components: [
+ {kind: "onyx.Button", content: "Delete", ontap: "deleteTap"},
+ {kind: "onyx.Button", content: "Cancel", ontap: "cancelTap"}
+ ]}
+ ],
+ //* @protected
+ swiping: -1,
+ create: function() {
+ this.inherited(arguments);
+ this.contentClassesChanged();
+ },
+ //* @public
+ //* Resets the sliding position of the SwipeableItem currently displaying confirmation options
+ reset: function() {
+ this.applyStyle("position", null);
+ this.$.confirm.setShowing(false);
+ // stop animating if we reset.
+ this.$.client.getAnimator().stop();
+ this.$.client.setValue(0);
+ },
+ //* @protected
+ contentClassesChanged: function() {
+ this.$.client.setClasses(this.defaultContentClasses + " " + this.contentClasses);
+ },
+ //* @public
+ //* Applies a single style value to the SwipeableItem
+ applyContentStyle: function(inStyle, inValue) {
+ this.$.client.applyStyle(inStyle, inValue);
+ },
+ //* Applies a CSS class to the SwipeableItem's contentClasses
+ addContentClass: function(inClass) {
+ this.$.client.addClass(inClass);
+ },
+ //* Removes a CSS class to the SwipeableItem's contentClasses
+ removeContentClass: function(inClass) {
+ this.$.client.removeClass(inClass);
+ },
+ //* Returns true if the _class_ attribute contains a substring matching _inClass_
+ hasContentClass: function(inClass) {
+ return this.$.client.hasClass(inClass);
+ },
+ /**
+ Adds or removes substring _inClass_ from the _class_ attribute of this object based
+ on the value of _inTrueToAdd_.
+
+ If _inTrueToAdd_ is truthy, then _inClass_ is added; otherwise, _inClass_ is removed.
+ */
+ addRemoveContentClass: function(inClass, inAdd) {
+ this.$.client.addRemoveClass(inClass, inAdd);
+ },
+ //* @protected
+ generateHtml: function() {
+ this.reset();
+ return this.inherited(arguments);
+ },
+ contentChanged: function() {
+ this.$.client.setContent(this.content);
+ },
+ confirmTap: function() {
+ return true;
+ },
+ deleteTap: function(inSender, inEvent) {
+ this.reset();
+ this.doDelete();
+ return true;
+ },
+ cancelTap: function(inSender, inEvent) {
+ this.$.client.animateToMax();
+ this.doCancel();
+ return true;
+ },
+ down: function(inSender, inEvent) {
+ // on down, remove swiping state
+ var last = this.swiping;
+ this.swiping = inEvent.index;
+ var flyweight = inEvent.flyweight;
+ if (this.swiping != last && last >= 0 && flyweight) {
+ flyweight.performOnRow(last, enyo.bind(this, function() {
+ this.reset();
+ }));
+ }
+ },
+ clientDragStart: function(inSender, inEvent) {
+ if (inSender.dragging) {
+ var flyweight = inEvent.flyweight;
+ if (flyweight) {
+ flyweight.prepareRow(inEvent.index);
+ // if needed, render confirm.
+ // NOTE: position relative so can enyo-fit confirm; apply only when confirm needed
+ // because it's a known rendering slowdown.
+ this.applyStyle("position", "relative");
+ this.$.confirm.setShowing(true);
+ if (!this.$.confirm.hasNode()) {
+ // NOTE: prepend so Slideable will be on top.
+ this.$.confirm.prepend = true;
+ this.$.confirm.render();
+ this.$.confirm.prepend = false;
+ }
+ // note: can't teardown.
+ }
+ }
+ return this.preventDragPropagation;
+ }
+}); \ No newline at end of file
diff --git a/html/lib/onyx/source/TabPanels.js b/html/lib/onyx/source/TabPanels.js
new file mode 100644
index 0000000..ae4d6da
--- /dev/null
+++ b/html/lib/onyx/source/TabPanels.js
@@ -0,0 +1,128 @@
+/**
+enyo.TabPanels is a subkind of <a href="#enyo.Panels">enyo.Panels</a> that
+displays a set of tabs, which allow navigation between the individual panels.
+Unlike enyo.Panels, by default, the user cannot drag between the panels of a
+TabPanels. This behavior can be enabled by setting *draggable* to true.
+
+Here's an example:
+
+ enyo.kind({
+ name: "App",
+ kind: "TabPanels",
+ fit: true,
+ components: [
+ {kind: "MyStartPanel"},
+ {kind: "MyMiddlePanel"},
+ {kind: "MyLastPanel"}
+ ]
+ });
+ new App().write();
+*/
+enyo.kind({
+ name: "enyo.TabPanels",
+ kind: "Panels",
+ //* @protected
+ draggable: false,
+ tabTools: [
+ {name: "scroller", kind: "Scroller", maxHeight: "100px", strategyKind: "TranslateScrollStrategy", thumb: false, vertical: "hidden", horizontal: "auto", components: [
+ {name: "tabs", kind: "onyx.RadioGroup", style: "text-align: left; white-space: nowrap", controlClasses: "onyx-tabbutton", onActivate: "tabActivate"}
+ ]},
+ {name: "client", fit: true, kind: "Panels", classes: "enyo-tab-panels", onTransitionStart: "clientTransitionStart"}
+ ],
+ create: function() {
+ this.inherited(arguments);
+ this.$.client.getPanels = enyo.bind(this, "getClientPanels");
+ this.draggableChanged();
+ this.animateChanged();
+ this.wrapChanged();
+ },
+ initComponents: function() {
+ this.createChrome(this.tabTools);
+ this.inherited(arguments);
+ },
+ getClientPanels: function() {
+ return this.getPanels();
+ },
+ flow: function() {
+ this.inherited(arguments);
+ this.$.client.flow();
+ },
+ reflow: function() {
+ this.inherited(arguments);
+ this.$.client.reflow();
+ },
+ draggableChanged: function() {
+ this.$.client.setDraggable(this.draggable);
+ this.draggable = false;
+ },
+ animateChanged: function() {
+ this.$.client.setAnimate(this.animate);
+ this.animate = false;
+ },
+ wrapChanged: function() {
+ this.$.client.setWrap(this.wrap);
+ this.wrap = false;
+ },
+ isPanel: function(inControl) {
+ var n = inControl.name;
+ return !(n == "tabs" || n == "client" || n == "scroller");
+ },
+ addControl: function(inControl) {
+ this.inherited(arguments);
+ if (this.isPanel(inControl)) {
+ var c = inControl.caption || ("Tab " + this.$.tabs.controls.length);
+ var t = inControl._tab = this.$.tabs.createComponent({content: c});
+ if (this.hasNode()) {
+ t.render();
+ }
+ }
+ },
+ removeControl: function(inControl) {
+ if (this.isPanel(inControl) && inControl._tab) {
+ inControl._tab.destroy();
+ }
+ this.inherited(arguments);
+ },
+ layoutKindChanged: function() {
+ if (!this.layout) {
+ this.layout = enyo.createFromKind("FittableRowsLayout", this);
+ }
+ this.$.client.setLayoutKind(this.layoutKind);
+ },
+ indexChanged: function() {
+ // FIXME: initialization order problem
+ if (this.$.client.layout) {
+ this.$.client.setIndex(this.index);
+ }
+ this.index = this.$.client.getIndex();
+ },
+ tabActivate: function(inSender, inEvent) {
+ if (this.hasNode()) {
+ if (inEvent.originator.active) {
+ var i = inEvent.originator.indexInContainer();
+ if (this.getIndex() != i) {
+ this.setIndex(i);
+ }
+ }
+ }
+ },
+ clientTransitionStart: function(inSender, inEvent) {
+ var i = inEvent.toIndex;
+ var t = this.$.tabs.getClientControls()[i];
+ if (t && t.hasNode()) {
+ this.$.tabs.setActive(t);
+ var tn = t.node;
+ var tl = tn.offsetLeft;
+ var tr = tl + tn.offsetWidth;
+ var sb = this.$.scroller.getScrollBounds();
+ if (tr < sb.left || tr > sb.left + sb.clientWidth) {
+ this.$.scroller.scrollToControl(t);
+ }
+ }
+ return true;
+ },
+ startTransition: enyo.nop,
+ finishTransition: enyo.nop,
+ stepTransition: enyo.nop,
+ refresh: enyo.nop
+}); \ No newline at end of file
diff --git a/html/lib/onyx/source/TextArea.js b/html/lib/onyx/source/TextArea.js
new file mode 100644
index 0000000..0a50808
--- /dev/null
+++ b/html/lib/onyx/source/TextArea.js
@@ -0,0 +1,19 @@
+/**
+ An onyx-styled TextArea control. In addition to the features of
+ <a href="#enyo.TextArea">enyo.TextArea</a>, onyx.TextArea has a
+ *defaultFocus* property that can be set to true to focus the TextArea when
+ it's rendered. Only one TextArea should be set as the *defaultFocus*.
+
+ Typically, an onyx.TextArea is placed inside an
+ <a href="#onyx.InputDecorator">onyx.InputDecorator</a>, which provides
+ styling, e.g.:
+
+ {kind: "onyx.InputDecorator", components: [
+ {kind: "onyx.TextArea", onchange: "inputChange"}
+ ]}
+*/
+enyo.kind({
+ name: "onyx.TextArea",
+ kind: "enyo.TextArea",
+ classes: "onyx-textarea"
+});
diff --git a/html/lib/onyx/source/ToggleButton.js b/html/lib/onyx/source/ToggleButton.js
new file mode 100644
index 0000000..cbc5fa3
--- /dev/null
+++ b/html/lib/onyx/source/ToggleButton.js
@@ -0,0 +1,114 @@
+/**
+ A control that looks like a switch with labels for two states. Each time a ToggleButton is tapped,
+ it switches its value and fires an onChange event.
+
+ {kind: "onyx.ToggleButton", onContent: "foo", offContent: "bar", onChange: "buttonToggle"}
+
+ buttonToggle: function(inSender, inEvent) {
+ this.log("Toggled to value " + inEvent.value);
+ }
+
+ To find out the value of the button, use getValue:
+
+ queryToggleValue: function() {
+ return this.$.toggleButton.getValue();
+ }
+
+ The color of the toggle button can be customized by applying a background color:
+
+ {kind: "onyx.ToggleButton", style: "background-color: #35A8EE;"}
+*/
+enyo.kind({
+ name: "onyx.ToggleButton",
+ classes: "onyx-toggle-button",
+ published: {
+ active: false,
+ value: false,
+ onContent: "On",
+ offContent: "Off",
+ disabled: false
+ },
+ events: {
+ /**
+ The onChange event fires when the user changes the value of the toggle button,
+ but not when the value is changed programmatically.
+ */
+ onChange: ""
+ },
+ //* @protected
+ handlers: {
+ ondragstart: "dragstart",
+ ondrag: "drag",
+ ondragfinish: "dragfinish"
+ },
+ components: [
+ {name: "contentOn", classes: "onyx-toggle-content on"},
+ {name: "contentOff", classes: "onyx-toggle-content off"},
+ {classes: "onyx-toggle-button-knob"}
+ ],
+ create: function() {
+ this.inherited(arguments);
+ this.value = Boolean(this.value || this.active);
+ this.onContentChanged();
+ this.offContentChanged();
+ this.disabledChanged();
+ },
+ rendered: function() {
+ this.inherited(arguments);
+ this.valueChanged();
+ },
+ valueChanged: function() {
+ this.addRemoveClass("off", !this.value);
+ this.$.contentOn.setShowing(this.value);
+ this.$.contentOff.setShowing(!this.value);
+ this.setActive(this.value);
+ },
+ activeChanged: function() {
+ this.setValue(this.active);
+ this.bubble("onActivate");
+ },
+ onContentChanged: function() {
+ this.$.contentOn.setContent(this.onContent || "");
+ this.$.contentOn.addRemoveClass("empty", !this.onContent);
+ },
+ offContentChanged: function() {
+ this.$.contentOff.setContent(this.offContent || "");
+ this.$.contentOff.addRemoveClass("empty", !this.onContent);
+ },
+ disabledChanged: function() {
+ this.addRemoveClass("disabled", this.disabled);
+ },
+ updateValue: function(inValue) {
+ if (!this.disabled) {
+ this.setValue(inValue);
+ this.doChange({value: this.value});
+ }
+ },
+ tap: function() {
+ this.updateValue(!this.value);
+ },
+ dragstart: function(inSender, inEvent) {
+ if (inEvent.horizontal) {
+ inEvent.preventDefault();
+ this.dragging = true;
+ this.dragged = false;
+ return true;
+ }
+ },
+ drag: function(inSender, inEvent) {
+ if (this.dragging) {
+ var d = inEvent.dx;
+ if (Math.abs(d) > 10) {
+ this.updateValue(d > 0);
+ this.dragged = true;
+ }
+ return true;
+ }
+ },
+ dragfinish: function(inSender, inEvent) {
+ this.dragging = false;
+ if (this.dragged) {
+ inEvent.preventTap();
+ }
+ }
+})
diff --git a/html/lib/onyx/source/Toolbar.js b/html/lib/onyx/source/Toolbar.js
new file mode 100644
index 0000000..d28e18b
--- /dev/null
+++ b/html/lib/onyx/source/Toolbar.js
@@ -0,0 +1,26 @@
+/**
+ A horizontal bar containing controls used to perform common UI actions.
+
+ A Toolbar customizes the styling of the controls it hosts, including
+ buttons, icons, and inputs.
+
+ {kind: "onyx.Toolbar", components: [
+ {kind: "onyx.Button", content: "Favorites"},
+ {kind: "onyx.InputDecorator", components: [
+ {kind: "onyx.Input", placeholder: "Enter a search term..."}
+ ]},
+ {kind: "onyx.IconButton", src: "go.png"}
+ ]}
+
+ Note that it's possible to style a set of controls to look like they are in
+ a toolbar without having the container itself look like a toolbar. To do so,
+ apply the "onyx-toolbar-inline" CSS class to the container that houses the
+ controls.
+*/
+enyo.kind({
+ name: "onyx.Toolbar",
+ classes: "onyx onyx-toolbar onyx-toolbar-inline",
+ handlers: {
+ onHide: "render"
+ }
+}); \ No newline at end of file
diff --git a/html/lib/onyx/source/Tooltip.js b/html/lib/onyx/source/Tooltip.js
new file mode 100644
index 0000000..9cbe4e4
--- /dev/null
+++ b/html/lib/onyx/source/Tooltip.js
@@ -0,0 +1,80 @@
+/**
+ _onyx.Tooltip_ is a kind of <a href="#onyx.Popup">onyx.Popup</a> that works
+ with an <a href="#onyx.TooltipDecorator">onyx.TooltipDecorator</a>. It
+ automatically displays a tooltip when the user hovers over the decorator.
+ The tooltip is positioned around the decorator where there is available
+ window space.
+
+ {kind: "onyx.TooltipDecorator", components: [
+ {kind: "onyx.Button", content: "Tooltip"},
+ {kind: "onyx.Tooltip", content: "I'm a tooltip for a button."}
+ ]}
+
+ You may manually display the tooltip by calling its _show_ method.
+*/
+enyo.kind({
+ name: "onyx.Tooltip",
+ kind: "onyx.Popup",
+ classes: "onyx-tooltip below left-arrow",
+ autoDismiss: false,
+ showDelay: 500,
+ defaultLeft: -6, //default margin-left value
+ handlers: {
+ onRequestShowTooltip: "requestShow",
+ onRequestHideTooltip: "requestHide"
+ },
+ requestShow: function() {
+ this.showJob = setTimeout(enyo.bind(this, "show"), this.showDelay);
+ return true;
+ },
+ cancelShow: function() {
+ clearTimeout(this.showJob);
+ },
+ requestHide: function() {
+ this.cancelShow();
+ return this.inherited(arguments);
+ },
+ showingChanged: function() {
+ this.cancelShow();
+ this.adjustPosition(true);
+ this.inherited(arguments);
+ },
+ applyPosition: function(inRect) {
+ var s = ""
+ for (n in inRect) {
+ s += (n + ":" + inRect[n] + (isNaN(inRect[n]) ? "; " : "px; "));
+ }
+ this.addStyles(s);
+ },
+ adjustPosition: function(belowActivator) {
+ if (this.showing && this.hasNode()) {
+ var b = this.node.getBoundingClientRect();
+
+ //when the tooltip bottom goes below the window height move it above the decorator
+ if (b.top + b.height > window.innerHeight) {
+ this.addRemoveClass("below", false);
+ this.addRemoveClass("above", true);
+ } else {
+ this.addRemoveClass("above", false);
+ this.addRemoveClass("below", true);
+ }
+
+ //when the tooltip's right edge is out of the window, align it's right edge with the decorator left edge (approx)
+ if (b.left + b.width > window.innerWidth){
+ this.applyPosition({'margin-left': -b.width, bottom: "auto"});
+ //use the right-arrow
+ this.addRemoveClass("left-arrow", false);
+ this.addRemoveClass("right-arrow", true);
+ }
+ }
+ },
+ resizeHandler: function() {
+ //reset the tooltip to align it's left edge with the decorator
+ this.applyPosition({'margin-left': this.defaultLeft, bottom: "auto"});
+ this.addRemoveClass("left-arrow", true);
+ this.addRemoveClass("right-arrow", false);
+
+ this.adjustPosition(true);
+ this.inherited(arguments);
+ }
+});
diff --git a/html/lib/onyx/source/TooltipDecorator.js b/html/lib/onyx/source/TooltipDecorator.js
new file mode 100644
index 0000000..a97167d
--- /dev/null
+++ b/html/lib/onyx/source/TooltipDecorator.js
@@ -0,0 +1,44 @@
+/**
+ A control that activates an <a href="#onyx.Tooltip">onyx.Tooltip</a>. It
+ surrounds a control such as a button and displays the tooltip when the
+ control generates an _onEnter_ event:
+
+ {kind: "onyx.TooltipDecorator", components: [
+ {kind: "onyx.Button", content: "Tooltip"},
+ {kind: "onyx.Tooltip", content: "I'm a tooltip for a button."}
+ ]}
+
+ Here's an example with an <a href="#onyx.Input">onyx.Input</a> control and a
+ decorator around the input:
+
+ {kind: "onyx.TooltipDecorator", components: [
+ {kind: "onyx.InputDecorator", components: [
+ {kind: "onyx.Input", placeholder: "Just an input..."}
+ ]},
+ {kind: "onyx.Tooltip", content: "I'm a tooltip for an input."}
+ ]}
+*/
+enyo.kind({
+ name: "onyx.TooltipDecorator",
+ defaultKind: "onyx.Button",
+ classes: "onyx-popup-decorator",
+ handlers: {
+ onenter: "enter",
+ onleave: "leave"
+ },
+ enter: function() {
+ this.requestShowTooltip();
+ },
+ leave: function() {
+ this.requestHideTooltip();
+ },
+ tap: function() {
+ this.requestHideTooltip();
+ },
+ requestShowTooltip: function() {
+ this.waterfallDown("onRequestShowTooltip");
+ },
+ requestHideTooltip: function() {
+ this.waterfallDown("onRequestHideTooltip");
+ }
+}); \ No newline at end of file
diff --git a/html/lib/onyx/source/css/onyx.css b/html/lib/onyx/source/css/onyx.css
new file mode 100644
index 0000000..4ebde3c
--- /dev/null
+++ b/html/lib/onyx/source/css/onyx.css
@@ -0,0 +1,896 @@
+/* onyx.css - combined CSS file for all released Onyx controls.
+ converted from single files to get around IE bug that allows
+ a maximum of 31 style sheets to be loaded before silently failing */
+
+.onyx {
+ color: #333333;
+ font-family: 'Helvetica Neue', 'Nimbus Sans L', Arial, sans-serif;
+ /*font-family: Prelude, Prelude Medium, Segoe UI, Neue Helvetica, Arial, Helvetica, Sans-Serif;**/
+ font-size: 20px;
+ cursor: default;
+ background-color: #EAEAEA;
+ /* remove automatic tap highlight color */
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+}
+
+/* prevent IE from inheriting line-height for these elements */
+.onyx button, .onyx label, .onyx input {
+ line-height: normal;
+}
+
+.onyx-selected {
+ background-color: #C4E3FE;
+}
+
+/* Icon.css */
+.onyx-icon {
+ width: 32px;
+ height: 32px;
+ background-repeat: no-repeat;
+ display: inline-block;
+ vertical-align: middle;
+}
+
+.onyx-icon.active, .onyx-icon:active:hover {
+ background-position: 0 -32px;
+}
+
+/* Button.css */
+.onyx-button {
+ outline: 0;
+ /**/
+ color: #292929;
+ font-size: 16px;
+ text-align: center;
+ white-space: nowrap;
+ /**/
+ margin: 0;
+ padding: 6px 18px;
+ overflow: hidden;
+ /**/
+ border-radius: 3px;
+ /* for IE8 */
+ border: 1px solid #777;
+ border: 1px solid rgba(15, 15, 15, 0.2);
+ /*
+ The border and the gradient interact in a strange way that
+ causes the bottom-border (top if the gradient is aligned top)
+ to be lighter than other borders.
+ We can fix it by using the darker bottom border below, but
+ then there are a few rogue pixels that end up very dark.
+ */
+ /* border-bottom: 1px solid rgba(15, 15, 15, 0.5); */
+ box-shadow: inset 0px 1px 0px rgba(255,255,255,0.2);
+ /*
+ box-shadow: 0px 1px 0px rgba(164,164,164,0.1), inset 0px 1px 1px rgba(164,164,164,0.35);
+ text-shadow: 0 -1px 1px rgba(0,0,0,0.2);
+ background-color: #E1E1E1;
+ */
+ /**/
+ background: #E1E1E1 url(../../images/gradient.png) repeat-x bottom;
+ background-size: contain;
+ /**/
+ text-overflow: ellipsis;
+ /* the following cause arcane problems on IE */
+ /*
+ min-width: 14px;
+ min-height: 20px;
+ */
+}
+
+/*
+ IE8 can't handle these selectors in tandem:
+ .onyx-button.active, .onyx-button:active:not([disabled]) {
+
+ the effect is as if .onyx-button.active doesn't exist
+*/
+.onyx-button.active {
+ background-image: url(../../images/gradient-invert.png);
+ background-position: top;
+ border-top: 1px solid rgba(15, 15, 15, 0.6);
+ box-shadow: inset 0px 1px 0px rgba(0,0,0,0.1);
+}
+
+.onyx-button:active:hover:not([disabled]) {
+ background-image: url(../../images/gradient-invert.png);
+ background-position: top;
+ border-top: 1px solid rgba(15, 15, 15, 0.6);
+ box-shadow: inset 0px 1px 0px rgba(0,0,0,0.1);
+}
+
+.onyx-button[disabled] {
+ opacity: 0.4;
+}
+
+.onyx-button > img {
+ padding: 0px 3px;
+}
+
+/* optional */
+
+button.onyx-button.onyx-affirmative {
+ background-color: #91BA07;
+ color: #F1F1F1;
+}
+
+button.onyx-button.onyx-negative {
+ background-color: #C51616;
+ color: #F1F1F1;
+}
+
+button.onyx-button.onyx-blue {
+ background-color: #35A8EE;
+ color: #F1F1F1;
+}
+
+/* Checkbox.css */
+.onyx-checkbox {
+ cursor: pointer;
+ height: 32px;
+ width: 32px;
+ background: url(../../images/checkbox.png) no-repeat;
+ /* reset for ? */
+ margin: 0px;
+ /* these entries cause toggle-button and checkbox to line up properly*/
+ display: inline-block;
+ vertical-align: middle;
+}
+
+.onyx-checkbox[checked] {
+ background-position: 0px -32px;
+}
+
+.onyx-checkbox[disabled] {
+ opacity: 0.4;
+}
+
+/* Grabber.css */
+.onyx-grabber {
+ background: url(../../images/grabbutton.png) no-repeat center;
+ width: 23px;
+ height: 27px;
+}
+
+/* Popup.css */
+.onyx-popup {
+ font-size: 16px;
+ box-shadow: 0 6px 10px rgba(0, 0, 0, 0.2);
+ border: 1px solid rgba(0,0,0,0.2);
+ border-radius: 8px;
+ padding: 6px;
+ color: white;
+ background: #4C4C4C url(../../images/gradient.png) repeat-x 0 bottom;
+}
+
+.onyx-popup-decorator {
+ position: relative;
+}
+
+/* Groupbox.css */
+.onyx-groupbox {
+}
+
+.onyx-groupbox > * {
+ display: block;
+ /*box-shadow: inset 0px 1px 1px rgba(255,255,255,0.5);*/
+ border-color: #aaaaaa;
+ border-style: solid;
+ border-width: 0 1px 1px 1px;
+ /*padding: 10px;*/
+ /* reset styles that make 'item' look bad if they happen to be there */
+ border-radius: 0;
+ margin: 0;
+}
+
+.onyx-groupbox > *:first-child {
+ border-top-color: #aaaaaa;
+ border-width: 1px;
+ border-radius: 4px 4px 0 0;
+}
+
+.onyx-groupbox > *:last-child {
+ border-radius: 0 0 4px 4px;
+}
+
+.onyx-groupbox > *:first-child:last-child {
+ border-radius: 4px;
+}
+
+.onyx-groupbox-header {
+ padding: 2px 10px;
+ /**/
+ color: white;
+ font-family: Segoe UI, Neue Helvetica, Helvectica, Arial, sans-serif;
+ font-size: 14px;
+ font-weight: bold;
+ text-transform: uppercase;
+ /**/
+ background-color: #343434;
+ border: none;
+ background: #4c4c4c url(../../images/gradient.png) repeat-x 0 10px;
+}
+
+.onyx-groupbox .onyx-input-decorator {
+ display: block;
+}
+
+.onyx-groupbox > .onyx-input-decorator {
+ border-color: #aaaaaa;
+ border-width: 0 1px 1px 1px;
+ border-radius: 0;
+}
+
+.onyx-groupbox > .onyx-input-decorator:first-child {
+ border-width: 1px;
+ border-radius: 4px 4px 0 0;
+}
+
+.onyx-groupbox > .onyx-input-decorator:last-child {
+ border-radius: 0 0 4px 4px;
+}
+
+.onyx-groupbox > .onyx-input-decorator:first-child:last-child {
+ border-radius: 4px;
+}
+
+/* Input.css */
+.onyx-input-decorator {
+ padding: 6px 8px 10px 8px;
+ border-radius: 3px;
+ border: 1px solid;
+ border-color: rgba(0,0,0,0.1);
+ margin: 0;
+}
+
+.onyx-input-decorator.onyx-focused {
+ box-shadow: inset 0px 1px 4px rgba(0,0,0,0.3);
+ border-color: rgba(0,0,0,0.3);
+ background-color: white;
+}
+
+.onyx-input-decorator.onyx-disabled {
+ /* FIXME: needed to color a disabled input placeholder. */
+ /*-webkit-text-fill-color: #888;*/
+ opacity: 0.4;
+}
+
+.onyx-input-decorator > input {
+ /* reset */
+ margin: 0;
+ padding: 0;
+ border: none;
+ outline: none;
+ cursor: pointer;
+ background-color: transparent;
+ font-size: 16px;
+ box-shadow: none;
+ /* FIXME: hack for styling reset on Android */
+ /* -webkit-appearance: caret;*/
+}
+
+.onyx-input-decorator.onyx-focused > input {
+ cursor: text;
+}
+
+.onyx-input-decorator.onyx-disabled > input {
+ cursor: default;
+}
+
+/* Menu.css */
+.onyx-menu, .onyx.onyx-menu {
+ min-width: 160px;
+ top: 100%;
+ left: 0;
+ margin-top: 2px;
+ padding: 3px 0;
+ border-radius: 3px;
+}
+
+.onyx-menu.onyx-menu-up {
+ top: auto;
+ bottom: 100%;
+ margin-top: 0;
+ margin-bottom: 2px;
+}
+
+.onyx-toolbar .onyx-menu {
+ margin-top: 11px;
+ border-radius: 0 0 3px 3px;
+}
+
+
+.onyx-toolbar .onyx-menu.onyx-menu-up {
+ margin-top: 0;
+ margin-bottom: 10px;
+ border-radius: 3px 3px 0 0;
+}
+
+.onyx-menu-item {
+ display: block;
+ padding: 10px;
+}
+
+.onyx-menu-item:hover {
+ background: #284152;
+}
+
+.onyx-menu-divider, .onyx-menu-divider:hover {
+ margin: 6px 0;
+ padding: 0;
+ border-bottom: 1px solid #aaa;
+}
+
+/* customize a toolbar to support menus */
+.onyx-menu-toolbar, .onyx-toolbar.onyx-menu-toolbar {
+ z-index: 10;
+ overflow: visible;
+ position: relative;
+}
+
+/* Picker.css */
+.onyx-picker-decorator .onyx-button {
+ padding: 10px 18px;
+}
+
+.onyx-picker {
+ top: 0;
+ margin-top: -3px;
+ min-width: 0;
+ width: 100%;
+ box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ color: black;
+ background: #E1E1E1;
+}
+
+.onyx-picker.onyx-menu-up {
+ top: auto;
+ bottom: 0;
+ margin-top: 3px;
+ margin-bottom: -3px;
+}
+
+.onyx-picker .onyx-menu-item {
+ text-align: center;
+}
+
+.onyx-picker .onyx-menu-item:hover {
+ background-color: transparent;
+}
+
+.onyx-picker .onyx-menu-item.selected, .onyx-picker .onyx-menu-item.active, .onyx-picker .onyx-menu-item:active:hover {
+ border-top: 1px solid rgba(0, 0, 0, 0.1);
+ background-color: #cde7fe;
+ box-shadow: inset 0px 0px 3px rgba(0, 0, 0, 0.2);
+}
+
+.onyx-picker .onyx-menu-item {
+ border-top: 1px solid rgba(255,255,255,0.5);
+ border-bottom: 1px solid rgba(0,0,0,0.2);
+}
+
+.onyx-picker:not(.onyx-flyweight-picker) .onyx-menu-item:first-child, .onyx-flyweight-picker :first-child > .onyx-menu-item {
+ border-top: none;
+}
+
+.onyx-picker:not(.onyx-flyweight-picker) .onyx-menu-item:last-child, .onyx-flyweight-picker :last-child > .onyx-menu-item {
+ border-bottom: none;
+}
+
+/* TextArea.css */
+.onyx-input-decorator > textarea {
+ /* reset */
+ margin: 0;
+ padding: 0;
+ border: none;
+ outline: none;
+ cursor: pointer;
+ background-color: transparent;
+ font-size: 16px;
+ box-shadow: none;
+ /* Remove scrollbars and resize handle */
+ resize: none;
+ overflow: auto;
+ /* FIXME: hack for styling reset on Android */
+ /* -webkit-appearance: caret;*/
+}
+
+.onyx-input-decorator.onyx-focused > textarea {
+ cursor: text;
+}
+
+.onyx-input-decorator.onyx-disabled > textarea {
+ cursor: default;
+}
+
+.onyx-textarea {
+ /* need >=50px for scrollbar to be usable on mac */
+ min-height: 50px;
+}
+
+/* RichText.css */
+.onyx-input-decorator > .onyx-richtext {
+ /* reset */
+ margin: 0;
+ padding: 0;
+ border: none;
+ outline: none;
+ cursor: pointer;
+ background-color: transparent;
+ font-size: 16px;
+ min-height: 20px;
+ min-width: 100px;
+ box-shadow: none;
+ /* FIXME: hack for styling reset on Android */
+ /* -webkit-appearance: caret;*/
+}
+
+.onyx-input-decorator.onyx-focused > .onyx-richtext {
+ cursor: text;
+}
+
+.onyx-input-decorator.onyx-disabled > .onyx-richtext {
+ cursor: default;
+}
+
+/* RadioButton.css */
+.onyx-radiobutton {
+ padding: 8px 12px;
+ margin: 0;
+ outline: 0;
+ /**/
+ font-size: 16px;
+ text-shadow: 0 1px 1px rgba(0,0,0,0.2);
+ text-align: center;
+ /**/
+ background: #E7E7E7 url(../../images/gradient.png) repeat-x bottom;
+ /* IE8 */
+ border: 1px solid #333;
+ border: 1px solid rgba(15, 15, 15, 0.2);
+ /* turn off right-border in a way IE8 ignores, because IE8 does not support :last-child */
+ border-right-color: rgba(0,0,0,0);
+ box-shadow: inset 1px 1px 0px rgba(255, 255, 255, 0.2);
+}
+
+.onyx-radiobutton:first-child {
+ border-radius: 3px 0 0 3px;
+}
+
+.onyx-radiobutton:last-child {
+ border-radius: 0px 3px 3px 0px;
+ /* IE8 */
+ border-right: 1px solid #333;
+ border-right: 1px solid rgba(15, 15, 15, 0.2);
+}
+
+.onyx-radiobutton.active {
+ color: White;
+ background: #0091F2 url(../../images/gradient-invert.png) repeat-x top;
+ border-top: 1px solid rgba(15, 15, 15, 0.6);
+ box-shadow: inset 1px 2px 2px rgba(0, 0, 0, 0.2);
+}
+
+/* Scrim.css */
+.onyx-scrim {
+ z-index: 1;
+ /*
+ note: by using pointer-events we allow tapping on scrim
+ while it is fading out; however, this requires any showing classes
+ to set pointer events to auto or scrim will not function as expected.
+ */
+ pointer-events: none;
+}
+
+.onyx-scrim.onyx-scrim-translucent{
+ pointer-events: auto;
+ background-color: rgb(0,0,0);
+ opacity: 0.65;
+ filter: alpha(opacity=65);
+}
+
+.onyx-scrim.onyx-scrim-transparent {
+ pointer-events: auto;
+ background: transparent;
+}
+
+/* TabButton.css */
+.onyx-radiobutton.onyx-tabbutton {
+ padding: 8px 34px;
+ font-size: 20px;
+ border-radius: 0px;
+}
+
+/* ToggleButton.css */
+.onyx-toggle-button {
+ display: inline-block;
+ height: 32px;
+ line-height: 32px;
+ min-width: 64px;
+ vertical-align: middle;
+ text-align: center;
+ /* */
+ border-radius: 3px;
+ box-shadow: inset 0px 1px 3px rgba(0,0,0,0.4);
+ background: #8BBA3D url(../../images/gradient-invert.png) repeat-x bottom;
+ background-size: auto 100%;
+ /* label */
+ color: white;
+ font-size: 14px;
+ font-weight: bold;
+ text-transform: uppercase;
+}
+
+.onyx-toggle-button.off {
+ background-color: #B1B1B1 !important;
+}
+
+.onyx-toggle-button-knob {
+ display: inline-block;
+ width: 30px;
+ height: 30px;
+ margin: 1px;
+ border-radius: 3px;
+ background: #F6F6F6 url(../../images/gradient.png) repeat-x;
+ background-size: auto 100%;
+}
+
+.onyx-toggle-button .onyx-toggle-button-knob {
+ box-shadow: -1px 0px 4px rgba(0,0,0,0.35), inset 0 -1px 0 rgba(0,0,0,0.4);
+ float: right;
+}
+
+.onyx-toggle-button.off .onyx-toggle-button-knob {
+ box-shadow: 1px 0px 4px rgba(0,0,0,0.35), inset 0 -1px 0 rgba(0,0,0,0.4);
+ float: left;
+}
+
+.onyx-toggle-button.disabled {
+ opacity: 0.4;
+}
+
+.onyx-toggle-content {
+ min-width: 32px;
+ padding: 0 6px;
+}
+
+.onyx-toggle-content.empty {
+ padding: 0;
+}
+
+.onyx-toggle-content.off {
+ float: right;
+}
+
+.onyx-toggle-content.on {
+ float: left;
+}
+
+/* Toolbar.css */
+.onyx-toolbar {
+ /*
+ line-height is unreliable for centering, instead
+ use vertical-align: middle to align
+ elements along a common centerline and use
+ padding to fill out the space.
+ */
+ padding: 9px 8px 10px 8px;
+ /**/
+ border: 1px solid #3A3A3A;
+ background: #4C4C4C url(../../images/gradient.png) repeat-x 0 bottom;
+ color: white;
+ /**/
+ white-space: nowrap;
+ overflow: hidden;
+}
+
+.onyx-toolbar-inline > * {
+ display: inline-block;
+ vertical-align: middle;
+ margin: 4px 6px 5px;
+ box-sizing: border-box;
+}
+
+.onyx-toolbar .onyx-icon-button {
+ margin: 3px 2px 1px;
+}
+
+.onyx-toolbar .onyx-button {
+ color: #F2F2F2;
+ background-color: #555656;
+ border-color: rgba(15, 15, 15, 0.5);
+ margin-top: 0;
+ margin-bottom: 0;
+ height: 36px;
+}
+
+.onyx-toolbar .onyx-input-decorator {
+ margin: 1px 3px;
+ box-shadow: inset 0px 1px 4px rgba(0,0,0,0.1);
+ background-color: rgba(0, 0, 0, 0.1);
+ padding: 0px 6px 5px 6px;
+}
+
+.onyx-toolbar .onyx-input-decorator.onyx-focused {
+ box-shadow: inset 0px 1px 4px rgba(0,0,0,0.3);
+ background-color: white;
+}
+
+.onyx-toolbar .onyx-input-decorator .onyx-input {
+ color: #e5e5e5;
+ font-size: 14px;
+}
+
+.onyx-toolbar .onyx-input-decorator .onyx-input:focus {
+ color: #000;
+}
+
+.onyx-toolbar .onyx-input-decorator .onyx-input:focus::-webkit-input-placeholder {
+ color: #ddd;
+}
+
+/* Tooltip.css */
+.onyx-tooltip {
+ z-index: 20;
+ left: 0;
+ padding: 4px 6px;
+ margin-top: 4px;
+ margin-left: -6px;
+ box-shadow: 0 6px 10px rgba(0, 0, 0, 0.2);
+ border: 1px solid rgba(0,0,0,0.2);
+ color: white;
+ background: #216593 url(../../images/gradient.png) repeat-x 0 bottom;
+ border-radius: 3px;
+ white-space: nowrap;
+}
+
+/*move the tooltip over to the right when displaying the right arrow so it aligns better with the decorator*/
+.onyx-tooltip.right-arrow {
+ left: 30px;
+}
+
+/*prep the left & right arrows using before & after - left arrow uses before & right arrow uses after*/
+.onyx-tooltip::before {
+ content: '';
+ border-left: 6px solid transparent;
+ border-right: 6px solid transparent;
+ position: absolute;
+ top: -6px;
+ left: 16px;
+}
+
+.onyx-tooltip::after {
+ content: '';
+ border-left: 6px solid transparent;
+ border-right: 6px solid transparent;
+ position: absolute;
+ top: -6px;
+ margin-left: -12px;
+}
+
+/*The following 3 rules handle the left & right arrows when the tooltip is displayed below the activator*/
+.onyx-tooltip.below {
+ top: 100%;
+}
+
+.onyx-tooltip.below.right-arrow::after {
+ border-bottom: 6px solid #1D587F;
+ top: -6px;
+}
+
+.onyx-tooltip.below.left-arrow::before {
+ border-bottom: 6px solid #1D587F;
+ top: -6px;
+}
+
+/*The following 3 rules handle the left & right arrows when the tooltip is displayed above the activator*/
+.onyx-tooltip.above {
+ top: -100%;
+}
+
+.onyx-tooltip.above.right-arrow::after {
+ content: '';
+ border-top: 6px solid #1D587F;
+ top: 100%;
+}
+
+.onyx-tooltip.above.left-arrow::before {
+ content: '';
+ border-top: 6px solid #1D587F;
+ top: 100%;
+}
+
+/* ProgressBar.css */
+.onyx-progress-bar {
+ margin: 8px;
+ height: 8px;
+ border: 1px solid rgba(15, 15, 15, 0.2);
+ border-radius: 3px;
+ background: #B8B8B8 url(../../images/gradient-invert.png) repeat-x;
+ background-size: auto 100%;
+}
+
+.onyx-progress-bar-bar {
+ height: 100%;
+ border-radius: 3px;
+ background: #58ABEF url(../../images/gradient.png) repeat-x;
+ background-size: auto 100%;
+}
+
+.onyx-progress-bar-bar.striped {
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-size: 40px 40px;
+}
+
+.onyx-progress-bar-bar.striped.animated {
+ -webkit-animation: progress-bar-stripes 2s linear infinite;
+ -moz-animation: progress-bar-stripes 2s linear infinite;
+ animation: progress-bar-stripes 2s linear infinite;
+}
+
+@-webkit-keyframes progress-bar-stripes {
+ from {
+ background-position: 0 0;
+ }
+ to {
+ background-position: 40px 0;
+ }
+}
+
+@-moz-keyframes progress-bar-stripes {
+ from {
+ background-position: 0 0;
+ }
+ to {
+ background-position: 40px 0;
+ }
+}
+
+@keyframes progress-bar-stripes {
+ from {
+ background-position: 0 0;
+ }
+ to {
+ background-position: 40px 0;
+ }
+}
+
+/* optional */
+
+.onyx-dark {
+ background-color: #626368;
+}
+
+.onyx-light {
+ background-color: #cacaca;
+}
+
+/* ProgressButton.css */
+.onyx-progress-button {
+ position: relative;
+ height: 36px;
+ line-height: 36px;
+ color: #F1F1F1;
+ font-size: 16px;
+ text-overflow: ellipsis;
+}
+
+.onyx-progress-button-bar {
+ height: 36px;
+}
+
+.onyx-progress-button-icon {
+ display: inline-block;
+ position: absolute;
+ top: 2px;
+ right: 0;
+}
+
+.onyx-progress-button-client {
+ display: inline-block;
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 36px;
+ margin-left: 8px;
+}
+
+.onyx-progress-button-client > * {
+ display: inline-block;
+}
+
+/* Slider.css */
+.onyx-slider {
+ position: relative;
+ margin: 8px 20px;
+}
+
+.onyx-slider-taparea {
+ position: absolute;
+ top: -11px;
+ height: 28px;
+ width: 100%;
+}
+
+.onyx-slider-knob {
+ position: relative;
+ height: 40px;
+ width: 40px;
+ background: url(../../images/slider-handle.png) left top no-repeat;
+ margin: -23px -20px;
+}
+
+.onyx-slider-knob.active, .onyx-slider-knob:active:hover {
+ background-position: 0 -40px;
+}
+
+/* Item.css */
+.onyx-item {
+ padding: 14px;
+}
+
+.onyx-highlight, .onyx-highlight.onyx-swipeable-item-content {
+ background-color: #DEDFDF;
+}
+
+.enyo-selected, .enyo-selected.onyx-swipeable-item-content {
+ background-color: #C4E3FE;
+}
+
+.onyx-item.onyx-swipeable-item {
+ overflow: hidden;
+ padding: 0;
+}
+
+.onyx-swipeable-item-content {
+ background-color: #EAEAEA;
+ box-sizing: border-box;
+ padding: 18px 6px;
+ min-height: 40px;
+}
+
+/* Spinner.css */
+.onyx-spinner {
+ width: 58px;
+ height: 59px;
+ display: inline-block;
+ background: url(../../images/spinner-dark.gif) no-repeat 0 0;
+}
+
+.onyx-spinner.onyx-light {
+ background: url(../../images/spinner-light.gif) no-repeat 0 0;
+}
+
+/* MoreToolbar.css */
+.onyx-more-toolbar {
+ overflow: visible;
+ position: relative;
+ z-index: 10;
+}
+
+.onyx-more-toolbar.active {
+ z-index:11;
+}
+
+.onyx-more-menu {
+ left: auto;
+ right: 0px;
+ min-width: 0;
+}
+
+.onyx-more-toolbar .onyx-more-menu > * {
+ float: right;
+ clear: both;
+ margin: 5px;
+ margin-top: 5px;
+ margin-bottom: 5px;
+}
+
+.onyx-more-button {
+ background-image: url(../../images/more.png);
+ margin: 4px 6px 5px;
+}
diff --git a/html/lib/onyx/source/css/package.js b/html/lib/onyx/source/css/package.js
new file mode 100644
index 0000000..a0e03d4
--- /dev/null
+++ b/html/lib/onyx/source/css/package.js
@@ -0,0 +1,3 @@
+enyo.depends(
+ "onyx.css"
+);
diff --git a/html/lib/onyx/source/design.js b/html/lib/onyx/source/design.js
new file mode 100644
index 0000000..430dc70
--- /dev/null
+++ b/html/lib/onyx/source/design.js
@@ -0,0 +1,66 @@
+//* @protected
+// This is an in-progress design description of the Onyx controls for use in the Ares designer tool.
+Palette.model.push(
+ {name: "onyx", items: [
+ {name: "onyx.Button", title: "Foofoofoo", icon: "box_software.png", stars: 4, version: 2.0, blurb: "Use as a simple page header, or add an optional navigation menu.",
+ inline: {content: "onyx.Button", kind: "onyx.Button"},
+ config: {content: "$name", isContainer: true, kind: "onyx.Button"}
+ },
+ {name: "onyx.InputDecorator", title: "Foofoofoo", icon: "box_software.png", stars: 4, version: 2.0, blurb: "Use as a simple page header, or add an optional navigation menu.",
+ inline: {kind: "onyx.InputDecorator", components: [
+ {kind: "Input", placeholder: "Enter text here"}
+ ]},
+ config: {kind: "onyx.InputDecorator", components: [
+ {kind: "Input", placeholder: "Enter text here"}
+ ]}
+ },
+ {name: "onyx.Input", title: "Foofoofoo", icon: "box_software.png", stars: 4, version: 2.0, blurb: "Use as a simple page header, or add an optional navigation menu.",
+ inline: {value: "onyx.Input", kind: "onyx.Input"},
+ config: {kind: "onyx.Input"}
+ },
+ {name: "onyx.ToggleButton", title: "Foofoofoo", icon: "box_software.png", stars: 4, version: 2.0, blurb: "Use as a simple page header, or add an optional navigation menu.",
+ inline: {kind: "onyx.ToggleButton"},
+ config: {kind: "onyx.ToggleButton"}
+ },
+ {name: "onyx.Checkbox", title: "Foofoofoo", icon: "box_software.png", stars: 4, version: 2.0, blurb: "Use as a simple page header, or add an optional navigation menu.",
+ inline: {kind: "onyx.Checkbox"},
+ config: {kind: "onyx.Checkbox"}
+ },
+ {name: "onyx.RadioGroup",
+ inline: {kind: "onyx.RadioGroup", components: [
+ {content: "A"},
+ {content: "B"},
+ {content: "C"}
+ ]},
+ config: {kind: "onyx.RadioGroup", isContainer: true, components: [
+ {content: "RadioButton"}
+ ]}
+ },
+ {name: "onyx.RadioButton",
+ inline: {content: "RadioButton", kind: "onyx.RadioButton"},
+ config: {content: "$name", kind: "onyx.RadioButton"}
+ },
+ {name: "onyx.Toolbar", title: "Foofoofoo", icon: "box_software.png", stars: 4, version: 2.0, blurb: "Use as a simple page header, or add an optional navigation menu.",
+ inline: {kind: "onyx.Toolbar"},
+ config: {isContainer: true, kind: "onyx.Toolbar"}
+ },
+ {name: "onyx.Grabber", title: "Foofoofoo", icon: "box_software.png", stars: 4, version: 2.0, blurb: "Use as a simple page header, or add an optional navigation menu.",
+ inline: {kind: "onyx.Grabber", style: "background-color: #333; padding: 4px 12px;"},
+ config: {kind: "onyx.Grabber"}
+ },
+ {name: "onyx.Groupbox", title: "Foofoofoo", icon: "box_software.png", stars: 4, version: 2.0, blurb: "Use as a simple page header, or add an optional navigation menu.",
+ inline: {kind: "onyx.Groupbox", components: [
+ {content: "Header", kind: "onyx.GroupboxHeader"},
+ {content: "Item", style: "padding: 10px;"}
+ ]},
+ config: {kind: "onyx.Groupbox", isContainer: true, components: [
+ {content: "Header", kind: "onyx.GroupboxHeader", isContainer: true},
+ {content: "Item", style: "padding: 10px;"}
+ ]}
+ },
+ {name: "onyx.GroupboxHeader", title: "Foofoofoo", icon: "box_software.png", stars: 4, version: 2.0, blurb: "Use as a simple page header, or add an optional navigation menu.",
+ inline: {content: "Header", kind: "onyx.GroupboxHeader"},
+ config: {content: "$name", kind: "onyx.GroupboxHeader", isContainer: true}
+ }
+ ]}
+); \ No newline at end of file
diff --git a/html/lib/onyx/source/package.js b/html/lib/onyx/source/package.js
new file mode 100644
index 0000000..4a5ff50
--- /dev/null
+++ b/html/lib/onyx/source/package.js
@@ -0,0 +1,37 @@
+enyo.depends(
+ "css/",
+ "Icon.js",
+ "Button.js",
+ "IconButton.js",
+ "Checkbox.js",
+ "Drawer.js",
+ "Grabber.js",
+ "Groupbox.js",
+ "Input.js",
+ "Popup.js",
+ "TextArea.js",
+ "RichText.js",
+ "InputDecorator.js",
+ "Tooltip.js",
+ "TooltipDecorator.js",
+ "MenuDecorator.js",
+ "Menu.js",
+ "MenuItem.js",
+ "PickerDecorator.js",
+ "PickerButton.js",
+ "Picker.js",
+ "FlyweightPicker.js",
+ "RadioButton.js",
+ "RadioGroup.js",
+ "ToggleButton.js",
+ "Toolbar.js",
+ "Tooltip.js",
+ "TooltipDecorator.js",
+ "ProgressBar.js",
+ "ProgressButton.js",
+ "Scrim.js",
+ "Slider.js",
+ "Item.js",
+ "Spinner.js",
+ "MoreToolbar.js"
+);
diff --git a/html/lib/onyx/source/wip-package.js b/html/lib/onyx/source/wip-package.js
new file mode 100644
index 0000000..7901550
--- /dev/null
+++ b/html/lib/onyx/source/wip-package.js
@@ -0,0 +1,4 @@
+enyo.depends(
+ "SwipeableItem.js",
+ "TabPanels.js"
+); \ No newline at end of file
diff --git a/html/liogo.js b/html/liogo.js
new file mode 100644
index 0000000..8e268d9
--- /dev/null
+++ b/html/liogo.js
@@ -0,0 +1,111 @@
+// Mini Logo engine
+// Based on Liogo compiler from Lionel Laské
+// http://liogo.sourceforge.net
+enyo.kind({
+ name: "Liogo",
+ published: { canvas: undefined, image: undefined },
+
+ // Create component
+ create: function() {
+ this.inherited(arguments);
+ this.commands = [];
+ this.initheading = 90;
+ this.canvasChanged();
+ },
+
+ // Canvas property changed
+ canvasChanged: function(old) {
+ this.initx = this.canvas.attributes.width / 2;
+ this.inity = this.canvas.attributes.height / 2;
+ this.width = this.canvas.attributes.width;
+ this.height = this.canvas.attributes.height;
+ },
+
+ // Draw canvas an its content
+ draw: function() {
+ // Clear all
+ this.ctx = this.canvas.node.getContext('2d');
+ this.ctx.clearRect(0, 0, this.width, this.height);
+ this.ctx.canvas.width = this.ctx.canvas.width;
+ this.ctx.strokeStyle = "blue";
+ this.ctx.lineWidth = 2;
+
+ // Set init pos
+ this.x = this.initx;
+ this.y = this.inity;
+ this.heading = this.initheading;
+ this.ctx.moveTo(this.x, this.y);
+
+ // Draw each command
+ var i = 0;
+ var len = this.commands.length;
+ for (i = 0 ; i < len ; i++) {
+ var action = this.commands[i];
+ switch(action.command)
+ {
+ case 'f':
+ this._do_forward(action.arg);
+ break;
+ case 'r':
+ this._do_right(action.arg);
+ break;
+ case 'l':
+ this._do_left(action.arg);
+ break;
+ }
+ this.ctx.moveTo(this.x, this.y);
+ }
+
+ // Draw turtle
+ this.ctx.translate(this.x, this.y);
+ this.ctx.rotate((90-this.heading) * (Math.PI / 180));
+ this.ctx.translate(-11,-14);
+ this.ctx.drawImage(this.image, 0, 0);
+ },
+
+ // Clear all content
+ clear: function() {
+ this.commands = [];
+ this.x = this.initx;
+ this.y = this.inity;
+ this.heading = 90;
+ },
+
+ // Forward turtle to the value
+ forward: function(value) {
+ this.commands.push({command: 'f', arg: value});
+ },
+
+ // Turn left turtle to the angle value
+ left: function(value) {
+ this.commands.push({command: 'l', arg: value});
+ },
+
+ // Turn right turtle to the angle value
+ right: function(value) {
+ this.commands.push({command: 'r', arg: value});
+ },
+
+ // Process forward command, forward turtle using value and current heading
+ _do_forward: function(value) {
+ var radians = this.heading * (Math.PI/180);
+ this.x = this.x + value * Math.cos(radians);
+ this.y = this.y - value * Math.sin(radians);
+ this.ctx.lineTo(this.x, this.y);
+ this.ctx.stroke();
+ },
+
+ // Process left command, just change heading
+ _do_left: function(value) {
+ this.heading += value;
+ if (this.heading > 360)
+ this.heading -= 360;
+ },
+
+ // Process right command, just change heading
+ _do_right: function(value) {
+ this.heading -= value;
+ if (this.heading < 0)
+ this.heading += 360;
+ }
+});
diff --git a/html/package.js b/html/package.js
new file mode 100644
index 0000000..2bdc082
--- /dev/null
+++ b/html/package.js
@@ -0,0 +1,8 @@
+enyo.depends(
+ "sugar.js",
+ "app.js",
+ "liogo.js",
+ "lib/layout/package.js",
+ "lib/onyx/package.js",
+ "lib/canvas/package.js"
+); \ No newline at end of file
diff --git a/html/styles.css b/html/styles.css
new file mode 100644
index 0000000..28190ff
--- /dev/null
+++ b/html/styles.css
@@ -0,0 +1,73 @@
+.main-window {
+ background-color: #ffffff;
+}
+
+.logo {
+ display: inline-block;
+ width: 100px;
+}
+
+.block-top {
+ display: inline-block;
+ vertical-align: top;
+}
+
+.hello-text {
+ display: inline-block;
+ margin-right: 10px;
+}
+
+.hello-line{
+ margin-right: 10px;
+}
+
+.hello-buddy {
+ font-weight: bold;
+}
+
+.hello-advice {
+ font-size: 11px;
+ font-style: italic;
+}
+
+.divider {
+ color: #F49200;
+ text-transform: uppercase;
+ font-family: Segoe UI, Prelude Medium, Helvetica, Verdana, sans-serif;
+ font-size: 14px;
+ font-weight: bold;
+ margin-top: 20px;
+ margin-bottom: 8px;
+}
+
+.spacer {
+ margin-left: 12px;
+}
+
+.slider-decorator {
+ display: inline-block;
+}
+
+.slider-text {
+ display: inline-block;
+ width: 30px;
+}
+
+.slider-bar {
+ display: inline-block;
+ width: 200px;
+}
+
+.liogo-logo {
+ visibility: hidden;
+}
+
+.canvas-style {
+ border: 1px solid black;
+}
+
+.right-part {
+ position: absolute;
+ left: 600px;
+ top: 0px;
+}
diff --git a/html/sugar.js b/html/sugar.js
new file mode 100644
index 0000000..6c9bf08
--- /dev/null
+++ b/html/sugar.js
@@ -0,0 +1,46 @@
+// Enyo interface for Sugar
+
+enyo.Sugar = {};
+enyo.Sugar.component = null;
+enyo.Sugar.sendMessage = function(name, args) {
+ if (enyo.Sugar.component) {
+ enyo.Sugar.component.signal(name, JSON.parse(args));
+ }
+};
+
+enyo.kind({
+ name: "Sugar",
+
+ // Constructor, init component
+ create: function() {
+ this.inherited(arguments);
+ this.handlers = [];
+ enyo.Sugar.component = this;
+ },
+
+ // Connect a callback to a message
+ connect: function(name, callback) {
+ this.handlers[name] = callback;
+ },
+
+ // Send a message to Sugar
+ sendMessage: function(name, args) {
+ var msg = "";
+ msg = msg + "enyo://"+name.length+"/"+name;
+ if (!args)
+ msg = msg + "/0/";
+ else {
+ var value = JSON.stringify(args);
+ msg = msg + "/"+value.length+"/"+value;
+ }
+ console.log(msg);
+ },
+
+ // A message was sent by Sugar
+ signal: function(name, args) {
+ var callback = this.handlers[name];
+ if (callback) {
+ callback(args);
+ }
+ }
+});
diff --git a/images/python-logo.jpg b/images/python-logo.jpg
new file mode 100644
index 0000000..d43e949
--- /dev/null
+++ b/images/python-logo.jpg
Binary files differ
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..f43b2f9
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2009, Simon Schampijer
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+from sugar3.activity import bundlebuilder
+
+bundlebuilder.start()