From ca3ad6305ec0655ad8475a12ac2228b61cdd9ba0 Mon Sep 17 00:00:00 2001 From: Lionel LASKE Date: Sat, 25 Aug 2012 20:23:36 +0000 Subject: Init commit --- 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. + + + Copyright (C) + + 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. + + , 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 @@ + + + +]> + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + 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(''); +}, +script: function(a, b, c) { +document.write('"); +} +}, 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, "&").replace(//g, ">") : ""; +}, +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.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, "&").replace(//g, ">") : ""; +}, +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, "&").replace(/\"/g, """) : 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, "
"); +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 @@ + + + + Enyo Activity + + + + + + + + + + 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 <canvas> HTML tag. It may + contain other canvas components that are derived not from + enyo.Control, but from + enyo.canvas.Control. 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 + enyo.Canvas 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 + enyo.FittableColumnsLayout + 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"} + ] + }); + + enyo.FittableColumnsLayout and + enyo.FittableRowsLayout (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 + enyo.FittableColumns: + + 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 + enyo.FittableRows: + + 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 + enyo.FittableRowsLayout + 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, 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; ienyo.Control and + enyo.Image. + + 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: }_ + */ + 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 enyo.Animator + 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 enyo.Layout 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 .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 enyo.Arranger 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 enyo.Arranger + 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 + enyo.LeftRightArranger. +*/ +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 enyo.Arranger + 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 enyo.Arranger + 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 enyo.Arranger + 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 enyo.Arranger + 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 enyo.Arranger 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 enyo.Arranger 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 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 @@ + + + + controlled inline: onyx toolbar design + + + + + +

+ +
+ +
+
Text in Div
+ +
+
+
+ + +
+
Text in Div
+ +
+
+
+ +
+ + +
+
Text in Div
+ +
+
+
+ +
+
+
Text in Div
+ +
+
+
+ +
+
+
+
+ +
+ + +
+
+
Text in Div
+ +
+
+
+ +
+
+
+
+
+
+
Text in Div
+ +
+
+
+ +
+
+
+
+ +
+ + +
+
+
Text in Div
+ +
+
+
+ +
+
+
+
+
+
+
+
Text in Div
+ +
+
+
+
+ +
+
+
+
+ + +
+
+ +
Text in Div
+ +
+
+
+ + +
+
+
+
+ + +
+
+
+
Text in Div
+ +
+
+
+
+ +
+
+
+
+
+ + 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 @@ + + + + inline: onyx toolbar design + + + + + +

+
+ +
+
+ + +
Text
+
+ + +
+
Text in Div
+
+
+ + +
+ +
+
+ + +
+
+
+
+ + +
+
Text in Div
+ +
+
+
+ + +
+
Text in Div
+ +
+
+
+ + +
+
Text in Div
+ +
+
+
+ + +
+
Text in Div
+ +
+
+
+
+ + 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 @@ + + + + + onyx toolbar design + + + + +
+ +
+
+ +
Text
+
+ +
+
Text in Div
+
+
+ +
+ +
+
+ +
+
+
+
+ +
+ +
+
+ +
+
Text in Div
+
+
+ +
+
+
Text in Div
+
+
+ +
+
+
+
Text in Div
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
div
+ +
+
+
+
+
div
+ +
+
+
+
+
div
+ +
+
+
+
+
div
+ +
+
+
+
+
div
+ +
+
div
+ +
+
+ +
+ +
+
+
+
div
+
+
div
+ +
+
+ +
+ +
+
+
+
div
+
+
+
+
+
div
+
+
+
+
+
+
+
+
+
+
+
div
+ +
+
div
+ +
+
+ +
+ +
+
+ + 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 @@ + + + + inline: onyx toolbar design + + + + + +

+
+ +
+
+ + +
Text
+
+ + +
+
Text in Div
+
+
+ + +
+ +
+
+ + +
+
+
+
+ + +
+
Text in Div
+ +
+
+
+ + +
+
Text in Div
+ +
+
+
+ + +
+
Text in Div
+ +
+
+
+ + +
+
Text in Div
+ +
+
+
+
+ + 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 onyx.Picker, + is a picker that employs the flyweight pattern. It is used to display a + large list of selectable items. As with + enyo.FlyweightRepeater, + 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 onyx.GroupboxHeader 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 onyx.Groupbox. 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 onyx.IconButton. + +*/ +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 + onyx.Icon inside an + onyx.Button, 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 + enyo.Input, 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 + onyx.InputDecorator, 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 onyx.Input. + + {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 onyx.Popup that + displays a list of onyx.MenuItems and looks + like a popup menu. It is meant to be used in conjunction with an + onyx.MenuDecorator. 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 onyx.Menu. 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 onyx.Menu. 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 onyx.Toolbar that can adapt to different screen sizes by moving overflowing controls and content into an onyx.Menu + + {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.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 onyx.Menu, is used to + display a list of items that can be selected. It is meant to be used in + conjunction with an onyx.PickerDecorator. + The decorator loosely couples the Picker with an + onyx.PickerButton--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 onyx.MenuItem, 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 + onyx.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. + */ +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 onyx.Picker. It + loosely couples the Picker with an activating + onyx.PickerButton. 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 onyx.RadioGroup. +*/ +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 onyx.RadioButton 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 + enyo.RichText, 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 + onyx.InputDecorator, 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. 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 enyo.Panels 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 + enyo.TextArea, 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 + onyx.InputDecorator, 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 onyx.Popup that works + with an onyx.TooltipDecorator. 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 onyx.Tooltip. 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 onyx.Input 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() -- cgit v0.9.1