← Back to team overview

ubuntu-touch-coreapps-reviewers team mailing list archive

[Merge] lp:~vthompson/ubuntu-weather-app/reboot-fix-location-reordering-text into lp:ubuntu-weather-app

 

Victor Thompson has proposed merging lp:~vthompson/ubuntu-weather-app/reboot-fix-location-reordering-text into lp:ubuntu-weather-app.

Commit message:
* Fix eliding on LocationsPage
* Hide condition data on LocationsPage when in multiselect mode

Requested reviews:
  Ubuntu Weather Developers (ubuntu-weather-dev)

For more details, see:
https://code.launchpad.net/~vthompson/ubuntu-weather-app/reboot-fix-location-reordering-text/+merge/252983

* Fix eliding on LocationsPage
* Hide condition data on LocationsPage when in multiselect mode

There are some spacing issues when in multiselect mode: http://i.imgur.com/dyz529A.png

This MP should fix that.
-- 
Your team Ubuntu Weather Developers is requested to review the proposed merge of lp:~vthompson/ubuntu-weather-app/reboot-fix-location-reordering-text into lp:ubuntu-weather-app.
=== added file '.bzrignore'
--- .bzrignore	1970-01-01 00:00:00 +0000
+++ .bzrignore	2015-03-14 20:11:20 +0000
@@ -0,0 +1,19 @@
+*.user*
+debian/files
+debian/tmp
+debian/ubuntu-weather-app*
+debian/app-template/
+debian/*.debhelper.log
+debian/*.substvars
+.build
+Makefile
+CMakeCache.txt
+CMakeFiles/
+*.cmake
+*.gmo
+*.mo
+*.desktop
+*.desktop.in
+*.desktop.in.in.h
+.excludes
+ubuntu-weather-app.json

=== renamed file '.bzrignore' => '.bzrignore.moved'
=== added file 'CMakeLists.txt'
--- CMakeLists.txt	1970-01-01 00:00:00 +0000
+++ CMakeLists.txt	2015-03-14 20:11:20 +0000
@@ -0,0 +1,108 @@
+project(com.ubuntu.weather)
+cmake_minimum_required(VERSION 2.8.9)
+
+find_program(INTLTOOL_MERGE intltool-merge)
+if(NOT INTLTOOL_MERGE)
+    message(FATAL_ERROR "Could not find intltool-merge, please install the intltool package")
+endif()
+find_program(INTLTOOL_EXTRACT intltool-extract)
+if(NOT INTLTOOL_EXTRACT)
+    message(FATAL_ERROR "Could not find intltool-extract, please install the intltool package")
+endif()
+
+set (UBUNTU_MANIFEST_PATH "manifest.json.in" CACHE INTERNAL "Relative path to the manifest file")
+set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-permissive -pedantic -Wall -Wextra")
+
+find_package(Qt5Core REQUIRED)
+find_package(Qt5Qml REQUIRED)
+find_package(Qt5Quick REQUIRED)
+
+# Automatically create moc files
+set(CMAKE_AUTOMOC ON)
+
+option(INSTALL_TESTS "Install the tests on make install" on)
+option(CLICK_MODE "Build as a click package" on)
+
+# Tests
+enable_testing()
+
+# Standard install paths
+include(GNUInstallDirs)
+
+set(APP_NAME weather)
+set(APP_HARDCODE ubuntu-weather-app)
+set(MAIN_QML ${APP_HARDCODE}.qml)
+set(DESKTOP_FILE "${APP_HARDCODE}.desktop")
+set(ICON weather-app@xxxxxx)
+set(AUTOPILOT_DIR ubuntu_weather_app)
+
+# Set install paths
+if(CLICK_MODE)
+  set(CMAKE_INSTALL_PREFIX "/")
+  set(UBUNTU-WEATHER_APP_DIR "${CMAKE_INSTALL_DATADIR}/qml")
+
+  set(QT_IMPORTS_DIR "${CMAKE_INSTALL_LIBDIR}")
+  set(EXEC "qmlscene $@ ${UBUNTU-WEATHER_APP_DIR}/${MAIN_QML}")
+  set(MODULE_PATH ${QT_IMPORTS_DIR})
+  if(NOT BZR_REVNO)
+    execute_process(
+      COMMAND bzr revno
+              OUTPUT_VARIABLE BZR_REVNO
+              WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+              OUTPUT_STRIP_TRAILING_WHITESPACE
+      )
+  endif(NOT BZR_REVNO)
+  if(NOT BZR_SOURCE)
+    set(BZR_SOURCE "lp:${APP_HARDCODE}/reboot")
+    message("-- Setting BZR_SOURCE to ${BZR_SOURCE}")
+  endif(NOT BZR_SOURCE)
+else(CLICK_MODE)
+  set(UBUNTU-WEATHER_APP_DIR "${CMAKE_INSTALL_DATADIR}/ubuntu-weather-app")
+  execute_process(
+    COMMAND qmake -query QT_INSTALL_QML
+            OUTPUT_VARIABLE QT_IMPORTS_DIR
+            OUTPUT_STRIP_TRAILING_WHITESPACE
+    )
+  set(MODULE_PATH ${QT_IMPORTS_DIR}/WeatherApp)
+endif(CLICK_MODE)
+
+if(${CLICK_MODE})
+  message("-- Configuring manifest.json")
+
+  execute_process(
+    COMMAND dpkg-architecture -qDEB_HOST_ARCH
+    OUTPUT_VARIABLE CLICK_ARCH
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+  )
+
+  configure_file(${UBUNTU_MANIFEST_PATH} ${CMAKE_CURRENT_BINARY_DIR}/manifest.json)
+  install(FILES ${CMAKE_CURRENT_BINARY_DIR}/manifest.json DESTINATION ${CMAKE_INSTALL_PREFIX})
+  install(FILES "${APP_HARDCODE}.apparmor" DESTINATION ${CMAKE_INSTALL_PREFIX})
+else(CLICK_MODE)
+  set(EXEC "qmlscene $@ -I ${MODULE_PATH}  ${CMAKE_INSTALL_PREFIX}/${UBUNTU-WEATHER_APP_DIR}/${MAIN_QML}")
+endif()
+
+
+file(GLOB_RECURSE I18N_SRC_FILES
+    RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/po
+    *.qml *.js)
+list(APPEND I18N_SRC_FILES ${DESKTOP_FILE}.in.in.h)
+list(SORT I18N_SRC_FILES)
+
+configure_file(${DESKTOP_FILE}.in.in ${DESKTOP_FILE}.in)
+
+add_custom_target(${DESKTOP_FILE} ALL
+    COMMENT "Merging translations into ${DESKTOP_FILE}..."
+    COMMAND LC_ALL=C ${INTLTOOL_MERGE} -d -u ${CMAKE_SOURCE_DIR}/po ${DESKTOP_FILE}.in ${DESKTOP_FILE}
+)
+
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${DESKTOP_FILE}
+        DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
+
+add_subdirectory(app)
+add_subdirectory(backend)
+add_subdirectory(po)
+add_subdirectory(tests)
+
+# TODO: Add custom target for autopilot and run.
+

=== renamed file 'CMakeLists.txt' => 'CMakeLists.txt.moved'
=== added file 'COPYING'
--- COPYING	1970-01-01 00:00:00 +0000
+++ COPYING	2015-03-14 20:11:20 +0000
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  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
+them 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 prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  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.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey 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;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If 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 convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU 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 that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  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.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+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.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     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
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program 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, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU 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 Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.

=== renamed file 'COPYING' => 'COPYING.moved'
=== added file 'README'
--- README	1970-01-01 00:00:00 +0000
+++ README	2015-03-14 20:11:20 +0000
@@ -0,0 +1,11 @@
+Weather app for Ubuntu devices
+
+To contribute:
+    https://wiki.ubuntu.com/Touch/CoreApps/Weather
+    https://wiki.ubuntu.com/Touch/CoreApps/DevelopmentGuide
+
+The following essential packages are required to develop this app:
+- ubuntu-sdk (see http://developer.ubuntu.com/start)
+- intltool - run the `sudo apt-get install intltool` command for installation
+
+See the debian/control file for an up-to-date list of dependencies

=== renamed file 'README' => 'README.moved'
=== added file 'README.translations'
--- README.translations	1970-01-01 00:00:00 +0000
+++ README.translations	2015-03-14 20:11:20 +0000
@@ -0,0 +1,40 @@
+# Updating translations
+
+Translations for the Weather app happen in [Launchpad Translations][] and
+are automatically committed daily on the trunk branch in the po/ folder.
+
+They are then built and installed as part of the package build, so that
+developers don't really need to worry about them.
+
+However, there is one task that needs to be taken care of: exposing new
+translatable messages to translators. So whenever you add new translatable
+messages in the code, make sure to follow these steps:
+
+ 1. Run click-buddy retaining the build directory:
+    `click-buddy --dir . --no-clean`
+ 2. Copy the .pot file from the <build dir> mentioned in the output to your
+    original source:
+    `cp <build dir>/po/*.pot po/`
+ 3. Commit the generated .pot file:
+    `bzr commit -m"Updated translation template"`
+ 4. Push the branch and send a merge proposal as usual
+
+And that's it, once the branch lands Launchpad should take care of all the rest!
+
+# Behind the scenes
+
+Behind the scenes, whenever the po/*.pot file (also known as translations template)
+is committed to trunk Launchpad reads it and updates the translatable strings
+exposed in the web UI. This will enable translators to work on the new strings.
+The translations template contains all translatable strings that have been
+extracted from the source code files.
+
+Launchpad will then store translations in its database and will commit them daily
+in the form of textual po/*.po files to trunk. The PO files are also usually
+referred to as the translations files. You'll find a translation file for each
+language the app has got at least a translated message available for.
+
+Translations for core apps follow the standard [gettext format].
+
+ [Launchpad Translations]: https://translations.launchpad.net/ubuntu-weather-app
+ [gettext format]: https://www.gnu.org/software/gettext/

=== renamed file 'README.translations' => 'README.translations.moved'
=== added directory 'app'
=== added file 'app/CMakeLists.txt'
--- app/CMakeLists.txt	1970-01-01 00:00:00 +0000
+++ app/CMakeLists.txt	2015-03-14 20:11:20 +0000
@@ -0,0 +1,18 @@
+if(NOT "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
+  file(GLOB QML_JS_FILES *.qml *.js)
+  add_custom_target(ubuntu-weather-app_QMlFiles ALL SOURCES ${QML_JS_FILES})
+endif(NOT "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
+
+
+if(CLICK_MODE)
+  set(ICON ${ICON})
+  install(FILES ${ICON} DESTINATION ${CMAKE_INSTALL_PREFIX})
+endif(CLICK_MODE)
+
+install(FILES ${MAIN_QML} DESTINATION ${UBUNTU-WEATHER_APP_DIR})
+
+add_subdirectory(components)
+add_subdirectory(data)
+add_subdirectory(graphics)
+add_subdirectory(ui)
+

=== added directory 'app/components'
=== added file 'app/components/CMakeLists.txt'
--- app/components/CMakeLists.txt	1970-01-01 00:00:00 +0000
+++ app/components/CMakeLists.txt	2015-03-14 20:11:20 +0000
@@ -0,0 +1,7 @@
+add_subdirectory(ListItemActions)
+
+file(GLOB COMPONENTS_QML_JS_FILES *.qml *.js)
+
+add_custom_target(ubuntu-weather-app_components_QMlFiles ALL SOURCES ${COMPONENTS_QML_JS_FILES})
+
+install(FILES ${COMPONENTS_QML_JS_FILES} DESTINATION ${UBUNTU-WEATHER_APP_DIR}/components)

=== added file 'app/components/DayDelegate.qml'
--- app/components/DayDelegate.qml	1970-01-01 00:00:00 +0000
+++ app/components/DayDelegate.qml	2015-03-14 20:11:20 +0000
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2015 Canonical Ltd
+ *
+ * This file is part of Ubuntu Weather App
+ *
+ * Ubuntu Weather App is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Ubuntu Weather App 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, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.3
+import Ubuntu.Components 1.1
+import Ubuntu.Components.ListItems 0.1 as ListItem
+
+ListItem.Standard {
+    height: units.gu(8)
+
+    // TODO: will expand when clicked to reveal more info
+
+    property alias day: dayLabel.text
+    property alias image: weatherImage.source
+    property alias high: highLabel.text
+    property alias low: lowLabel.text
+
+    // Standard divider is not full width so add a ThinDivider to the bottom
+    showDivider: false
+
+    ListItem.ThinDivider {
+        anchors {
+            bottom: parent.bottom
+        }
+    }
+
+    Label {
+        id: dayLabel
+        anchors {
+            left: parent.left
+            right: weatherImage.left
+            rightMargin: units.gu(1)
+            verticalCenter: parent.verticalCenter
+        }
+        elide: Text.ElideRight
+        font.weight: Font.Light
+        fontSize: "medium"
+    }
+
+    Image {
+        id: weatherImage
+        anchors {
+            horizontalCenter: parent.horizontalCenter
+            verticalCenter: parent.verticalCenter
+        }
+        height: units.gu(3)
+        width: units.gu(3)
+    }
+
+    Label {
+        id: lowLabel
+        anchors {
+            left: weatherImage.right
+            right: highLabel.left
+            rightMargin: units.gu(1)
+            verticalCenter: parent.verticalCenter
+        }
+        elide: Text.ElideRight
+        font.pixelSize: units.gu(2)
+        font.weight: Font.Light
+        fontSize: "medium"
+        height: units.gu(2)
+        horizontalAlignment: Text.AlignRight
+        verticalAlignment: Text.AlignTop  // AlignTop appears to align bottom?
+    }
+
+    Label {
+        id: highLabel
+        anchors {
+            bottom: lowLabel.bottom
+            right: parent.right
+        }
+        color: UbuntuColors.orange
+        elide: Text.ElideRight
+        font.pixelSize: units.gu(3)
+        font.weight: Font.Normal
+        height: units.gu(3)
+        verticalAlignment: Text.AlignTop  // AlignTop appears to align bottom?
+    }
+}

=== added file 'app/components/FastScroll.js'
--- app/components/FastScroll.js	1970-01-01 00:00:00 +0000
+++ app/components/FastScroll.js	2015-03-14 20:11:20 +0000
@@ -0,0 +1,130 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** Copyright (C) 2014 Canonical Ltda
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@xxxxxxxxx)
+**
+** This file is part of the Qt Components project.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**   * Redistributions of source code must retain the above copyright
+**     notice, this list of conditions and the following disclaimer.
+**   * Redistributions in binary form must reproduce the above copyright
+**     notice, this list of conditions and the following disclaimer in
+**     the documentation and/or other materials provided with the
+**     distribution.
+**   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
+**     the names of its contributors may be used to endorse or promote
+**     products derived from this software without specific prior written
+**     permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// FastScroll.js - this is just SectionScroller.js with a fix for
+// section.criteria == ViewSection.FirstCharacter
+var sectionData = [];
+var _sections = [];
+
+function initialize(list) {
+    initSectionData(list);
+}
+
+function contains(name) {
+    return (_sections.indexOf(name) > -1)
+}
+
+function initSectionData(list) {
+    if (!list || !list.model) return;
+    sectionData = [];
+    _sections = [];
+    var current = "",
+        prop = list.section.property,
+        sectionText;
+
+    if (list.section.criteria === ViewSection.FullString) {
+        for (var i = 0, count = list.model.count; i < count; i++) {
+            sectionText = list.getSectionText(i)
+            if (sectionText !== current) {
+                current = sectionText;
+                _sections.push(current);
+                sectionData.push({ index: i, header: current });
+            }
+        }
+    } else if (list.section.criteria === ViewSection.FirstCharacter) {
+        for (var i = 0, count = list.model.count; i < count; i++) {
+            sectionText = list.getSectionText(i).substring(0, 1)
+            if (sectionText !== current) {
+                current = sectionText
+                _sections.push(sectionText);
+                sectionData.push({ index: i, header: current });
+            }
+        }
+    }
+}
+
+function getSectionPositionString(name) {
+    var val = _sections.indexOf(name);
+    return val === 0 ? "first" :
+           val === _sections.length - 1 ? "last" : false;
+}
+
+function getAt(pos) {
+    return _sections[pos] ? _sections[pos] : "";
+}
+
+function getRelativeSections(current) {
+    var val = _sections.indexOf(current),
+        sect = [],
+        sl = _sections.length;
+
+    val = val < 1 ? 1 : val >= sl-1 ? sl-2 : val;
+    sect = [getAt(val - 1), getAt(val), getAt(val + 1)];
+
+    return sect;
+}
+
+function getClosestSection(pos, down) {
+    var tmp = (_sections.length) * pos;
+    var val = Math.ceil(tmp) // TODO: better algorithm
+    val = val < 2 ? 1 : val;
+    return _sections[val-1];
+}
+
+function getNextSection(current) {
+    var val = _sections.indexOf(current);
+    return (val > -1 ? _sections[(val < _sections.length - 1 ? val + 1 : val)] : _sections[0]) || "";
+}
+
+function getPreviousSection(current) {
+    var val = _sections.indexOf(current);
+    return (val > -1 ? _sections[(val > 0 ? val - 1 : val)] : _sections[0]) || "";
+}
+
+function getIndexFor(sectionName) {
+    var data = sectionData[_sections.indexOf(sectionName)]
+    if (data) {
+        var val = data.index;
+        return val === 0 || val > 0 ? val : -1;
+    } else {
+        return -1
+    }
+}

=== added file 'app/components/FastScroll.qml'
--- app/components/FastScroll.qml	1970-01-01 00:00:00 +0000
+++ app/components/FastScroll.qml	2015-03-14 20:11:20 +0000
@@ -0,0 +1,321 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** Copyright (C) 2014 Canonical Ltda
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@xxxxxxxxx)
+**
+** This file is part of the Qt Components project.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**   * Redistributions of source code must retain the above copyright
+**     notice, this list of conditions and the following disclaimer.
+**   * Redistributions in binary form must reproduce the above copyright
+**     notice, this list of conditions and the following disclaimer in
+**     the documentation and/or other materials provided with the
+**     distribution.
+**   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
+**     the names of its contributors may be used to endorse or promote
+**     products derived from this software without specific prior written
+**     permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// FastScroll.qml
+import QtQuick 2.3
+import Ubuntu.Components 1.1
+import "FastScroll.js" as Sections
+
+Item {
+    id: root
+
+    property ListView listView
+    property int pinSize: units.gu(2)
+
+    readonly property var letters: ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
+    readonly property alias fastScrolling: internal.fastScrolling
+    readonly property bool showing: (rail.opacity !== 0.0)
+    readonly property double minimumHeight: rail.height
+
+    width: units.gu(7)
+    height: rail.height
+
+    onListViewChanged: {
+        if (listView && listView.model) {
+            internal.initDirtyObserver();
+        } else if (listView) {
+            listView.modelChanged.connect(function() {
+                if (listView.model) {
+                    internal.initDirtyObserver();
+                }
+            });
+        }
+    }
+
+    Connections {
+        target: listView
+        onCurrentIndexChanged: {
+            if (currentIndex != -1) {
+                rail.opacity = 0.0
+            }
+        }
+    }
+
+    Rectangle {
+        id: magnified
+
+        color: Theme.palette.normal.overlay
+        radius: height * 0.3
+        height: pinSize * 2
+        width: height
+        opacity: internal.fastScrolling && root.enabled ? 1.0 : 0.0
+        x: -cursor.width - units.gu(3)
+        y: {
+            if (internal.currentItem) {
+                var itemCenterY = rail.y + internal.currentItem.y + (internal.currentItem.height / 2)
+                return (itemCenterY - (magnified.height / 2))
+            } else {
+                return 0
+            }
+        }
+
+        Label {
+            anchors.fill: parent
+            horizontalAlignment: Text.AlignHCenter
+            verticalAlignment: Text.AlignVCenter
+            text: internal.desireSection
+            fontSize: "small"
+        }
+
+        Behavior on opacity {
+            UbuntuNumberAnimation {}
+        }
+    }
+
+    Rectangle {
+        id: cursor
+
+        property bool showLabel: false
+        property string currentSectionName: ""
+
+        radius: pinSize * 0.3
+        height: pinSize
+        width: height
+        color: Theme.palette.normal.foreground
+        opacity: rail.opacity
+        x: rail.x
+        y: {
+            if (internal.currentItem) {
+                var itemCenterY = rail.y + internal.currentItem.y + (internal.currentItem.height / 2)
+                return (itemCenterY - (cursor.height / 2))
+            } else {
+                return 0
+            }
+        }
+        Behavior on y {
+            enabled: !internal.fastScrolling
+            UbuntuNumberAnimation { }
+        }
+    }
+
+    Column {
+        id: rail
+
+        property bool isVisible: root.enabled &&
+                                 (listView.flicking || dragArea.pressed) &&
+                                 (listView.currentIndex == -1)
+        anchors {
+            right: parent.right
+            rightMargin: units.gu(2)
+            left: parent.left
+            leftMargin: units.gu(2)
+            top: parent.top
+        }
+        height: childrenRect.height
+        opacity: 0.0
+        onIsVisibleChanged: {
+            if (isVisible) {
+                rail.opacity = 1.0
+                hideTimer.stop()
+            } else if (!root.enabled) {
+                rail.opacity = 0.0
+            } else {
+                hideTimer.restart()
+            }
+        }
+
+        Behavior on opacity {
+            UbuntuNumberAnimation { }
+        }
+
+        Repeater {
+            id: sectionsRepeater
+
+            model: root.letters
+            Label {
+                id: lbl
+
+                anchors.left: parent.left
+                height: pinSize
+                width: pinSize
+                verticalAlignment: Text.AlignVCenter
+                horizontalAlignment: Text.AlignHCenter
+                text: modelData
+                fontSize: "x-small"
+                color: cursor.y === y ? "white" : Theme.palette.selected.backgroundText
+                opacity: !internal.modelDirty && Sections.contains(text) ? 1.0 : 0.5
+            }
+        }
+
+        Timer {
+            id: hideTimer
+
+            running: false
+            interval: 2000
+            onTriggered: rail.opacity = 0.0
+        }
+    }
+
+    MouseArea {
+        id: dragArea
+
+        anchors {
+            left: parent.left
+            right: parent.right
+        }
+        y: rail.y
+        height: rail.height
+        visible: rail.opacity == 1.0
+
+        preventStealing: true
+        onPressed: {
+            internal.adjustContentPosition(mouseY)
+            dragginTimer.start()
+        }
+
+        onReleased: {
+            dragginTimer.stop()
+            internal.desireSection = ""
+            internal.fastScrolling = false
+        }
+
+        onPositionChanged: internal.adjustContentPosition(mouseY)
+
+        Timer {
+            id: dragginTimer
+
+            running: false
+            interval: 150
+            onTriggered: {
+                internal.fastScrolling = true
+            }
+        }
+    }
+
+    Timer {
+        id: dirtyTimer
+        interval: 500
+        running: false
+        onTriggered: {
+            Sections.initSectionData(listView);
+            internal.modelDirty = false;
+        }
+    }
+
+    Timer {
+        id: timerScroll
+
+        running: false
+        interval: 10
+        onTriggered: {
+            if (internal.desireSection != internal.currentSection) {
+                var idx = Sections.getIndexFor(internal.desireSection)
+                if (idx !== -1) {
+                    listView.cancelFlick()
+                    listView.positionViewAtIndex(idx, ListView.Beginning)
+                }
+            }
+        }
+    }
+
+    QtObject {
+        id: internal
+
+        property string currentSection: listView.currentSection
+        property string desireSection: ""
+        property string targetSection: fastScrolling ? desireSection : currentSection
+        property int oldY: 0
+        property bool modelDirty: false
+        property bool down: true
+        property bool fastScrolling: false
+        property var currentItem: null
+
+        onTargetSectionChanged: moveIndicator(targetSection)
+
+        function initDirtyObserver() {
+            Sections.initialize(listView);
+            function dirtyObserver() {
+                if (!internal.modelDirty) {
+                    internal.modelDirty = true;
+                    dirtyTimer.running = true;
+                }
+            }
+
+            if (listView.model.countChanged)
+                listView.model.countChanged.connect(dirtyObserver);
+
+            if (listView.model.itemsChanged)
+                listView.model.itemsChanged.connect(dirtyObserver);
+
+            if (listView.model.itemsInserted)
+                listView.model.itemsInserted.connect(dirtyObserver);
+
+            if (listView.model.itemsMoved)
+                listView.model.itemsMoved.connect(dirtyObserver);
+
+            if (listView.model.itemsRemoved)
+                listView.model.itemsRemoved.connect(dirtyObserver);
+        }
+
+        function adjustContentPosition(mouseY) {
+            var child = rail.childAt(rail.width / 2, mouseY)
+            if (!child || child.text === "") {
+                return
+            }
+            var section = child.text
+            if (internal.desireSection !== section) {
+                internal.desireSection = section
+                moveIndicator(section)
+                if (dragArea.pressed) {
+                    timerScroll.restart()
+                }
+            }
+        }
+
+        function moveIndicator(section)
+        {
+            var index = root.letters.indexOf(section)
+            if (index != -1) {
+                currentItem = sectionsRepeater.itemAt(index)
+            }
+        }
+    }
+}

=== added file 'app/components/HeaderRow.qml'
--- app/components/HeaderRow.qml	1970-01-01 00:00:00 +0000
+++ app/components/HeaderRow.qml	2015-03-14 20:11:20 +0000
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 Canonical Ltd
+ *
+ * This file is part of Ubuntu Weather App
+ *
+ * Ubuntu Weather App is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Ubuntu Weather App 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, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.3
+import Ubuntu.Components 1.1
+
+
+Row {
+    spacing: units.gu(2)
+    width: parent.width
+
+    property alias locationName: locationNameLabel.text
+
+    Label {
+        id: locationNameLabel
+        color: UbuntuColors.darkGrey
+        elide: Text.ElideRight
+        font.weight: Font.Normal
+        fontSize: "large"
+        height: settingsButton.height
+        width: parent.width - settingsButton.width - parent.spacing
+        verticalAlignment: Text.AlignVCenter
+    }
+
+    AbstractButton {
+        id: settingsButton
+        height: width
+        width: units.gu(4)
+
+        onClicked: mainPageStack.push(Qt.resolvedUrl("../ui/SettingsPage.qml"))
+
+        Rectangle {
+            anchors {
+                fill: parent
+            }
+            color: Theme.palette.selected.background
+            visible: parent.pressed
+        }
+
+        Icon {
+            anchors {
+                centerIn: parent
+            }
+            color: UbuntuColors.darkGrey
+            name: "settings"
+            height: width
+            width: units.gu(2.5)
+        }
+    }
+}

=== added file 'app/components/HomeGraphic.qml'
--- app/components/HomeGraphic.qml	1970-01-01 00:00:00 +0000
+++ app/components/HomeGraphic.qml	2015-03-14 20:11:20 +0000
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 Canonical Ltd
+ *
+ * This file is part of Ubuntu Weather App
+ *
+ * Ubuntu Weather App is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Ubuntu Weather App 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, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.3
+import Ubuntu.Components 1.1
+
+Item {
+    height: units.gu(32)
+    width: parent.width
+
+    property alias icon: iconImage.source
+
+    // TODO: will be on 'rails' (horizontal listview?) to reveal hourly forecast
+    Image {
+        id: iconImage
+        anchors {
+            centerIn: parent
+        }
+        fillMode: Image.PreserveAspectFit
+        width: parent.width
+    }
+}
+

=== added file 'app/components/HomeHourly.qml'
--- app/components/HomeHourly.qml	1970-01-01 00:00:00 +0000
+++ app/components/HomeHourly.qml	2015-03-14 20:11:20 +0000
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2015 Canonical Ltd
+ *
+ * This file is part of Ubuntu Weather App
+ *
+ * Ubuntu Weather App is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Ubuntu Weather App 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, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.3
+import Ubuntu.Components 1.1
+import Ubuntu.Components.ListItems 0.1 as ListItem
+import QtGraphicalEffects 1.0
+
+Item {
+    id: homeHourly
+
+    onVisibleChanged: {
+        if(visible) {
+            ListView.model = forecasts.length
+        }
+    }
+
+    ListView {
+        id: hourlyForecasts
+        width:parent.width
+        height:parent.height
+        model: forecasts.length
+        orientation: ListView.Horizontal
+        clip:true
+        MouseArea {
+            anchors.fill: parent
+            onClicked: {
+                homeGraphic.visible = true
+            }
+        }
+        delegate: Rectangle {
+            width: childrenRect.width
+            height: parent.height
+            property var hourData: forecasts[index]
+            Column {
+                id: hourColumn
+                width: units.gu(10)
+                height: childrenRect.height
+                anchors.verticalCenter: parent.verticalCenter
+                Label {
+                    text: formatTimestamp(hourData.date, 'ddd')+" "+formatTime(hourData.date, 'h:mm')
+                    fontSize: "small"
+                    font.weight: Font.Light
+                    anchors.horizontalCenter: parent.horizontalCenter
+                }
+                Item {
+                    width: units.gu(7)
+                    height: units.gu(7)
+                    anchors.horizontalCenter: parent.horizontalCenter
+                    Image {
+                        id: iconImage
+                        fillMode: Image.PreserveAspectFit
+                        anchors.fill: parent
+                        source: (hourData.icon !== undefined && iconMap[hourData.icon] !== undefined) ? iconMap[hourData.icon] : ""
+                    }
+                    ColorOverlay {
+                        anchors.fill: iconImage
+                        source: iconImage
+                        color: UbuntuColors.orange
+                        anchors.horizontalCenter: parent.horizontalCenter
+                    }
+                }
+                Label {
+                    font.pixelSize: units.gu(3)
+                    font.weight: Font.Light
+                    anchors.horizontalCenter: parent.horizontalCenter
+                    text: Math.round(hourData[tempUnits].temp).toString()+settings.tempScale
+                }
+
+            }
+            Rectangle {
+                width: units.gu(0.1)
+                height: hourColumn.height
+                color: UbuntuColors.darkGrey
+                anchors.verticalCenter: parent.verticalCenter
+            }
+        }
+    }
+}

=== added file 'app/components/HomeTempInfo.qml'
--- app/components/HomeTempInfo.qml	1970-01-01 00:00:00 +0000
+++ app/components/HomeTempInfo.qml	2015-03-14 20:11:20 +0000
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2015 Canonical Ltd
+ *
+ * This file is part of Ubuntu Weather App
+ *
+ * Ubuntu Weather App is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Ubuntu Weather App 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, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.3
+import Ubuntu.Components 1.1
+
+
+Column {
+    anchors {
+        left: parent.left
+        right: parent.right
+    }
+    spacing: units.gu(1)
+
+    property alias description: descriptionLabel.text
+    property alias high: highLabel.text
+    property alias low: lowLabel.text
+    property alias now: nowLabel.text
+
+    Label {
+        font.weight: Font.Light
+        fontSize: "small"
+        text: i18n.tr("Today")
+    }
+
+    Label {
+        id: descriptionLabel
+        font.weight: Font.Normal
+        fontSize: "large"
+    }
+
+    Row {
+        spacing: units.gu(2)
+
+        Label {
+            id: nowLabel
+            color: UbuntuColors.orange
+            font.pixelSize: units.gu(8)
+            font.weight: Font.Light
+            height: units.gu(8)
+            verticalAlignment: Text.AlignBottom  // AlignBottom seems to put it at the top?
+        }
+
+        Column {
+            Label {
+                id: lowLabel
+                font.weight: Font.Light
+                fontSize: "medium"
+            }
+
+            Label {
+                id: highLabel
+                font.weight: Font.Light
+                fontSize: "medium"
+            }
+        }
+    }
+}

=== added directory 'app/components/ListItemActions'
=== added file 'app/components/ListItemActions/CMakeLists.txt'
--- app/components/ListItemActions/CMakeLists.txt	1970-01-01 00:00:00 +0000
+++ app/components/ListItemActions/CMakeLists.txt	2015-03-14 20:11:20 +0000
@@ -0,0 +1,5 @@
+file(GLOB LISTITEMACTIONS_QML_JS_FILES *.qml *.js)
+
+add_custom_target(ubuntu-weather-app_listitemactions_QMlFiles ALL SOURCES ${LISTITEMACTIONS_QML_JS_FILES})
+
+install(FILES ${LISTITEMACTIONS_QML_JS_FILES} DESTINATION ${UBUNTU-WEATHER_APP_DIR}/components/ListItemActions)

=== added file 'app/components/ListItemActions/CheckBox.qml'
--- app/components/ListItemActions/CheckBox.qml	1970-01-01 00:00:00 +0000
+++ app/components/ListItemActions/CheckBox.qml	2015-03-14 20:11:20 +0000
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2012-2014 Canonical, Ltd.
+ *
+ * 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; version 3.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.2
+import Ubuntu.Components 1.1
+
+CheckBox {
+    checked: root.selected
+    width: implicitWidth
+    // disable item mouse area to avoid conflicts with parent mouse area
+    __mouseArea.enabled: false
+}

=== added file 'app/components/ListItemActions/Remove.qml'
--- app/components/ListItemActions/Remove.qml	1970-01-01 00:00:00 +0000
+++ app/components/ListItemActions/Remove.qml	2015-03-14 20:11:20 +0000
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2014 Andrew Hayzen <ahayzen@xxxxxxxxx>
+ *                    Daniel Holm <d.holmen@xxxxxxxxx>
+ *                    Victor Thompson <victor.thompson@xxxxxxxxx>
+ *
+ * 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; version 3.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.3
+import Ubuntu.Components 1.1
+
+Action {
+    id: removeAction
+    iconName: "delete"
+    objectName: "swipeDeleteAction"
+    text: i18n.tr("Remove")
+}

=== added file 'app/components/ListItemReorderComponent.qml'
--- app/components/ListItemReorderComponent.qml	1970-01-01 00:00:00 +0000
+++ app/components/ListItemReorderComponent.qml	2015-03-14 20:11:20 +0000
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2013, 2014, 2015
+ *      Andrew Hayzen <ahayzen@xxxxxxxxx>
+ *      Nekhelesh Ramananthan <krnekhelesh@xxxxxxxxx>
+ *      Victor Thompson <victor.thompson@xxxxxxxxx>
+ *
+ * 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; version 3.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.3
+import Ubuntu.Components 1.1
+
+
+Item {
+    id: actionReorder
+    width: units.gu(4)
+
+    Icon {
+        anchors {
+            horizontalCenter: parent.horizontalCenter
+            verticalCenter: parent.verticalCenter
+        }
+        name: "navigation-menu"  // TODO: use proper image
+        height: width
+        width: units.gu(3)
+    }
+
+    MouseArea {
+        id: actionReorderMouseArea
+        anchors {
+            fill: parent
+        }
+        property int startY: 0
+        property int startContentY: 0
+
+        onPressed: {
+            root.parent.parent.interactive = false;  // stop scrolling of listview
+            startY = root.y;
+            startContentY = root.parent.parent.contentY;
+            root.z += 10;  // force ontop of other elements
+
+            console.debug("Reorder listitem pressed", root.y)
+        }
+        onMouseYChanged: root.y += mouse.y - (root.height / 2);
+        onReleased: {
+            console.debug("Reorder diff by position", getDiff());
+
+            var diff = getDiff();
+
+            // Remove the height of the actual item if moved down
+            if (diff > 0) {
+                diff -= 1;
+            }
+
+            root.parent.parent.interactive = true;  // reenable scrolling
+
+            if (diff === 0) {
+                // Nothing has changed so reset the item
+                // z index is restored after animation
+                resetListItemYAnimation.start();
+            }
+            else {
+                var newIndex = index + diff;
+
+                if (newIndex < 0) {
+                    newIndex = 0;
+                }
+                else if (newIndex > root.parent.parent.count - 1) {
+                    newIndex = root.parent.parent.count - 1;
+                }
+
+                root.z -= 10;  // restore z index
+                reorder(index, newIndex)
+            }
+        }
+
+        function getDiff() {
+            // Get the amount of items that have been passed over (by centre)
+            return Math.round((((root.y - startY) + (root.parent.parent.contentY - startContentY)) / root.height) + 0.5);
+        }
+    }
+
+    SequentialAnimation {
+        id: resetListItemYAnimation
+        UbuntuNumberAnimation {
+            target: root;
+            property: "y";
+            to: actionReorderMouseArea.startY
+        }
+        ScriptAction {
+            script: {
+                root.z -= 10;  // restore z index
+            }
+        }
+    }
+}

=== added file 'app/components/ListItemWithActions.qml'
--- app/components/ListItemWithActions.qml	1970-01-01 00:00:00 +0000
+++ app/components/ListItemWithActions.qml	2015-03-14 20:11:20 +0000
@@ -0,0 +1,496 @@
+/*
+ * Copyright (C) 2012-2015 Canonical, Ltd.
+ *
+ * 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; version 3.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.3
+import Ubuntu.Components 1.1
+import Ubuntu.Components.ListItems 1.0 as ListItem
+
+
+Item {
+    id: root
+    width: parent.width
+
+    property Action leftSideAction: null
+    property list<Action> rightSideActions
+    property double defaultHeight: units.gu(8)
+    property bool locked: false
+    property Action activeAction: null
+    property var activeItem: null
+    property bool triggerActionOnMouseRelease: false
+    property color color: Theme.palette.normal.background
+    property color selectedColor: "#E6E6E6"
+    property bool selected: false
+    property bool selectionMode: false
+    property alias internalAnchors: mainContents.anchors
+    default property alias contents: mainContents.children
+
+    readonly property double actionWidth: units.gu(4)
+    readonly property double leftActionWidth: units.gu(10)
+    readonly property double actionThreshold: actionWidth * 0.4
+    readonly property double threshold: 0.4
+    readonly property string swipeState: main.x == 0 ? "Normal" : main.x > 0 ? "LeftToRight" : "RightToLeft"
+    readonly property alias swipping: mainItemMoving.running
+    readonly property bool _showActions: mouseArea.pressed || swipeState != "Normal" || swipping
+
+    property alias _main: main  // CUSTOM
+    property alias pressed: mouseArea.pressed  // CUSTOM
+
+    /* internal */
+    property var _visibleRightSideActions: filterVisibleActions(rightSideActions)
+
+    signal itemClicked(var mouse)
+    signal itemPressAndHold(var mouse)
+
+    function returnToBoundsRTL(direction)
+    {
+        var actionFullWidth = actionWidth + units.gu(2)
+
+        // go back to normal state if swipping reverse
+        if (direction === "LTR") {
+            updatePosition(0)
+            return
+        } else if (!triggerActionOnMouseRelease) {
+            updatePosition(-rightActionsView.width + units.gu(2))
+            return
+        }
+
+        var xOffset = Math.abs(main.x)
+        var index = Math.min(Math.floor(xOffset / actionFullWidth), _visibleRightSideActions.length)
+        var newX = 0
+
+        if (index === _visibleRightSideActions.length) {
+            newX = -(rightActionsView.width - units.gu(2))
+        } else if (index >= 1) {
+            newX = -(actionFullWidth * index)
+        }
+
+        updatePosition(newX)
+    }
+
+    function returnToBoundsLTR(direction)
+    {
+        var finalX = leftActionWidth
+        if ((direction === "RTL") || (main.x <= (finalX * root.threshold)))
+            finalX = 0
+        updatePosition(finalX)
+    }
+
+    function returnToBounds(direction)
+    {
+        if (main.x < 0) {
+            returnToBoundsRTL(direction)
+        } else if (main.x > 0) {
+            returnToBoundsLTR(direction)
+        } else {
+            updatePosition(0)
+        }
+    }
+
+    function contains(item, point, marginX)
+    {
+        var itemStartX = item.x - marginX
+        var itemEndX = item.x + item.width + marginX
+        return (point.x >= itemStartX) && (point.x <= itemEndX) &&
+               (point.y >= item.y) && (point.y <= (item.y + item.height));
+    }
+
+    function getActionAt(point)
+    {
+        if (leftSideAction && contains(leftActionViewLoader.item, point, 0)) {
+            return leftSideAction
+        } else if (contains(rightActionsView, point, 0)) {
+            var newPoint = root.mapToItem(rightActionsView, point.x, point.y)
+            for (var i = 0; i < rightActionsRepeater.count; i++) {
+                var child = rightActionsRepeater.itemAt(i)
+                if (contains(child, newPoint, units.gu(1))) {
+                    return i
+                }
+            }
+        }
+        return -1
+    }
+
+    function updateActiveAction()
+    {
+        if (triggerActionOnMouseRelease &&
+            (main.x <= -(root.actionWidth + units.gu(2))) &&
+            (main.x > -(rightActionsView.width - units.gu(2)))) {
+            var actionFullWidth = actionWidth + units.gu(2)
+            var xOffset = Math.abs(main.x)
+            var index = Math.min(Math.floor(xOffset / actionFullWidth), _visibleRightSideActions.length)
+            index = index - 1
+            if (index > -1) {
+                root.activeItem = rightActionsRepeater.itemAt(index)
+                root.activeAction = root._visibleRightSideActions[index]
+            }
+        } else {
+            root.activeAction = null
+        }
+    }
+
+    function resetSwipe()
+    {
+        updatePosition(0)
+    }
+
+    function filterVisibleActions(actions)
+    {
+        var visibleActions = []
+        for(var i = 0; i < actions.length; i++) {
+            var action = actions[i]
+            if (action.visible) {
+                visibleActions.push(action)
+            }
+        }
+        return visibleActions
+    }
+
+    function updatePosition(pos)
+    {
+        if (!root.triggerActionOnMouseRelease && (pos !== 0)) {
+            mouseArea.state = pos > 0 ? "RightToLeft" : "LeftToRight"
+        } else {
+            mouseArea.state = ""
+        }
+        main.x = pos
+    }
+
+    // CUSTOM remove animation
+    SequentialAnimation {
+        id: removeAnimation
+
+        property var action
+
+        UbuntuNumberAnimation {
+            target: root
+            duration: UbuntuAnimation.BriskDuration
+            property: "height";
+            to: 0
+        }
+        ScriptAction {
+            script: removeAnimation.action.trigger()
+        }
+    }
+
+    states: [
+        State {
+            name: "select"
+            when: selectionMode || selected
+            PropertyChanges {
+                target: selectionIcon
+                source: Qt.resolvedUrl("ListItemActions/CheckBox.qml")
+                anchors.leftMargin: units.gu(2)
+            }
+            PropertyChanges {
+                target: root
+                locked: true
+            }
+            PropertyChanges {
+                target: main
+                x: 0
+            }
+        }
+    ]
+
+    height: defaultHeight
+    //clip: height !== defaultHeight  // CUSTOM
+
+    Loader {  // CUSTOM
+        id: leftActionViewLoader
+        anchors {
+            top: parent.top
+            bottom: parent.bottom
+            right: main.left
+        }
+        asynchronous: true
+        sourceComponent: leftSideAction ? leftActionViewComponent : undefined
+    }
+
+    Component {  // CUSTOM
+        id: leftActionViewComponent
+
+        Rectangle {
+            id: leftActionView
+            width: root.leftActionWidth + actionThreshold
+            color: UbuntuColors.red
+
+            Icon {
+                id: leftActionIcon
+                anchors {
+                    centerIn: parent
+                    horizontalCenterOffset: actionThreshold / 2
+                }
+                objectName: "swipeDeleteAction"  // CUSTOM
+                name: leftSideAction && _showActions ? leftSideAction.iconName : ""
+                color: Theme.palette.selected.field
+                height: units.gu(3)
+                width: units.gu(3)
+            }
+        }
+    }
+
+    //Rectangle {
+    Item {  // CUSTOM
+       id: rightActionsView
+
+       anchors {
+           top: main.top
+           left: main.right
+           bottom: main.bottom
+       }
+       visible: _visibleRightSideActions.length > 0
+       width: rightActionsRepeater.count > 0 ? rightActionsRepeater.count * (root.actionWidth + units.gu(2)) + root.actionThreshold + units.gu(2) : 0
+       // color: "white"  // CUSTOM
+
+       Row {
+           anchors{
+               top: parent.top
+               left: parent.left
+               leftMargin: units.gu(2)
+               right: parent.right
+               rightMargin: units.gu(2)
+               bottom: parent.bottom
+           }
+           spacing: units.gu(2)
+           Repeater {
+               id: rightActionsRepeater
+
+               model: _showActions ? _visibleRightSideActions : []
+               Item {
+                   property alias image: img
+
+                   height: rightActionsView.height
+                   width: root.actionWidth
+
+                   Icon {
+                       id: img
+
+                       anchors.centerIn: parent
+                       objectName: rightSideActions[index].objectName  // CUSTOM
+                       width: units.gu(3)
+                       height: units.gu(3)
+                       name: modelData.iconName
+                       color: root.activeAction === modelData ? UbuntuColors.orange : UbuntuColors.coolGrey  // CUSTOM
+                   }
+               }
+           }
+       }
+    }
+
+    Rectangle {
+        id: main
+        objectName: "mainItem"
+
+        anchors {
+            top: parent.top
+            bottom: parent.bottom
+        }
+
+        width: parent.width
+        color: root.selected ? root.selectedColor : root.color
+
+        Loader {
+            id: selectionIcon
+
+            anchors {
+                left: main.left
+                verticalCenter: main.verticalCenter
+            }
+            asynchronous: true  // CUSTOM
+            width: (status === Loader.Ready) ? item.implicitWidth : 0
+            visible: (status === Loader.Ready) && (item.width === item.implicitWidth)
+
+            Behavior on width {
+                NumberAnimation {
+                    duration: UbuntuAnimation.SnapDuration
+                }
+            }
+        }
+
+        Item {
+            id: mainContents
+
+            anchors {
+                left: selectionIcon.right
+                //leftMargin: units.gu(2)  // CUSTOM
+                top: parent.top
+                //topMargin: units.gu(1)  // CUSTOM
+                right: parent.right
+                //rightMargin: units.gu(2)  // CUSTOM
+                bottom: parent.bottom
+                //bottomMargin: units.gu(1)  // CUSTOM
+            }
+        }
+
+        Behavior on x {
+            UbuntuNumberAnimation {
+                id: mainItemMoving
+
+                easing.type: Easing.OutElastic
+                duration: UbuntuAnimation.SlowDuration
+            }
+        }
+    }
+
+    SequentialAnimation {
+        id: triggerAction
+
+        property var currentItem: root.activeItem ? root.activeItem.image : null
+
+        running: false
+        ParallelAnimation {
+            UbuntuNumberAnimation {
+                target: triggerAction.currentItem
+                property: "opacity"
+                from: 1.0
+                to: 0.0
+                duration: UbuntuAnimation.SlowDuration
+                easing {type: Easing.InOutBack; }
+            }
+            UbuntuNumberAnimation {
+                target: triggerAction.currentItem
+                properties: "width, height"
+                from: units.gu(3)
+                to: root.actionWidth
+                duration: UbuntuAnimation.SlowDuration
+                easing {type: Easing.InOutBack; }
+            }
+        }
+        PropertyAction {
+            target: triggerAction.currentItem
+            properties: "width, height"
+            value: units.gu(3)
+        }
+        PropertyAction {
+            target: triggerAction.currentItem
+            properties: "opacity"
+            value: 1.0
+        }
+        ScriptAction {
+            script: {
+                root.activeAction.triggered(root)
+                mouseArea.state = ""
+            }
+        }
+        PauseAnimation {
+            duration: 500
+        }
+        UbuntuNumberAnimation {
+            target: main
+            property: "x"
+            to: 0
+        }
+    }
+
+    MouseArea {
+        id: mouseArea
+
+        property bool locked: root.locked || ((root.leftSideAction === null) && (root._visibleRightSideActions.count === 0))  // CUSTOM
+        property bool manual: false
+        property string direction: "None"
+        property real lastX: -1
+
+        anchors.fill: parent
+        drag {
+            target: locked ? null : main
+            axis: Drag.XAxis
+            minimumX: rightActionsView.visible ? -(rightActionsView.width) : 0
+            maximumX: leftSideAction ? leftActionViewLoader.item.width : 0
+            threshold: root.actionThreshold
+        }
+
+        states: [
+            State {
+                name: "LeftToRight"
+                PropertyChanges {
+                    target: mouseArea
+                    drag.maximumX: 0
+                }
+            },
+            State {
+                name: "RightToLeft"
+                PropertyChanges {
+                    target: mouseArea
+                    drag.minimumX: 0
+                }
+            }
+        ]
+
+        onMouseXChanged: {
+            var offset = (lastX - mouseX)
+            if (Math.abs(offset) <= root.actionThreshold) {
+                return
+            }
+            lastX = mouseX
+            direction = offset > 0 ? "RTL" : "LTR";
+        }
+
+        onPressed: {
+            lastX = mouse.x
+        }
+
+        onReleased: {
+            if (root.triggerActionOnMouseRelease && root.activeAction) {
+                triggerAction.start()
+            } else {
+                root.returnToBounds()
+                root.activeAction = null
+            }
+            lastX = -1
+            direction = "None"
+        }
+        onClicked: {
+            if (selectionMode) {  // CUSTOM - selecting a listitem should toggle selection if in selectionMode
+                selected = !selected
+                return
+            } else if (main.x === 0) {
+                root.itemClicked(mouse)
+            } else if (main.x > 0) {
+                var action = getActionAt(Qt.point(mouse.x, mouse.y))
+                if (action && action !== -1) {
+                    //action.triggered(root)
+                    removeAnimation.action = action  // CUSTOM - use our animation instead
+                    removeAnimation.start()  // CUSTOM
+                }
+            } else {
+                var actionIndex = getActionAt(Qt.point(mouse.x, mouse.y))
+
+                if (actionIndex !== -1 && actionIndex !== leftSideAction) {  // CUSTOM - can be leftAction
+                    root.activeItem = rightActionsRepeater.itemAt(actionIndex)
+                    root.activeAction = root.rightSideActions[actionIndex]
+                    triggerAction.start()
+                    return
+                }
+            }
+            root.resetSwipe()
+        }
+
+        onPositionChanged: {
+            if (mouseArea.pressed) {
+                updateActiveAction()
+
+                listItemSwiping(index)  // CUSTOM - tells other listitems to dismiss any swipe
+            }
+        }
+        onPressAndHold: {
+            if (main.x === 0) {
+                root.itemPressAndHold(mouse)
+            }
+        }
+
+        z: -1
+    }
+}

=== added file 'app/components/MultiSelectHeadState.qml'
--- app/components/MultiSelectHeadState.qml	1970-01-01 00:00:00 +0000
+++ app/components/MultiSelectHeadState.qml	2015-03-14 20:11:20 +0000
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2015
+ *      Andrew Hayzen <ahayzen@xxxxxxxxx>
+ *      Victor Thompson <victor.thompson@xxxxxxxxx>
+ *
+ * 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; version 3.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.3
+import Ubuntu.Components 1.1
+
+PageHeadState {
+    id: selectionState
+    actions: [
+        Action {
+            iconName: "select"
+            text: i18n.tr("Select All")
+            onTriggered: {
+                if (listview.selectedItems.length === listview.model.count) {
+                    listview.clearSelection()
+                } else {
+                    listview.selectAll()
+                }
+            }
+        },
+        Action {
+            enabled: listview.selectedItems.length > 0
+            iconName: "delete"
+            text: i18n.tr("Delete")
+            visible: removable
+
+            onTriggered: {
+                removed(listview.selectedItems)
+
+                listview.closeSelection()
+            }
+        }
+
+    ]
+    backAction: Action {
+        text: i18n.tr("Cancel selection")
+        iconName: "back"
+        onTriggered: {
+            listview.clearSelection()
+            listview.state = "normal"
+        }
+    }
+    head: thisPage.head
+    name: "selection"
+
+    PropertyChanges {
+        target: thisPage.head
+        backAction: selectionState.backAction
+        actions: selectionState.actions
+    }
+
+    property ListView listview
+    property bool removable: false
+    property Page thisPage
+
+    signal removed(var selectedItems)
+}

=== added file 'app/components/MultiSelectListView.qml'
--- app/components/MultiSelectListView.qml	1970-01-01 00:00:00 +0000
+++ app/components/MultiSelectListView.qml	2015-03-14 20:11:20 +0000
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2013, 2014, 2015
+ *      Andrew Hayzen <ahayzen@xxxxxxxxx>
+ *      Daniel Holm <d.holmen@xxxxxxxxx>
+ *      Victor Thompson <victor.thompson@xxxxxxxxx>
+ *
+ * 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; version 3.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.3
+import Ubuntu.Components 1.1
+
+
+WeatherListView {
+    property var selectedItems: []
+
+    signal clearSelection()
+    signal closeSelection()
+    signal selectAll()
+
+    onClearSelection: selectedItems = []
+    onCloseSelection: {
+        clearSelection()
+        state = "normal"
+    }
+    onSelectAll: {
+        var tmp = selectedItems
+
+        for (var i=0; i < model.count; i++) {
+            if (tmp.indexOf(i) === -1) {
+                tmp.push(i)
+            }
+        }
+
+        selectedItems = tmp
+    }
+    onVisibleChanged: {
+        if (!visible) {
+            closeSelection()
+        }
+    }
+}

=== added file 'app/components/PageWithBottomEdge.qml'
--- app/components/PageWithBottomEdge.qml	1970-01-01 00:00:00 +0000
+++ app/components/PageWithBottomEdge.qml	2015-03-14 20:11:20 +0000
@@ -0,0 +1,411 @@
+/*
+ * Copyright (C) 2014 Canonical, Ltd.
+ *
+ * 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; version 3.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+    Example:
+
+    MainView {
+        objectName: "mainView"
+
+        applicationName: "com.ubuntu.developer.boiko.bottomedge"
+
+        width: units.gu(100)
+        height: units.gu(75)
+
+        Component {
+            id: pageComponent
+
+            PageWithBottomEdge {
+                id: mainPage
+                title: i18n.tr("Main Page")
+
+                Rectangle {
+                    anchors.fill: parent
+                    color: "white"
+                }
+
+                bottomEdgePageComponent: Page {
+                    title: "Contents"
+                    anchors.fill: parent
+                    //anchors.topMargin: contentsPage.flickable.contentY
+
+                    ListView {
+                        anchors.fill: parent
+                        model: 50
+                        delegate: ListItems.Standard {
+                            text: "One Content Item: " + index
+                        }
+                    }
+                }
+                bottomEdgeTitle: i18n.tr("Bottom edge action")
+            }
+        }
+
+        PageStack {
+            id: stack
+            Component.onCompleted: stack.push(pageComponent)
+        }
+    }
+
+*/
+
+import QtQuick 2.2
+import Ubuntu.Components 1.1
+
+Page {
+    id: page
+
+    property alias bottomEdgePageComponent: edgeLoader.sourceComponent
+    property alias bottomEdgePageSource: edgeLoader.source
+    property alias bottomEdgeTitle: tipLabel.text
+    property bool bottomEdgeEnabled: true
+    property int bottomEdgeExpandThreshold: page.height * 0.2
+    property int bottomEdgeExposedArea: bottomEdge.state !== "expanded" ? (page.height - bottomEdge.y - bottomEdge.tipHeight) : _areaWhenExpanded
+    property bool reloadBottomEdgePage: true
+
+    readonly property alias bottomEdgePage: edgeLoader.item
+    readonly property bool isReady: ((bottomEdge.y === 0) && bottomEdgePageLoaded && edgeLoader.item.active)
+    readonly property bool isCollapsed: (bottomEdge.y === page.height)
+    readonly property bool bottomEdgePageLoaded: (edgeLoader.status == Loader.Ready)
+
+    property bool _showEdgePageWhenReady: false
+    property int _areaWhenExpanded: 0
+
+    // CUSTOM properties to allow changing of color
+    property alias tipColor: tip.color
+    property alias tipLabelColor: tipLabel.color
+
+    signal bottomEdgeReleased()
+    signal bottomEdgeDismissed()
+
+
+    function showBottomEdgePage(source, properties)
+    {
+        edgeLoader.setSource(source, properties)
+        _showEdgePageWhenReady = true
+    }
+
+    function setBottomEdgePage(source, properties)
+    {
+        edgeLoader.setSource(source, properties)
+    }
+
+    function _pushPage()
+    {
+        if (edgeLoader.status === Loader.Ready) {
+            edgeLoader.item.active = true
+            page.pageStack.push(edgeLoader.item)
+            if (edgeLoader.item.flickable) {
+                edgeLoader.item.flickable.contentY = -page.header.height
+                edgeLoader.item.flickable.returnToBounds()
+            }
+            if (edgeLoader.item.ready)
+                edgeLoader.item.ready()
+        }
+    }
+
+
+    Component.onCompleted: {
+        // avoid a binding on the expanded height value
+        var expandedHeight = height;
+        _areaWhenExpanded = expandedHeight;
+    }
+
+    onActiveChanged: {
+        if (active) {
+            bottomEdge.state = "collapsed"
+        }
+    }
+
+    onBottomEdgePageLoadedChanged: {
+        if (_showEdgePageWhenReady && bottomEdgePageLoaded) {
+            bottomEdge.state = "expanded"
+            _showEdgePageWhenReady = false
+        }
+    }
+
+    Rectangle {
+        id: bgVisual
+
+        color: "black"
+        anchors.fill: page
+        opacity: 0.7 * ((page.height - bottomEdge.y) / page.height)
+        z: 1
+    }
+
+    UbuntuShape {
+        id: tip
+        objectName: "bottomEdgeTip"
+
+        property bool hidden: (activeFocus === false) || ((bottomEdge.y - units.gu(1)) < tip.y)
+
+        enabled: mouseArea.enabled
+        visible: page.bottomEdgeEnabled
+        anchors {
+            bottom: parent.bottom
+            horizontalCenter: bottomEdge.horizontalCenter
+            bottomMargin: hidden ? - height + units.gu(1) : -units.gu(1)
+            Behavior on bottomMargin {
+                SequentialAnimation {
+                    // wait some msecs in case of the focus change again, to avoid flickering
+                    PauseAnimation {
+                        duration: 300
+                    }
+                    UbuntuNumberAnimation {
+                        duration: UbuntuAnimation.SnapDuration
+                    }
+                }
+            }
+        }
+
+        z: 1
+        width: tipLabel.paintedWidth + units.gu(6)
+        height: bottomEdge.tipHeight + units.gu(1)
+        color: Theme.palette.normal.overlay
+        Label {
+            id: tipLabel
+
+            anchors {
+                top: parent.top
+                left: parent.left
+                right: parent.right
+            }
+            height: bottomEdge.tipHeight
+            verticalAlignment: Text.AlignVCenter
+            horizontalAlignment: Text.AlignHCenter
+            opacity: tip.hidden ? 0.0 : 1.0
+            Behavior on opacity {
+                UbuntuNumberAnimation {
+                    duration: UbuntuAnimation.SnapDuration
+                }
+            }
+        }
+    }
+
+    Rectangle {
+        id: shadow
+
+        anchors {
+            left: parent.left
+            right: parent.right
+            bottom: parent.bottom
+        }
+        height: units.gu(1)
+        z: 1
+        opacity: 0.0
+        gradient: Gradient {
+            GradientStop { position: 0.0; color: "transparent" }
+            GradientStop { position: 1.0; color: Qt.rgba(0, 0, 0, 0.2) }
+        }
+    }
+
+    MouseArea {
+        id: mouseArea
+
+        property real previousY: -1
+        property string dragDirection: "None"
+
+        preventStealing: true
+        drag {
+            axis: Drag.YAxis
+            target: bottomEdge
+            minimumY: bottomEdge.pageStartY
+            maximumY: page.height
+        }
+        enabled: edgeLoader.status == Loader.Ready
+        visible: page.bottomEdgeEnabled
+
+        anchors {
+            left: parent.left
+            right: parent.right
+            bottom: parent.bottom
+
+        }
+        height: bottomEdge.tipHeight
+        z: 1
+
+        onReleased: {
+            page.bottomEdgeReleased()
+            if ((dragDirection === "BottomToTop") &&
+                bottomEdge.y < (page.height - bottomEdgeExpandThreshold - bottomEdge.tipHeight)) {
+                bottomEdge.state = "expanded"
+            } else {
+                bottomEdge.state = "collapsed"
+            }
+            previousY = -1
+            dragDirection = "None"
+        }
+
+        onPressed: {
+            previousY = mouse.y
+            tip.forceActiveFocus()
+        }
+
+        onMouseYChanged: {
+            var yOffset = previousY - mouseY
+            // skip if was a small move
+            if (Math.abs(yOffset) <= units.gu(2)) {
+                return
+            }
+            previousY = mouseY
+            dragDirection = yOffset > 0 ? "BottomToTop" : "TopToBottom"
+        }
+    }
+
+    Rectangle {
+        id: bottomEdge
+        objectName: "bottomEdge"
+
+        readonly property int tipHeight: units.gu(3)
+        readonly property int pageStartY: 0
+
+        z: 1
+        color: Theme.palette.normal.background
+        clip: true
+        anchors {
+            left: parent.left
+            right: parent.right
+        }
+        height: page.height
+        y: height
+        visible: !page.isCollapsed
+        state: "collapsed"
+        states: [
+            State {
+                name: "collapsed"
+                PropertyChanges {
+                    target: bottomEdge
+                    y: bottomEdge.height
+                }
+            },
+            State {
+                name: "expanded"
+                PropertyChanges {
+                    target: bottomEdge
+                    y: bottomEdge.pageStartY
+                }
+            },
+            State {
+                name: "floating"
+                when: mouseArea.drag.active
+                PropertyChanges {
+                    target: shadow
+                    opacity: 1.0
+                }
+            }
+        ]
+
+        transitions: [
+            Transition {
+                to: "expanded"
+                SequentialAnimation {
+                    alwaysRunToEnd: true
+
+                    SmoothedAnimation {
+                        target: bottomEdge
+                        property: "y"
+                        duration: UbuntuAnimation.FastDuration
+                        easing.type: Easing.Linear
+                    }
+                    SmoothedAnimation {
+                        target: edgeLoader
+                        property: "anchors.topMargin"
+                        to: - units.gu(4)
+                        duration: UbuntuAnimation.FastDuration
+                        easing.type: Easing.Linear
+                    }
+                    SmoothedAnimation {
+                        target: edgeLoader
+                        property: "anchors.topMargin"
+                        to: 0
+                        duration: UbuntuAnimation.FastDuration
+                        easing: UbuntuAnimation.StandardEasing
+                    }
+                    ScriptAction {
+                        script: page._pushPage()
+                    }
+                }
+            },
+            Transition {
+                from: "expanded"
+                to: "collapsed"
+                SequentialAnimation {
+                    alwaysRunToEnd: true
+
+                    ScriptAction {
+                        script: {
+                            Qt.inputMethod.hide()
+                            edgeLoader.item.parent = edgeLoader
+                            edgeLoader.item.anchors.fill = edgeLoader
+                            edgeLoader.item.active = false
+                        }
+                    }
+                    SmoothedAnimation {
+                        target: bottomEdge
+                        property: "y"
+                        duration: UbuntuAnimation.SlowDuration
+                    }
+                    ScriptAction {
+                        script: {
+                            // destroy current bottom page
+                            if (page.reloadBottomEdgePage) {
+                                edgeLoader.active = false
+                                // tip will receive focus on page active true
+                            } else {
+                                tip.forceActiveFocus()
+                            }
+
+                            // notify
+                            page.bottomEdgeDismissed()
+
+                            edgeLoader.active = true
+                        }
+                    }
+                }
+            },
+            Transition {
+                from: "floating"
+                to: "collapsed"
+                SmoothedAnimation {
+                    target: bottomEdge
+                    property: "y"
+                    duration: UbuntuAnimation.FastDuration
+                }
+            }
+        ]
+
+        Loader {
+            id: edgeLoader
+
+            asynchronous: true
+            anchors.fill: parent
+            //WORKAROUND: The SDK move the page contents down to allocate space for the header we need to avoid that during the page dragging
+            Binding {
+                target: edgeLoader.status === Loader.Ready ? edgeLoader : null
+                property: "anchors.topMargin"
+                value:  edgeLoader.item && edgeLoader.item.flickable ? edgeLoader.item.flickable.contentY : 0
+                when: !page.isReady
+            }
+
+            onLoaded: {
+                tip.forceActiveFocus()
+                if (page.isReady && edgeLoader.item.active !== true) {
+                    page._pushPage()
+                }
+            }
+        }
+    }
+}

=== added file 'app/components/WeatherListItem.qml'
--- app/components/WeatherListItem.qml	1970-01-01 00:00:00 +0000
+++ app/components/WeatherListItem.qml	2015-03-14 20:11:20 +0000
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2013, 2014, 2015
+ *      Andrew Hayzen <ahayzen@xxxxxxxxx>
+ *      Nekhelesh Ramananthan <krnekhelesh@xxxxxxxxx>
+ *      Victor Thompson <victor.thompson@xxxxxxxxx>
+ *
+ * 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; version 3.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.3
+import Ubuntu.Components 1.1
+import Ubuntu.Components.ListItems 0.1 as ListItem
+
+
+ListItemWithActions {
+    id: root
+
+    property int listItemIndex: index
+    property bool multiselectable: false
+    property int previousListItemIndex: -1
+    property bool reorderable: false
+
+    signal reorder(int from, int to)
+
+    onItemPressAndHold: {
+        if (multiselectable) {
+            selectionMode = true
+        }
+    }
+
+    onListItemIndexChanged: {
+        var i = parent.parent.selectedItems.lastIndexOf(previousListItemIndex)
+
+        if (i !== -1) {
+            parent.parent.selectedItems[i] = listItemIndex
+        }
+
+        previousListItemIndex = listItemIndex
+    }
+
+    onSelectedChanged: {
+        if (selectionMode) {
+            var tmp = parent.parent.selectedItems
+
+            if (selected) {
+                if (parent.parent.selectedItems.indexOf(listItemIndex) === -1) {
+                    tmp.push(listItemIndex)
+                    parent.parent.selectedItems = tmp
+                }
+            } else {
+                tmp.splice(parent.parent.selectedItems.indexOf(listItemIndex), 1)
+                parent.parent.selectedItems = tmp
+            }
+        }
+    }
+
+    onSelectionModeChanged: {
+        if (reorderable && selectionMode) {
+            resetSwipe()
+        }
+
+        for (var j=0; j < _main.children.length; j++) {
+            if (_main.children[j] !== actionReorderLoader) {
+                _main.children[j].anchors.rightMargin = reorderable && selectionMode ? actionReorderLoader.width + units.gu(2) : 0
+            }
+        }
+
+        parent.parent.state = selectionMode ? "multiselectable" : "normal"
+
+        if (!selectionMode) {
+            selected = false
+        }
+    }
+
+    /* Highlight the listitem on press */
+    Rectangle {
+        id: listItemBrighten
+        color: root.pressed ? UbuntuColors.coolGrey : "transparent"
+        opacity: 0.1
+        height: root.height
+        x: root.x - parent.x  // -parent.x due to selectionIcon in ListItemWithActions
+        width: root.width
+    }
+
+    /* Reorder Component */
+    Loader {
+        id: actionReorderLoader
+        active: reorderable && selectionMode && root.parent.parent.selectedItems.length === 0
+        anchors {
+            bottom: parent.bottom
+            right: parent.right
+            rightMargin: units.gu(1)
+            top: parent.top
+        }
+        asynchronous: true
+        source: "ListItemReorderComponent.qml"
+    }
+
+    Item {
+        Connections {  // Only allow one ListItem to be swiping at any time
+            target: weatherApp
+            onListItemSwiping: {
+                if (i !== index) {
+                    root.resetSwipe();
+                }
+            }
+        }
+
+        Connections {  // Connections from signals in the ListView
+            target: root.parent.parent
+            onClearSelection: selected = false
+            onFlickingChanged: {
+                if (root.parent.parent.flicking) {
+                    root.resetSwipe()
+                }
+            }
+            onSelectAll: selected = true
+            onStateChanged: selectionMode = root.parent.parent.state === "multiselectable"
+        }
+    }
+
+    Component.onCompleted: {  // reload settings as delegates are destroyed
+        if (parent.parent.selectedItems.indexOf(index) !== -1) {
+            selected = true
+        }
+
+        selectionMode = root.parent.parent.state === "multiselectable"
+    }
+}

=== added file 'app/components/WeatherListView.qml'
--- app/components/WeatherListView.qml	1970-01-01 00:00:00 +0000
+++ app/components/WeatherListView.qml	2015-03-14 20:11:20 +0000
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2013, 2014, 2015
+ *      Andrew Hayzen <ahayzen@xxxxxxxxx>
+ *      Daniel Holm <d.holmen@xxxxxxxxx>
+ *      Victor Thompson <victor.thompson@xxxxxxxxx>
+ *
+ * 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; version 3.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.3
+import Ubuntu.Components 1.1
+
+
+ListView {
+    Component.onCompleted: {
+        // FIXME: workaround for qtubuntu not returning values depending on the grid unit definition
+        // for Flickable.maximumFlickVelocity and Flickable.flickDeceleration
+        var scaleFactor = units.gridUnit / 8;
+        maximumFlickVelocity = maximumFlickVelocity * scaleFactor;
+        flickDeceleration = flickDeceleration * scaleFactor;
+    }
+}

=== added directory 'app/data'
=== added file 'app/data/CMakeLists.txt'
--- app/data/CMakeLists.txt	1970-01-01 00:00:00 +0000
+++ app/data/CMakeLists.txt	2015-03-14 20:11:20 +0000
@@ -0,0 +1,5 @@
+file(GLOB DATA_QML_JS_FILES *.qml *.js)
+
+add_custom_target(ubuntu-weather-app_data_QMlFiles ALL SOURCES ${DATA_QML_JS_FILES})
+
+install(FILES ${DATA_QML_JS_FILES} DESTINATION ${UBUNTU-WEATHER_APP_DIR}/data)

=== added file 'app/data/CitiesList.js'
--- app/data/CitiesList.js	1970-01-01 00:00:00 +0000
+++ app/data/CitiesList.js	2015-03-14 20:11:20 +0000
@@ -0,0 +1,65 @@
+.pragma library
+/*
+ * Copyright (C) 2013 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Authored by: Martin Borho <martin@xxxxxxxxx>
+ */
+
+var preList = [
+    {"coord":{"lon":"4.88969","lat":"52.37403"},"timezone":{"gmtOffset":1,"dstOffset":2,"timeZoneId":"Europe/Amsterdam"},"population":741636,"adminName2":"Gemeente Amsterdam","name":"Amsterdam","country":"NL","countryName":"Netherlands","adminName1":"North Holland","adminName3":"","services":{"geonames":2759794},"areaLabel":"North Holland, Netherlands"},
+    {"coord":{"lon":"116.39723","lat":"39.9075"},"timezone":{"gmtOffset":8,"dstOffset":8,"timeZoneId":"Asia/Shanghai"},"population":7480601,"adminName2":"","name":"Beijing","country":"CN","countryName":"China","adminName1":"Beijing","adminName3":"","services":{"geonames":1816670},"areaLabel":"Beijing, China"},
+    {"coord":{"lon":"-74.08175","lat":"4.60971"},"timezone":{"gmtOffset":-5,"dstOffset":-5,"timeZoneId":"America/Bogota"},"population":7674366,"adminName2":"","name":"Bogotá","country":"CO","countryName":"Colombia","adminName1":"Bogota D.C.","adminName3":"","services":{"geonames":3688689},"areaLabel":"Bogota D.C., Colombia"},
+    {"coord":{"lon":"-58.37723","lat":"-34.61315"},"timezone":{"gmtOffset":-3,"dstOffset":-3,"timeZoneId":"America/Argentina/Buenos_Aires"},"population":13076300,"adminName2":"","name":"Buenos Aires","country":"AR","countryName":"Argentina","adminName1":"Buenos Aires F.D.","adminName3":"","services":{"geonames":3435910},"areaLabel":"Buenos Aires F.D., Argentina"},
+    {"coord":{"lon":"31.24967","lat":"30.06263"},"timezone":{"gmtOffset":2,"dstOffset":2,"timeZoneId":"Africa/Cairo"},"population":7734614,"adminName2":"","name":"Cairo","country":"EG","countryName":"Egypt","adminName1":"Al Qāhirah","adminName3":"","services":{"geonames":360630},"areaLabel":"Al Qāhirah, Egypt"},
+    {"coord":{"lon":"-66.87919","lat":"10.48801"},"timezone":{"gmtOffset":-4.5,"dstOffset":-4.5,"timeZoneId":"America/Caracas"},"population":3000000,"adminName2":"Municipio Libertador","name":"Caracas","country":"VE","countryName":"Venezuela","adminName1":"Distrito Federal","adminName3":"","services":{"geonames":3646738},"areaLabel":"Distrito Federal, Venezuela, Bolivarian Republic of"},
+    {"coord":{"lon":"-87.65005","lat":"41.85003"},"timezone":{"gmtOffset":-6,"dstOffset":-5,"timeZoneId":"America/Chicago"},"population":2695598,"adminName2":"Cook County","name":"Chicago","country":"US","countryName":"United States","adminName1":"Illinois","adminName3":"","services":{"geonames":4887398},"areaLabel":"Illinois, United States"},
+    {"coord":{"lon":"77.22897","lat":"28.65381"},"timezone":{"gmtOffset":5.5,"dstOffset":5.5,"timeZoneId":"Asia/Kolkata"},"population":10927986,"adminName2":"","name":"Dehli","country":"IN","countryName":"India","adminName1":"NCT","adminName3":"","services":{"geonames":1273294},"areaLabel":"NCT, India"},
+    {"coord":{"lon":"55.30472","lat":"25.25817"},"timezone":{"gmtOffset":4,"dstOffset":4,"timeZoneId":"Asia/Dubai"},"population":1137347,"adminName2":"","name":"Dubai","country":"AE","countryName":"United Arab Emirates","adminName1":"Dubai","adminName3":"","services":{"geonames":292223},"areaLabel":"Dubai, United Arab Emirates"},
+    {"coord":{"lon":"113.25","lat":"23.11667"},"timezone":{"gmtOffset":8,"dstOffset":8,"timeZoneId":"Asia/Shanghai"},"population":3152825,"adminName2":"","name":"Guangzhou","country":"CN","countryName":"China","adminName1":"Guangdong Province","adminName3":"","services":{"geonames":1809858},"areaLabel":"Guangdong Province, China"},
+    {"coord":{"lon":"10.01534","lat":"53.57532"},"timezone":{"gmtOffset":1,"dstOffset":2,"timeZoneId":"Europe/Berlin"},"population":1739117,"adminName2":"","name":"Hamburg","country":"DE","countryName":"Germany","adminName1":"Hamburg","adminName3":"","services":{"geonames":2911298},"areaLabel":"Hamburg, Germany"},
+    {"coord":{"lon":"-95.36327","lat":"29.76328"},"timezone":{"gmtOffset":-6,"dstOffset":-5,"timeZoneId":"America/Chicago"},"population":2099451,"adminName2":"Harris County","name":"Houston","country":"US","countryName":"United States","adminName1":"Texas","adminName3":"","services":{"geonames":4699066},"areaLabel":"Texas, United States"},
+    {"coord":{"lon":"28.94966","lat":"41.01384"},"timezone":{"gmtOffset":2,"dstOffset":3,"timeZoneId":"Europe/Istanbul"},"population":11174257,"adminName2":"","name":"Istanbul","country":"TR","countryName":"Turkey","adminName1":"Istanbul","adminName3":"","services":{"geonames":745044},"areaLabel":"Istanbul, Turkey"},
+    {"coord":{"lon":"106.84513","lat":"-6.21462"},"timezone":{"gmtOffset":7,"dstOffset":7,"timeZoneId":"Asia/Jakarta"},"population":8540121,"adminName2":"","name":"Jakarta","country":"ID","countryName":"Indonesia","adminName1":"Jakarta Raya","adminName3":"","services":{"geonames":1642911},"areaLabel":"Jakarta Raya, Indonesia"},
+    {"coord":{"lon":"28.04363","lat":"-26.20227"},"timezone":{"gmtOffset":2,"dstOffset":2,"timeZoneId":"Africa/Johannesburg"},"population":2026469,"adminName2":"City of Johannesburg Metropolitan Municipality","name":"Johannesburg","country":"ZA","countryName":"South Africa","adminName1":"Gauteng","adminName3":"City of Johannesburg","services":{"geonames":993800},"areaLabel":"Gauteng, South Africa"},
+    {"coord":{"lon":"67.0822","lat":"24.9056"},"timezone":{"gmtOffset":5,"dstOffset":5,"timeZoneId":"Asia/Karachi"},"population":11624219,"adminName2":"Karāchi District","name":"Karachi","country":"PK","countryName":"Pakistan","adminName1":"Sindh","adminName3":"","services":{"geonames":1174872},"areaLabel":"Sindh, Pakistan"},
+    {"coord":{"lon":"15.30807","lat":"-4.32142"},"timezone":{"gmtOffset":1,"dstOffset":1,"timeZoneId":"Africa/Kinshasa"},"population":7785965,"adminName2":"","name":"Kinshasa","country":"CD","countryName":"Democratic Republic of the Congo","adminName1":"Kinshasa","adminName3":"","services":{"geonames":2314302},"areaLabel":"Kinshasa, Congo, the Democratic Republic of the"},
+    {"coord":{"lon":"88.36304","lat":"22.56263"},"timezone":{"gmtOffset":5.5,"dstOffset":5.5,"timeZoneId":"Asia/Kolkata"},"population":4631392,"adminName2":"","name":"Kolkata","country":"IN","countryName":"India","adminName1":"Bengal","adminName3":"","services":{"geonames":1275004},"areaLabel":"Bengal, India"},
+    {"coord":{"lon":"3.39583","lat":"6.45306"},"timezone":{"gmtOffset":1,"dstOffset":1,"timeZoneId":"Africa/Lagos"},"population":9000000,"adminName2":"","name":"Lagos","country":"NG","countryName":"Nigeria","adminName1":"Lagos","adminName3":"","services":{"geonames":2332459},"areaLabel":"Lagos, Nigeria"},
+    {"coord":{"lon":"-77.02824","lat":"-12.04318"},"timezone":{"gmtOffset":-5,"dstOffset":-5,"timeZoneId":"America/Lima"},"population":7737002,"adminName2":"","name":"Lima","country":"PE","countryName":"Peru","adminName1":"Provincia de Lima","adminName3":"","services":{"geonames":3936456},"areaLabel":"Provincia de Lima, Peru"},
+    {"coord":{"lon":"-0.12574","lat":"51.50853"},"timezone":{"gmtOffset":0,"dstOffset":1,"timeZoneId":"Europe/London"},"population":7556900,"adminName2":"Greater London","name":"London","country":"GB","countryName":"United Kingdom","adminName1":"England","adminName3":"","services":{"geonames":2643743},"areaLabel":"England, Greater London, United Kingdom"},
+    {"coord":{"lon":"-118.24368","lat":"34.05223"},"timezone":{"gmtOffset":-8,"dstOffset":-7,"timeZoneId":"America/Los_Angeles"},"population":3792621,"adminName2":"Los Angeles County","name":"Los Angeles","country":"US","countryName":"United States","adminName1":"California","adminName3":"","services":{"geonames":5368361},"areaLabel":"California, United States"},
+    {"coord":{"lon":"-3.70256","lat":"40.4165"},"timezone":{"gmtOffset":1,"dstOffset":2,"timeZoneId":"Europe/Madrid"},"population":3255944,"adminName2":"Madrid","name":"Madrid","country":"ES","countryName":"Spain","adminName1":"Madrid","adminName3":"Madrid","services":{"geonames":3117735},"areaLabel":"Madrid, Spain"},
+    {"coord":{"lon":"120.9822","lat":"14.6042"},"timezone":{"gmtOffset":8,"dstOffset":8,"timeZoneId":"Asia/Manila"},"population":10444527,"adminName2":"PH.NCR.D9","name":"Manila","country":"PH","countryName":"Philippines","adminName1":"National Capital Region","adminName3":"","services":{"geonames":1701668},"areaLabel":"National Capital, Philippines"},
+    {"coord":{"lon":"-73.58781","lat":"45.50884"},"timezone":{"gmtOffset":-5,"dstOffset":-4,"timeZoneId":"America/Montreal"},"population":3268513,"adminName2":"Montréal","name":"Montreal","country":"CA","countryName":"Canada","adminName1":"Quebec","adminName3":"","services":{"geonames":6077243},"areaLabel":"Quebec, Canada"},
+    {"coord":{"lon":"-99.12766","lat":"19.42847"},"timezone":{"gmtOffset":-6,"dstOffset":-5,"timeZoneId":"America/Mexico_City"},"population":12294193,"adminName2":"","name":"Mexico City","country":"MX","countryName":"Mexico","adminName1":"The Federal District","adminName3":"","services":{"geonames":3530597},"areaLabel":"The Federal District, Mexico"},
+    {"coord":{"lon":"37.61556","lat":"55.75222"},"timezone":{"gmtOffset":4,"dstOffset":4,"timeZoneId":"Europe/Moscow"},"population":10381222,"adminName2":"","name":"Moscow","country":"RU","countryName":"Russia","adminName1":"Moscow","adminName3":"","services":{"geonames":524901},"areaLabel":"Moscow, Russian Federation"},
+    {"coord":{"lon":"72.88261","lat":"19.07283"},"timezone":{"gmtOffset":5.5,"dstOffset":5.5,"timeZoneId":"Asia/Kolkata"},"population":12691836,"adminName2":"Konkan Division","name":"Mumbai","country":"IN","countryName":"India","adminName1":"Mahārāshtra","adminName3":"","services":{"geonames":1275339},"areaLabel":"Mahārāshtra, India"},
+    {"coord":{"lon":"36.81667","lat":"-1.28333"},"timezone":{"gmtOffset":3,"dstOffset":3,"timeZoneId":"Africa/Nairobi"},"population":2750547,"adminName2":"","name":"Nairobi","country":"KE","countryName":"Kenya","adminName1":"Nairobi Area","adminName3":"","services":{"geonames":184745},"areaLabel":"Nairobi Area, Kenya"},
+    {"coord":{"lon":"-74.00597","lat":"40.71427"},"timezone":{"gmtOffset":-5,"dstOffset":-4,"timeZoneId":"America/New_York"},"population":8175133,"adminName2":"","name":"New York City","country":"US","countryName":"United States","adminName1":"New York","adminName3":"","services":{"geonames":5128581},"areaLabel":"New York, United States"},
+    {"coord":{"lon":"135.50218","lat":"34.69374"},"timezone":{"gmtOffset":9,"dstOffset":9,"timeZoneId":"Asia/Tokyo"},"population":2592413,"adminName2":"","name":"Osaka","country":"JP","countryName":"Japan","adminName1":"Ōsaka","adminName3":"","services":{"geonames":1853909},"areaLabel":"Ōsaka, Japan"},
+    {"coord":{"lon":"-43.2075","lat":"-22.90278"},"timezone":{"gmtOffset":-2,"dstOffset":-3,"timeZoneId":"America/Sao_Paulo"},"population":6023699,"adminName2":"Rio de Janeiro","name":"Rio de Janeiro","country":"BR","countryName":"Brazil","adminName1":"Rio de Janeiro","adminName3":"","services":{"geonames":3451190},"areaLabel":"Rio de Janeiro, Brazil"},
+    {"coord":{"lon":"12.4839","lat":"41.89474"},"timezone":{"gmtOffset":1,"dstOffset":2,"timeZoneId":"Europe/Rome"},"population":2563241,"adminName2":"Rome","name":"Rome","country":"IT","countryName":"Italy","adminName1":"Latium","adminName3":"Rome","services":{"geonames":3169070},"areaLabel":"Latium, Italy"},
+    {"coord":{"lon":"126.97783","lat":"37.56826"},"timezone":{"gmtOffset":9,"dstOffset":9,"timeZoneId":"Asia/Seoul"},"population":10349312,"adminName2":"","name":"Seoul","country":"KR","countryName":"South Korea","adminName1":"Seoul","adminName3":"","services":{"geonames":1835848},"areaLabel":"Seoul, Korea, Republic of"},
+    {"coord":{"lon":"-122.41942","lat":"37.77493"},"timezone":{"gmtOffset":-8,"dstOffset":-7,"timeZoneId":"America/Los_Angeles"},"population":805235,"adminName2":"San Francisco County","name":"San Francisco","country":"US","countryName":"United States","adminName1":"California","adminName3":"","services":{"geonames":5391959},"areaLabel":"California, United States"},
+    {"coord":{"lon":"-46.63611","lat":"-23.5475"},"timezone":{"gmtOffset":-2,"dstOffset":-3,"timeZoneId":"America/Sao_Paulo"},"population":10021295,"adminName2":"São Paulo","name":"São Paulo","country":"BR","countryName":"Brazil","adminName1":"São Paulo","adminName3":"","services":{"geonames":3448439},"areaLabel":"São Paulo, Brazil"},
+    {"coord":{"lon":"121.45806","lat":"31.22222"},"timezone":{"gmtOffset":8,"dstOffset":8,"timeZoneId":"Asia/Shanghai"},"population":14608512,"adminName2":"","name":"Shanghai","country":"CN","countryName":"China","adminName1":"Shanghai Shi","adminName3":"","services":{"geonames":1796236},"areaLabel":"Shanghai Shi, China"},
+    {"coord":{"lon":"114.0683","lat":"22.54554"},"timezone":{"gmtOffset":8,"dstOffset":8,"timeZoneId":"Asia/Shanghai"},"population":3000000,"adminName2":"Shenzhen","name":"Shenzhen","country":"CN","countryName":"China","adminName1":"Guangdong Province","adminName3":"","services":{"geonames":1795565},"areaLabel":"Guangdong Province, Shenzhen, China"},
+    {"coord":{"lon":"103.85007","lat":"1.28967"},"timezone":{"gmtOffset":8,"dstOffset":8,"timeZoneId":"Asia/Singapore"},"population":3547809,"adminName2":"","name":"Singapore","country":"SG","countryName":"Singapore","adminName1":"","adminName3":"","services":{"geonames":1880252},"areaLabel":"Singapore"},
+    {"coord":{"lon":"151.20732","lat":"-33.86785"},"timezone":{"gmtOffset":11,"dstOffset":10,"timeZoneId":"Australia/Sydney"},"population":4627345,"adminName2":"City of Sydney","name":"Sydney","country":"AU","countryName":"Australia","adminName1":"New South Wales","adminName3":"","services":{"geonames":2147714},"areaLabel":"New South Wales, Australia"},
+    {"coord":{"lon":"51.42151","lat":"35.69439"},"timezone":{"gmtOffset":3.5,"dstOffset":4.5,"timeZoneId":"Asia/Tehran"},"population":7153309,"adminName2":"","name":"Tehran","country":"IR","countryName":"Iran","adminName1":"Tehrān","adminName3":"","services":{"geonames":112931},"areaLabel":"Tehrān, Iran, Islamic Republic of"},
+    {"coord":{"lon":"117.17667","lat":"39.14222"},"timezone":{"gmtOffset":8,"dstOffset":8,"timeZoneId":"Asia/Shanghai"},"population":3766207,"adminName2":"","name":"Tianjin","country":"CN","countryName":"China","adminName1":"Tianjin Shi","adminName3":"","services":{"geonames":1792947},"areaLabel":"Tianjin Shi, China"},
+    {"coord":{"lon":"139.69171","lat":"35.6895"},"timezone":{"gmtOffset":9,"dstOffset":9,"timeZoneId":"Asia/Tokyo"},"population":8336599,"adminName2":"","name":"Tokyo","country":"JP","countryName":"Japan","adminName1":"Tōkyō","adminName3":"","services":{"geonames":1850147},"areaLabel":"Tōkyō, Japan"},
+    {"coord":{"lon":"-79.4163","lat":"43.70011"},"timezone":{"gmtOffset":-5,"dstOffset":-4,"timeZoneId":"America/Toronto"},"population":4612191,"adminName2":"","name":"Toronto","country":"CA","countryName":"Canada","adminName1":"Ontario","adminName3":"","services":{"geonames":6167865},"areaLabel":"Ontario, Canada"}
+]

=== added file 'app/data/Storage.qml'
--- app/data/Storage.qml	1970-01-01 00:00:00 +0000
+++ app/data/Storage.qml	2015-03-14 20:11:20 +0000
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2013, 2014, 2015 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Authored by: Martin Borho <martin@xxxxxxxxx>
+ */
+import QtQuick.LocalStorage 2.0
+import QtQuick 2.3
+
+Item {
+    property var db: null
+
+    function openDB() {
+        if(db !== null) return;
+
+        db = LocalStorage.openDatabaseSync("com.ubuntu.weather", "", "Default Ubuntu weather app", 100000);
+
+        if (db.version === "") {
+            db.changeVersion("", "0.1",
+                function(tx) {
+                    tx.executeSql('CREATE TABLE IF NOT EXISTS Locations(id INTEGER PRIMARY KEY AUTOINCREMENT, data TEXT, date TEXT)');
+                    console.log('Database created');
+                });
+            // reopen database with new version number
+            db = LocalStorage.openDatabaseSync("com.ubuntu.weather", "", "Default Ubuntu weather app", 100000);
+        }
+
+        if(db.version === "0.1") {
+            db.changeVersion("0.1", "0.2",
+                function(tx) {
+                    tx.executeSql('CREATE TABLE IF NOT EXISTS settings(key TEXT UNIQUE, value TEXT)');
+                    console.log('Settings table added, Database upgraded to v0.2');
+                });
+            // reopen database with new version number
+            db = LocalStorage.openDatabaseSync("com.ubuntu.weather", "", "Default Ubuntu weather app", 100000);
+        }
+
+        if(db.version === "0.2") {
+            db.changeVersion("0.2", "0.3",
+                function(tx) {
+                    tx.executeSql('DELETE FROM Locations WHERE 1');
+                    console.log('Removed old locations, Database upgraded to v0.3');
+                });
+        }
+
+        if (!settings.migrated) {
+            try {  // attempt to read the old settings
+                var oldSettings = {};
+
+                // Load old settings
+                db.readTransaction( function(tx) {
+                    var rs = tx.executeSql("SELECT * FROM settings")
+
+                    for(var i = 0; i < rs.rows.length; i++) {
+                        var row = rs.rows.item(i);
+                        oldSettings[row.key] = row.value;
+                    }
+                });
+
+                console.debug("Migrating old data:", JSON.stringify(oldSettings))
+
+                // Move to new Settings API
+                settings.migrated = true
+                settings.precipUnits = oldSettings["precip_units"]
+                settings.service = oldSettings["service"]
+                settings.tempScale = "°" + (oldSettings["units"] === "metric" ? "C" : "F")
+                settings.units = oldSettings["units"]
+                settings.windUnits = oldSettings["wind_units"]
+
+                /*
+                  TODO: uncomment when reboot is ready to replace existing app
+                db.transaction( function(tx) {
+                    tx.executeSql("DROP TABLE IF EXISTS settings")
+                });
+                */
+            } catch (e) {  // likely table did not exist
+                console.debug("No old data to migrate.")
+                settings.migrated = true
+            }
+        }
+    }
+
+    function insertLocation(data) {
+        openDB();
+        var res;
+
+        db.transaction( function(tx){
+            var r = tx.executeSql('INSERT INTO Locations(data, date) VALUES(?, ?)', [JSON.stringify(data), new Date().getTime()]);
+            res = r.insertId;
+        });
+        return res;
+    }
+
+    function updateLocation(dbId, data) {
+        openDB();
+        db.transaction( function(tx){
+            var r = tx.executeSql('UPDATE Locations SET data = ?, date=? WHERE id = ?', [JSON.stringify(data), new Date().getTime(), dbId])
+        });
+    }
+
+    function getLocations(callback) {
+        openDB();
+        db.readTransaction(
+            function(tx){
+                var locations = [];
+                var rs = tx.executeSql('SELECT * FROM Locations');
+                for(var i = 0; i < rs.rows.length; i++) {
+                    var row = rs.rows.item(i),
+                        locData = JSON.parse(row.data);
+                    locData["updated"] = parseInt(row.date, 10);
+                    locData["db"] = {id: row.id, updated: new Date(parseInt(row.date, 10))};
+                    locations.push(locData);
+                }
+                callback(locations);
+            }
+        );
+    }
+
+    function clearLocation(location_id) {
+        openDB();
+        db.transaction(function(tx){
+            tx.executeSql('DELETE FROM Locations WHERE id = ?', [location_id]);
+        });
+    }
+
+    function clearMultiLocation(locations) {
+        openDB();
+
+        db.transaction(function (tx) {
+            // Remove all the deleted indexes
+            for (var i=0; i < locations.length; i++) {
+                tx.executeSql('DELETE FROM Locations WHERE id=?;', [locations[i]])
+            }
+
+            // Rebuild locations in order
+            var rs = tx.executeSql('SELECT id FROM Locations ORDER BY id ASC')
+
+            for (i=0; i < rs.rows.length; i++) {
+                tx.executeSql('UPDATE Locations SET id=? WHERE id=?;',
+                              [i, rs.rows.item(i).id])
+            }
+        })
+    }
+
+    function clearDB() { // for dev purposes
+        openDB();
+        db.transaction(function(tx){
+            tx.executeSql('DELETE FROM Locations WHERE 1');
+        });
+    }
+
+    function reorder(from, to) {
+        openDB();
+
+        db.transaction(function(tx) {
+            // Track to move put as -1 for now
+            tx.executeSql('UPDATE Locations SET id=? WHERE id=?;',
+                          [-1, from])
+
+            // Shuffle locations inbetween from->to
+            if (from > to) {
+                for (var i = from-1; i >= to; i--) {
+                    tx.executeSql('UPDATE Locations SET id=? WHERE id=?;',
+                                  [i+1, i])
+                }
+            } else {
+                for (var j = from+1; j <= to; j++) {
+                    tx.executeSql('UPDATE Locations SET id=? WHERE id=?;',
+                                  [j-1, j])
+                }
+            }
+
+            // Switch moving location to its new position
+            tx.executeSql('UPDATE Locations SET id=? WHERE id=?;',
+                          [to, -1])
+        })
+    }
+}

=== added file 'app/data/WeatherApi.js'
--- app/data/WeatherApi.js	1970-01-01 00:00:00 +0000
+++ app/data/WeatherApi.js	2015-03-14 20:11:20 +0000
@@ -0,0 +1,750 @@
+.pragma library
+/*
+ * Copyright (C) 2013 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Authored by: Raúl Yeguas <neokore@xxxxxxxxx>
+ *              Martin Borho <martin@xxxxxxxxx>
+ *              Andrew Starr-Bochicchio <a.starr.b@xxxxxxxxx>
+ */
+
+/**
+*  Version of the response data format.
+*  Increase this number to force a refresh.
+*/
+var RESPONSE_DATA_VERSION = 20150307;
+
+/**
+* Helper functions
+*/
+function debug(obj) {
+    print(JSON.stringify(obj))
+}
+//
+function calcFahrenheit(celsius) {
+        return celsius * 1.8 + 32;
+}
+//
+function calcMph(ms) {
+    return ms*2.24;
+}
+//
+function calcInch(mm) {
+    return mm/25.4;
+}
+//
+function calcKmh(ms) {
+    return ms*3.6;
+}
+//
+function convertKmhToMph(kmh) {
+    return kmh*0.621;
+}
+//
+function calcWindDir(degrees) {
+    var direction =  "?";
+    if(degrees >=0 && degrees <= 30){
+        direction = "N";
+    } else if(degrees >30 && degrees <= 60){
+        direction = "NE";
+    } else if(degrees >60 && degrees <= 120){
+        direction = "E";
+    } else if(degrees >120 && degrees <= 150){
+        direction = "SE";
+    } else if(degrees >150 && degrees <= 210){
+        direction = "S";
+    } else if(degrees >210 && degrees <= 240){
+        direction = "SW";
+    } else if(degrees >240 && degrees <= 300){
+        direction = "W";
+    } else if(degrees >300 && degrees <= 330){
+        direction = "NW";
+    } else if(degrees >330 && degrees <= 360){
+        direction = "N";
+    }
+    return direction;
+}
+
+//
+function getLocationTime(tstamp) {
+    var locTime = new Date(tstamp);
+    return {
+        year: locTime.getUTCFullYear(),
+        month: locTime.getUTCMonth(),
+        date: locTime.getUTCDate(),
+        hours: locTime.getUTCHours(),
+        minutes: locTime.getUTCMinutes()
+    }
+}
+// Serialize a JavaScript object to URL parameters
+// E.g. {param1: value1, param2: value2} to "param1=value&param2=value"
+// TODO: it'd be nice to make it work with either passing a single object
+// or several at once
+function parameterize(obj) {
+  var str = [];
+  for(var param in obj) {
+     str.push(encodeURIComponent(param) + "=" + encodeURIComponent(obj[param]));
+  }
+  return str.join("&");
+}
+
+var GeoipApi = (function() {
+    var _baseUrl = "http://geoip.ubuntu.com/lookup";;
+    return {
+        getLatLong: function(params, apiCaller, onSuccess, onError) {
+            var request = { type: "geolookup",url: _baseUrl},
+                resultHandler = (function(request, xmlDoc) {
+                    var coords = {},
+                        childNodes = xmlDoc.childNodes;
+                    for(var i=0;i<childNodes.length;i++) {
+                        if(childNodes[i].nodeName === "Latitude") {
+                            coords.lat = childNodes[i].firstChild.nodeValue;
+                        } else if(childNodes[i].nodeName === "Longitude") {
+                            coords.lon = childNodes[i].firstChild.nodeValue;
+                        }
+                    }
+                    onSuccess(coords);
+                }),
+                retryHandler = (function(err) {
+                    console.log("geolookup retry of "+err.request.url);
+                    apiCaller(request, resultHandler, onError);
+                });
+            apiCaller(request, resultHandler, retryHandler);
+        }
+    }
+})();
+
+var GeonamesApi = (function() {
+    /**
+      provides neccessary methods for requesting and preparing data from Geonames.org
+    */
+    var _baseUrl = "http://api.geonames.org/";;
+    var _username = "uweatherdev"
+    var _addParams = "&maxRows=25&featureClass=P"
+    //
+    function _buildSearchResult(request, data) {
+        var searchResult = { locations: [], request: request };
+        if(data.geonames) {
+            data.geonames.forEach(function(r) {
+               searchResult.locations.push({
+                    name: r.name,
+                    coord: {lat: r.lat, lon: r.lng},
+                    country: r.countryCode,
+                    countryName: r.countryName,
+                    timezone: r.timezone,
+                    adminName1: r.adminName1,
+                    adminName2: r.adminName2,
+                    adminName3: r.adminName3,
+                    population: r.population,
+                    services: {
+                        "geonames": r.geonameId
+                    }
+                });
+            })
+        }
+        return searchResult;
+    }
+    //
+    return {
+        //
+        search: function(mode, params, apiCaller, onSuccess, onError) {
+            var request,
+                retryHandler = (function(err) {
+                        console.log("search retry of "+err.request.url);
+                        apiCaller(request, searchResponseHandler, onError);
+                }),
+                searchResponseHandler = function(request, data) {
+                    onSuccess(_buildSearchResult(request, data));
+                };
+            if(mode === "point") {
+                request = { type: "search",
+                            url: _baseUrl+ "findNearbyPlaceNameJSON?style=full&username="+encodeURIComponent(_username)
+                            +"&lat="+encodeURIComponent(params.coords.lat)+"&lng="+encodeURIComponent(params.coords.lon)
+                            +_addParams}
+            } else {
+                request = { type: "search",
+                            url: _baseUrl+ "searchJSON?style=full&username="+encodeURIComponent(_username)
+                                    +"&name_startsWith="+encodeURIComponent(params.name)+_addParams}
+            }
+            apiCaller(request, searchResponseHandler, retryHandler);
+        }
+    }
+
+})();
+
+var OpenWeatherMapApi = (function() {
+    /**
+      provides neccessary methods for requesting and preparing data from OpenWeatherMap.org
+    */
+    var _baseUrl = "http://api.openweathermap.org/data/2.5/";;
+    //
+    var _serviceName = "openweathermap";
+    //
+    var _icon_map = {
+        "01d": "sun",
+        "01n": "moon",
+        "02d": "cloud_sun",
+        "02n": "cloud_moon",
+        "03d": "cloud_sun",
+        "03n": "cloud_moon",
+        "04d": "cloud",
+        "04n": "cloud",
+        "09d": "rain",
+        "09n": "rain",
+        "10d": "rain",
+        "10n": "rain",
+        "11d": "thunder",
+        "11n": "thunder",
+        "13d": "snow_shower",
+        "13n": "snow_shower",
+        "50d": "fog",
+        "50n": "fog"
+    }    
+    //
+    function _buildDataPoint(date, data) {
+        var result = {
+            timestamp: data.dt,
+            date: date,
+            metric: {
+                temp:data.main.temp,
+                windSpeed: calcKmh(data.wind.speed),
+                rain: data.main.rain || ((data.rain) ? data.rain["3h"] : false ) || 0,
+                snow: data.main.snow || ((data.snow) ? data.snow["3h"] : false ) || 0
+            },
+            imperial: {
+                temp: calcFahrenheit(data.main.temp),
+                windSpeed: calcMph(data.wind.speed),
+                rain: calcInch(data.main.rain || ((data.rain) ? data.rain["3h"] : false ) || 0),
+                snow: calcInch(data.main.snow || ((data.snow) ? data.snow["3h"] : false ) ||0)
+            },
+            humidity: data.main.humidity,
+            pressure: data.main.pressure,
+            windDeg: data.wind.deg,
+            windDir: calcWindDir(data.wind.deg),
+            icon: _icon_map[data.weather[0].icon],
+            condition: data.weather[0].main
+        };
+        if(data.id !== undefined) {
+            result["service"] = _serviceName;
+            result["service_id"] =  data.id;
+        }
+        return result;
+    }
+    //
+    function _buildDayFormat(date, data) {
+        var result = {
+            date: date,
+            timestamp: data.dt,
+            metric: {
+                tempMin: data.temp.min,
+                tempMax: data.temp.max,
+                windSpeed: calcKmh(data.speed),
+                rain: data.rain || 0,
+                snow: data.snow || 0
+            },
+            imperial: {
+                tempMin: calcFahrenheit(data.temp.min),
+                tempMax: calcFahrenheit(data.temp.max),
+                windSpeed: calcMph(data.speed),
+                rain: calcInch(data.rain || 0),
+                snow: calcInch(data.snow || 0)
+            },
+            pressure: data.pressure,
+            humidity: data.humidity,
+            icon: _icon_map[data.weather[0].icon],
+            condition: data.weather[0].main,
+            windDeg: data.deg,
+            windDir: calcWindDir(data.deg),
+            hourly: []
+        }
+        return result;
+    }
+    //
+    function formatResult(data, location) {
+        var tmpResult = {},
+            result = [],
+            day=null,
+            offset=(location.timezone && location.timezone.gmtOffset) ? location.timezone.gmtOffset*60*60*1000: 0,
+            localNow = getLocationTime(new Date().getTime()+offset),
+            todayDate;        
+        print("["+location.name+"] "+JSON.stringify(localNow))
+        // add openweathermap id for faster responses
+        if(location.services && !location.services[_serviceName] && data["current"].id) {
+            location.services[_serviceName] = data["current"].id
+        }
+        //
+        data["daily"]["list"].forEach(function(dayData) {
+            var date = getLocationTime(((dayData.dt*1000)-1000)+offset), // minus 1 sec to handle +/-12 TZ
+                day = date.year+"-"+date.month+"-"+date.date;
+            if(!todayDate) {
+                if(localNow.year+"-"+localNow.month+"-"+localNow.date > day) {
+                    // skip "yesterday"
+                    return;
+                }
+                todayDate = date;
+            }
+            tmpResult[day] = _buildDayFormat(date, dayData);
+        })
+        //
+        var today = todayDate.year+"-"+todayDate.month+"-"+todayDate.date
+        tmpResult[today]["current"] = _buildDataPoint(todayDate, data["current"]);
+        if(data["forecast"] !== undefined) {
+            data["forecast"]["list"].forEach(function(hourData) {                
+                var dateData = getLocationTime((hourData.dt*1000)+offset),
+                    day = dateData.year+"-"+dateData.month+"-"+dateData.date;
+                if(tmpResult[day]) {
+                    tmpResult[day]["hourly"].push(_buildDataPoint(dateData, hourData));
+                }
+            })
+        }
+        //
+        for(var d in tmpResult) {
+            result.push(tmpResult[d]);
+        }
+        return result;
+    }
+    //
+    function _getUrls(params) {
+        var urls = {
+                current: "",
+                daily: "",
+                forecast: ""
+            },
+            latLongParams = "&lat="+encodeURIComponent(params.location.coord.lat)
+                + "&lon="+encodeURIComponent(params.location.coord.lon);
+        if(params.location.services && params.location.services[_serviceName]) {
+            urls.current = _baseUrl + "weather?units="+params.units+"&id="+params.location.services[_serviceName];
+            urls.daily = _baseUrl + "forecast/daily?id="+params.location.services[_serviceName]+"&cnt=10&units="+params.units
+            urls.forecast = _baseUrl + "forecast?id="+params.location.services[_serviceName]+"&units="+params.units
+
+        } else if (params.location.coord) {
+            urls.current = _baseUrl + "weather?units="+params.units+latLongParams;
+            urls.daily = _baseUrl+"forecast/daily?cnt=10&units="+params.units+latLongParams;
+            urls.forecast = _baseUrl+"forecast?units="+params.units+latLongParams;
+        }
+        return urls;
+    }
+    //
+    return {     
+        //
+        getData: function(params, apiCaller, onSuccess, onError) {
+            var urls = _getUrls(params),
+                handlerMap = {
+                current: { type: "current",url: urls.current},
+                daily: { type: "daily",url: urls.daily},
+                forecast: { type: "forecast", url: urls.forecast}},
+            response = {
+                location: params.location,
+                db: (params.db) ? params.db : null,
+                format: RESPONSE_DATA_VERSION
+            },
+            respData = {},
+            addDataToResponse = (function(request, data) {
+                var formattedResult;
+                respData[request.type] = data;
+                if(respData["current"] !== undefined
+                        && respData["forecast"] !== undefined
+                            && respData["daily"] !== undefined) {
+                    response["data"] = formatResult(respData, params.location)
+                    onSuccess(response);
+                }
+            }),
+            onErrorHandler = (function(err) {
+                onError(err);
+            }),
+            retryHandler = (function(err) {
+                console.log("retry of "+err.request.url);
+                var retryFunc = handlerMap[err.request.type];
+                apiCaller(retryFunc, addDataToResponse, onErrorHandler);
+            });
+            //
+            apiCaller(handlerMap.current, addDataToResponse, retryHandler);
+            apiCaller(handlerMap.forecast, addDataToResponse, retryHandler);
+            apiCaller(handlerMap.daily, addDataToResponse, retryHandler);
+        }
+    }
+
+})();
+
+var WeatherChannelApi = (function() {
+    /**
+      provides neccessary methods for requesting and preparing data from OpenWeatherMap.org
+    */
+    var _baseUrl = "http://wxdata.weather.com/wxdata/";;
+    //
+    var _serviceName = "weatherchannel";
+    //
+    // see http://s.imwx.com/v.20131006.223722/img/wxicon/72/([0-9]+).png
+    var _iconMap = {
+        "0": "thunder", // ??
+        "1": "thunder", // ??
+        "2": "thunder", // ??
+        "3": "thunder", // ??
+        "4": "thunder", //T-Storms
+        "5": "snow_rain", //Rain / Snow
+        "6": "snow_rain", // ??
+        "7": "snow_rain", //Wintry Mix
+        "8": "scattered", //Freezing Drizzle
+        "9": "scattered", //Drizzle
+        "10": "rain", // ??
+        "11": "rain", //Showers
+        "12": "rain", //Rain
+        "13": "snow_shower", // ??
+        "14": "snow_shower", //Snow shower/Light snow
+        "15": "snow_shower", //
+        "16": "snow_shower", //Snow
+        "17": "thunder", // Hail??
+        "18": "snow_rain", // Rain / Snow ??
+        "19": "fog", //Fog ??
+        "20": "fog", //Fog
+        "21": "fog", //Haze
+        "22": "fog", // ??
+        "23": "fog", // Wind ??
+        "24": "overcast", //Partly Cloudy / Wind
+        "25": "overcast", // ??
+        "26": "overcast",//Cloudy
+        "27": "cloud_moon",//Mostly Cloudy
+        "28": "cloud_sun", //Mostly Cloudy
+        "29": "cloud_moon", //Partly Cloudy
+        "30": "cloud_sun", //Partly Cloudy
+        "31": "moon", //Clear
+        "32": "sun", //Sunny
+        "33": "cloud_moon", //Mostly Clear
+        "34": "cloud_sun", //Mostly Sunny
+        "35": "snow_rain", // ??
+        "36": "sun", //Sunny
+        "37": "thunder", //Isolated T-Storms
+        "38": "thunder", //Scattered T-Storms
+        "39": "scattered", //Scattered Showers
+        "40": "rain", // ??
+        "41": "snow", //Scattered Snow Showers
+        "42": "snow_shower", // ??
+        "43": "snow_shower", // ??
+        "44": "fog", // ??
+        "45": "scattered", // ??
+        "46": "snow_shower", //Snow Showers Early
+        "47": "thunder" //Isolated T-Storms
+    };
+    //
+    function _buildDataPoint(date, dataObj) {
+        var data = dataObj["Observation"] || dataObj,
+            result = {
+                timestamp: data.date || data.dateTime,
+                date: date,
+                metric: {
+                    temp: data.temp,
+                    tempFeels: data.feelsLike,
+                    windSpeed: data.wSpeed
+                },
+                imperial: {
+                    temp: calcFahrenheit(data.temp),
+                    tempFeels: calcFahrenheit(data.feelsLike),
+                    windSpeed: convertKmhToMph(data.wSpeed)
+                },
+                precipType: (data.precip_type !== undefined) ? data.precip_type : null,
+                propPrecip: (data.pop !== undefined) ? data.pop : null,
+                humidity: data.humid,
+                pressure: data.pressure,
+                windDeg: data.wDir,
+                windDir: data.wDirText,
+                icon: _iconMap[(data.wxIcon||data.icon)],
+                condition: data.text || data.wDesc,
+                uv: data.uv
+        };
+        if(_iconMap[data.wxIcon||data.icon] === undefined) {
+            print("ICON MISSING POINT: "+(data.wxIcon||data.icon)+" "+result.condition)
+        }
+        return result;
+    }
+    //
+    function _buildDayFormat(date, data, now) {
+        var partData = (now > data.validDate || data.day === undefined) ? data.night : data.day,
+            result = {
+            date: date,
+            timestamp: data.validDate,
+            metric: {
+                tempMin: data.minTemp,
+                tempMax: data.maxTemp,
+                windSpeed: partData.wSpeed
+            },
+            imperial: {
+                tempMin: calcFahrenheit(data.minTemp),
+                tempMax: calcFahrenheit(data.maxTemp !== undefined ? data.maxTemp : data.minTemp),
+                windSpeed: convertKmhToMph(partData.wSpeed)
+            },
+            precipType: partData.precip_type,
+            propPrecip: partData.pop,
+            pressure: null,
+            humidity: partData.humid,
+            icon: _iconMap[partData.icon],
+            condition: partData.phrase,
+            windDeg: partData.wDir,
+            windDir: partData.wDirText,
+            uv: partData.uv,
+            hourly: []
+        }
+        if(_iconMap[partData.icon] === undefined) {
+            print("ICON MISSING  DAY: "+partData.icon+" "+result.condition)
+        }
+        return result;
+    }
+    //
+    function formatResult(combinedData, location) {
+        var tmpResult = {}, result = [],
+            day=null, todayDate,
+            offset=(location.timezone && location.timezone.gmtOffset) ? location.timezone.gmtOffset*60*60*1000: 0,
+            now = new Date().getTime(),
+            nowMs = parseInt(now/1000),
+            localNow = getLocationTime(now+offset),
+            data = {
+                "location": combinedData[0]["Location"],
+                "daily": combinedData[0]["DailyForecasts"],
+                "forecast": combinedData[0]["HourlyForecasts"],
+                "current": combinedData[0]["StandardObservation"],
+            };
+        print("["+location.name+"] "+JSON.stringify(localNow));
+        // add openweathermap id for faster responses
+        if(location.services && !location.services[_serviceName] && data["location"].key) {
+            location.services[_serviceName] = data["location"].key
+        }                
+        // only 5 days of forecast for TWC
+        for(var x=0;x<5;x++) {
+            var dayData = data["daily"][x],
+                date = getLocationTime(((dayData.validDate*1000)-1000)+offset); // minus 1 sec to handle +/-12 TZ
+            day = date.year+"-"+date.month+"-"+date.date;
+            if(!todayDate) {
+                if(localNow.year+"-"+localNow.month+"-"+localNow.date > day) {
+                    // skip "yesterday"
+                    continue;
+                }
+                todayDate = date;
+            }
+            tmpResult[day] = _buildDayFormat(date, dayData, nowMs);
+        }
+        //
+        if(data["current"]) {
+            var today = todayDate.year+"-"+todayDate.month+"-"+todayDate.date
+            tmpResult[today]["current"] = _buildDataPoint(todayDate, data["current"]);
+        }
+        if(data["forecast"] !== undefined) {
+            data["forecast"].forEach(function(hourData) {
+                var dateData = getLocationTime((hourData.dateTime*1000)+offset),
+                    day = dateData.year+"-"+dateData.month+"-"+dateData.date;
+                if(tmpResult[day]) {                    
+                    tmpResult[day]["hourly"].push(_buildDataPoint(dateData, hourData));
+                }
+            })
+        }
+        //
+        for(var d in tmpResult) {
+            result.push(tmpResult[d]);
+        }
+        return result;
+    }
+    //
+    function _getUrl(params) {
+        var url, serviceId,
+            baseParams = {
+                key: params.api_key,
+                units: (params.units === "metric") ? "m" : "e",
+                locale: Qt.locale().name,
+                hours: "48",
+            },
+            commands = {
+                "mobileaggregation": "mobile/mobagg/",
+            };
+        if(params.location.services && params.location.services[_serviceName]) {
+            serviceId = encodeURIComponent(params.location.services[_serviceName]);
+            url = _baseUrl+commands["mobileaggregation"]+serviceId+".js?"+parameterize(baseParams);
+        } else if (params.location.coord) {
+            var coord = {lat: params.location.coord.lat, lng: params.location.coord.lon};
+            url = _baseUrl+commands["mobileaggregation"]+"get.js?"+parameterize(baseParams)+"&"+
+                  parameterize(coord);
+        }
+        return url;
+    }
+    //
+    return {
+        getData: function(params, apiCaller, onSuccess, onError) {
+            var url = _getUrl(params),
+                handlerMap = {
+                    all: { type: "all", url: url}
+                },
+                response = {
+                    location: params.location,
+                    db: (params.db) ? params.db : null,
+                    format: RESPONSE_DATA_VERSION
+                },
+                addDataToResponse = (function(request, data) {
+                    var formattedResult;
+                    response["data"] = formatResult(data, params.location);
+                    onSuccess(response);
+                }),
+                onErrorHandler = (function(err) {
+                    onError(err);
+                });
+            apiCaller(handlerMap.all, addDataToResponse, onErrorHandler);
+        }
+    }
+})();
+
+var WeatherApi = (function(_services) {
+    /**
+      proxy for requesting weather apis, the passed _services are providing the respective api endpoints
+      and formatters to build a uniform response object
+    */
+    function _getService(name) {
+        if(_services[name] !== undefined) {
+            return _services[name];
+        }
+        return _services["weatherchannel"];
+    }
+    //
+    function _sendRequest(request, onSuccess, onError) {
+        var xmlHttp = new XMLHttpRequest();
+        if (xmlHttp) {
+            console.log("Sent request URL: " + request.url);
+            xmlHttp.open('GET', request.url, true);
+            xmlHttp.onreadystatechange = function () {
+                try {
+                    if (xmlHttp.readyState == 4) {
+                        if(xmlHttp.status === 200) {
+                            if(xmlHttp.responseXML) {
+                                onSuccess(request, xmlHttp.responseXML.documentElement);
+                            } else {
+                                var json = JSON.parse(xmlHttp.responseText);
+                                onSuccess(request,json);
+                            }
+                        } else {
+                            onError({
+                                msg: "wrong response http code, got "+xmlHttp.status,
+                                request: request
+                            });
+                        }
+                    }
+                } catch (e) {
+                    print("Exception: "+e)
+                    onError({msg: "wrong response data format", request: request});
+                }
+            };
+            xmlHttp.send(null);
+        }
+    }
+    //
+    return  {
+        //
+        geoLookup: function(params, onSuccess, onError) {
+            var service = _getService('geoip'),
+                geoNameService = _getService('geonames'),
+                lookupHandler = function(data) {
+                    print("Geolookup: "+JSON.stringify(data))
+                    geoNameService.search("point", {coords:data}, _sendRequest, onSuccess, onError);
+                };
+            service.getLatLong(params, _sendRequest, lookupHandler, onError)
+        },
+        //
+        search: function(mode, params, onSuccess, onError) {
+            var service = _getService('geonames');
+            service.search(mode, params, _sendRequest, onSuccess, onError);
+        },
+        //
+        getLocationData: function(params, onSuccess, onError) {
+            var service = _getService(params.service);
+            service.getData(params, _sendRequest, onSuccess, onError);
+        },
+    }
+})({
+    "openweathermap": OpenWeatherMapApi,
+    "weatherchannel": WeatherChannelApi,
+    "geonames": GeonamesApi,
+    "geoip": GeoipApi
+});
+
+/**
+*  following WorkerScript handles the data requests against the weather API.
+*  "message" requires a "params" property with the required params to perform
+*  the API call and an "action" property, which will be added also to the response.
+*/
+
+if(typeof WorkerScript != "undefined") {
+    WorkerScript.onMessage = function(message) {
+        // handles the response data
+        var finished = function(result) {
+            // print result to get data for test json files
+            // print(JSON.stringify(result));
+            WorkerScript.sendMessage({
+                action: message.action,
+                result: result
+            })
+        }
+        // handles errors
+        var onError = function(err) {
+            console.log(JSON.stringify(err, null, true));
+            WorkerScript.sendMessage({ 'error': err})
+        }
+        // keep order of locations, sort results
+        var sortDataResults = function(locA, locB) {
+            return locA.db.id - locB.db.id;
+        }
+        // perform the api calls
+        if(message.action === "searchByName") {
+            WeatherApi.search("name", message.params, finished, onError);
+        } else if(message.action === "searchByPoint") {
+            WeatherApi.search("point", message.params, finished, onError);
+        } else if(message.action === "getGeoIp") {
+            WeatherApi.geoLookup(message.params, finished, onError);
+        } else if(message.action === "updateData") {
+            var locLength = message.params.locations.length,
+                locUpdated = 0,
+                result = [],
+                now = new Date().getTime();
+            if(locLength > 0) {
+                message.params.locations.forEach(function(loc) {
+                    var updatedHnd = function (newData, cached) {
+                            locUpdated += 1;
+                            if(cached === true) {
+                                newData["save"] = false;
+                            } else {
+                                newData["save"] = true;
+                                newData["updated"] =  new Date().getTime();
+                            }
+                            result.push(newData);
+                            if(locUpdated === locLength) {
+                                result.sort(sortDataResults);
+                                finished(result);
+                            }
+                        },
+                        params = {
+                            location:loc.location,
+                            db: loc.db,
+                            units: 'metric',
+                            service: message.params.service,
+                            api_key: message.params.api_key,
+                            interval: message.params.interval
+                        },
+                        secsFromLastFetch = (now-loc.updated)/1000;
+                    if( message.params.force===true || loc.format !== RESPONSE_DATA_VERSION || secsFromLastFetch > params.interval){
+                        // data older than 30min, location is new or data format is deprecated
+                        WeatherApi.getLocationData(params, updatedHnd, onError);
+                    } else {
+                        console.log("["+loc.location.name+"] returning cached data, time from last fetch: "+secsFromLastFetch)
+                        updatedHnd(loc, true);
+                    }
+                })
+            } else {
+                finished(result);
+            }
+        }
+    }
+}

=== added file 'app/data/key.js'
--- app/data/key.js	1970-01-01 00:00:00 +0000
+++ app/data/key.js	2015-03-14 20:11:20 +0000
@@ -0,0 +1,1 @@
+var twcKey = "";

=== added directory 'app/graphics'
=== added file 'app/graphics/Big-Rain.png'
Binary files app/graphics/Big-Rain.png	1970-01-01 00:00:00 +0000 and app/graphics/Big-Rain.png	2015-03-14 20:11:20 +0000 differ
=== added file 'app/graphics/CMakeLists.txt'
--- app/graphics/CMakeLists.txt	1970-01-01 00:00:00 +0000
+++ app/graphics/CMakeLists.txt	2015-03-14 20:11:20 +0000
@@ -0,0 +1,5 @@
+file(GLOB IMAGE_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.png *.svg)
+
+add_custom_target(ubuntu-weather-app_graphics_IMAGEFiles ALL SOURCES ${IMAGE_FILES})
+
+install(FILES ${IMAGE_FILES} DESTINATION ${UBUNTU-WEATHER_APP_DIR}/graphics)

=== added file 'app/graphics/Cloudy-Circles.png'
Binary files app/graphics/Cloudy-Circles.png	1970-01-01 00:00:00 +0000 and app/graphics/Cloudy-Circles.png	2015-03-14 20:11:20 +0000 differ
=== added file 'app/graphics/Cloudy-Night.png'
Binary files app/graphics/Cloudy-Night.png	1970-01-01 00:00:00 +0000 and app/graphics/Cloudy-Night.png	2015-03-14 20:11:20 +0000 differ
=== added file 'app/graphics/Cloudy-Snow.png'
Binary files app/graphics/Cloudy-Snow.png	1970-01-01 00:00:00 +0000 and app/graphics/Cloudy-Snow.png	2015-03-14 20:11:20 +0000 differ
=== added file 'app/graphics/Cloudy.png'
Binary files app/graphics/Cloudy.png	1970-01-01 00:00:00 +0000 and app/graphics/Cloudy.png	2015-03-14 20:11:20 +0000 differ
=== added file 'app/graphics/Fog.png'
Binary files app/graphics/Fog.png	1970-01-01 00:00:00 +0000 and app/graphics/Fog.png	2015-03-14 20:11:20 +0000 differ
=== added file 'app/graphics/Raindrop.png'
Binary files app/graphics/Raindrop.png	1970-01-01 00:00:00 +0000 and app/graphics/Raindrop.png	2015-03-14 20:11:20 +0000 differ
=== added file 'app/graphics/Showers.png'
Binary files app/graphics/Showers.png	1970-01-01 00:00:00 +0000 and app/graphics/Showers.png	2015-03-14 20:11:20 +0000 differ
=== added file 'app/graphics/Starry-Night.png'
Binary files app/graphics/Starry-Night.png	1970-01-01 00:00:00 +0000 and app/graphics/Starry-Night.png	2015-03-14 20:11:20 +0000 differ
=== added file 'app/graphics/Stormy.png'
Binary files app/graphics/Stormy.png	1970-01-01 00:00:00 +0000 and app/graphics/Stormy.png	2015-03-14 20:11:20 +0000 differ
=== added file 'app/graphics/Sunny.png'
Binary files app/graphics/Sunny.png	1970-01-01 00:00:00 +0000 and app/graphics/Sunny.png	2015-03-14 20:11:20 +0000 differ
=== added file 'app/graphics/Windy-n-Snow.png'
Binary files app/graphics/Windy-n-Snow.png	1970-01-01 00:00:00 +0000 and app/graphics/Windy-n-Snow.png	2015-03-14 20:11:20 +0000 differ
=== added file 'app/graphics/clouds.svg'
--- app/graphics/clouds.svg	1970-01-01 00:00:00 +0000
+++ app/graphics/clouds.svg	2015-03-14 20:11:20 +0000
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd";>
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg"; xmlns:xlink="http://www.w3.org/1999/xlink"; x="0px" y="0px"
+	 width="100px" height="100px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">
+<path fill="#818181" d="M59.975,15.531c-6.848,0.01-13.273,3.294-17.295,8.844l-0.411,0.567l-0.671-0.19
+	c-1.473-0.416-2.996-0.631-4.526-0.637c-9.091,0.014-16.524,7.214-16.842,16.316l-0.031,0.855l-0.849,0.102
+	c-6.084,0.731-10.651,5.881-10.661,12.02v0.002c0.001,6.707,5.407,12.121,12.102,12.121h3.322c0.589-1.477,1.377-2.828,2.297-4.092
+	l-5.6,0.092H20.8c-4.47,0-8.115-3.646-8.115-8.115c0-3.366,1.763-5.854,4.17-7.211c2.048-1.154,4.522-1.433,6.938-1.211
+	c-0.458-4.202,0.209-8.238,2.176-11.357c2.15-3.412,5.85-5.666,10.734-5.666c2.806,0,5.319,1.063,7.457,2.701
+	c2.545-6.614,8.791-11.138,15.965-11.141c9.521,0.001,17.26,7.74,17.26,17.26v0.002v0.002c-0.005,1.498-0.358,2.956-0.745,4.399
+	c5.03,0.398,9.048,4.506,9.048,9.638c0.001,4.139-2.635,7.588-6.279,9.016c0.326,1.08,0.611,2.18,0.791,3.318
+	c0.511,0.35,0.987,0.742,1.451,1.146c5.283-2.223,9.037-7.371,9.037-13.481c0-5.812-3.426-11.069-8.73-13.424l-0.58-0.257
+	l-0.014-0.636C81.113,24.848,71.623,15.539,59.975,15.531 M57.005,48.5c-7.043,0-13.026,4.346-15.889,10.567l-0.246,0.537
+	l-0.59,0.043c-6.467,0.471-11.592,6.07-11.592,12.9c0,7.137,5.573,12.984,12.455,12.984h15.861H71.79
+	c5.129,0,9.283-4.365,9.283-9.666c0-4.021-2.406-7.385-5.82-8.838l-0.58-0.246l-0.027-0.629C74.202,56.361,66.522,48.5,57.005,48.5
+	 M57.079,52.549h0.002c6.954,0,12.623,5.336,13.289,12.119l0.105-0.105l-0.01,1.07c0.003,0.113,0.033,0.221,0.033,0.336v0.002
+	c0,0.309-0.021,0.602-0.043,0.895v0.141c0,0.033-0.009,0.041-0.01,0.074c0,0.004,0,0.004,0,0.008
+	c-0.02,0.967-0.148,1.457-0.273,2.15c0.313-0.035,0.307-0.07,0.711-0.076h0.01h0.01c3.422,0,6.217,2.797,6.217,6.219
+	s-2.795,6.217-6.217,6.219l-29.236,0.012c-4.991,0-9.058-4.066-9.059-9.057c0-4.992,4.066-9.059,9.059-9.059
+	c0.797,0,1.559,0.252,2.324,0.459C44.999,57.496,50.432,52.555,57.079,52.549"/>
+</svg>

=== added file 'app/graphics/cloudy-night.svg'
--- app/graphics/cloudy-night.svg	1970-01-01 00:00:00 +0000
+++ app/graphics/cloudy-night.svg	2015-03-14 20:11:20 +0000
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd";>
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg"; xmlns:xlink="http://www.w3.org/1999/xlink"; x="0px" y="0px"
+	 width="100px" height="100px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">
+<path fill="#818181" d="M22.384,58.326c0.157,0.785,0.342,1.566,0.555,2.34C22.726,59.893,22.541,59.111,22.384,58.326"/>
+<path fill="#818181" d="M33.675,14.738v0.007h-0.012c-1.445,2.071-2.622,4.172-3.442,5.757c-1.776,0.263-4.07,0.684-6.311,1.341
+	v0.007c0,0.003,0.012,0,0.012,0v0.007c1.472,2.028,3.06,3.815,4.286,5.108c-0.262,1.768-0.519,4.046-0.54,6.352
+	c0,0.003,0.012,0,0.012,0c0,0.003,0.012,0,0.012,0c2.334-0.807,4.47-1.787,6.056-2.578c1.612,0.814,3.758,1.801,6,2.564
+	c0-0.002,0.012,0.003,0.012,0c0-0.002,0-0.006,0-0.006c0-0.003,0-0.007,0-0.007c-0.037-2.434-0.288-4.731-0.539-6.477
+	c1.256-1.303,2.847-3.076,4.244-5.004c0-0.003,0,0-0.012,0l-0.013-0.007c-2.329-0.681-4.598-1.122-6.338-1.396
+	c-0.836-1.613-2.019-3.708-3.415-5.668C33.687,14.738,33.675,14.735,33.675,14.738z"/>
+<path fill="#818181" d="M14.458,35.462v0.005c-0.91,1.304-1.652,2.628-2.169,3.627c-1.119,0.166-2.563,0.431-3.976,0.845v0.004
+	c0,0.003,0,0,0,0v0.005c0.928,1.278,1.928,2.404,2.7,3.218c-0.165,1.114-0.326,2.549-0.34,4.002c0,0.001,0,0,0,0c0,0.001,0,0,0,0
+	c1.47-0.509,2.815-1.126,3.814-1.624c1.016,0.513,2.367,1.135,3.779,1.615c0-0.001,0,0.001,0,0s0-0.004,0-0.004
+	c0-0.001,0-0.004,0-0.005c-0.021-1.533-0.181-2.98-0.339-4.08c0.791-0.821,1.794-1.938,2.673-3.153c0-0.001,0,0,0,0v-0.005
+	c-1.468-0.429-2.896-0.706-3.993-0.879C16.083,38.018,15.338,36.698,14.458,35.462C14.458,35.462,14.458,35.461,14.458,35.462"/>
+<path fill="#818181" d="M67.917,53.49c-6.222,0-11.51,3.842-14.033,9.324v0.004l-0.133,0.291l-0.322,0.021
+	c-5.765,0.422-10.324,5.414-10.324,11.483c0,6.342,4.962,11.556,11.098,11.556h13.719h12.787c4.62,0,8.354-3.929,8.354-8.685
+	c0-3.607-2.164-6.637-5.232-7.942v-0.004l-0.311-0.132l-0.02-0.338v-0.002c-0.392-8.631-7.17-15.576-15.579-15.576H67.917z
+	 M67.987,57.638L67.987,57.638h0.264v0.056c6.099,0.145,11.026,5.094,11.026,11.228c0,0.206-0.015,0.397-0.026,0.58v0.015v0.301
+	v0.002c-0.011,0.988-0.195,1.824-0.291,2.295c0.228-0.043,0.428-0.098,0.99-0.108c2.786,0.001,5.051,2.272,5.051,5.058
+	c0,2.784-2.267,5.053-5.051,5.055l-25.287,0.009c-4.142,0.001-7.514-3.366-7.515-7.507c0-4.143,3.372-7.514,7.515-7.512
+	c0.78,0,1.523,0.201,2.246,0.44c0.699-5.594,5.373-9.894,11.073-9.905v-0.005H67.987z M67.987,57.645l-0.248,0.248v0.012
+	c0,0,0.01,0,0.014,0v-0.017l0.016,0.017c0.073-0.002,0.145-0.012,0.218-0.012v-0.002L67.987,57.645L67.987,57.645z"/>
+<path fill="#818181" d="M67.329,20.841c4.776,7.285,6.465,16.21,4.197,24.725v0.002c-1.296,4.837-3.683,9.09-6.816,12.581
+	c1.036-0.322,2.132-0.504,3.273-0.506v-0.005h0.004h0.264v0.056c0.62,0.015,1.223,0.09,1.814,0.201
+	c2.396-3.314,4.242-7.105,5.361-11.284v-0.008c1.096-4.116,1.181-8.337,0.744-12.537l-0.354-3.391h0.002l2.129,2.662
+	c5.514,6.899,7.812,16.207,5.35,25.422v0.002c-0.878,3.277-2.283,6.265-4.094,8.904c0.047,0.413,0.074,0.831,0.074,1.256
+	c0,0.206-0.016,0.397-0.027,0.58v0.015v0.301v0.002c-0.012,0.987-0.195,1.824-0.291,2.295c0.228-0.043,0.428-0.098,0.991-0.108
+	c0.324,0,0.642,0.035,0.95,0.095c2.86-3.5,5.048-7.642,6.295-12.294c4.364-16.322-4.422-32.944-19.865-38.965H67.329z
+	 M24.888,63.143c3.756,9.717,11.88,17.168,22.051,19.907v-0.002c6.843,1.834,13.753,1.362,19.92-0.926l-12.195,0.004
+	c-2.345,0.001-4.441-1.08-5.82-2.767c-0.287-0.068-0.574-0.133-0.861-0.209v-0.002c-3.729-1.006-6.977-2.896-9.885-5.123
+	l-2.717-2.08l3.408,0.291c3.014,0.256,6.002,0.108,8.9-0.396c1.107-2.771,3.814-4.735,6.975-4.733c0.78,0,1.523,0.201,2.245,0.44
+	c0.174-1.376,0.589-2.672,1.198-3.847c-7.001,4.381-15.729,5.963-24.326,3.66v-0.002C30.58,66.49,27.638,64.969,24.888,63.143"/>
+<path fill="#818181" d="M67.987,57.645l-0.248,0.248v0.012c0,0,0.01,0,0.014,0v-0.017l0.017,0.017
+	c0.072-0.002,0.144-0.012,0.217-0.012v-0.002L67.987,57.645L67.987,57.645z"/>
+</svg>

=== added file 'app/graphics/cloudy.svg'
--- app/graphics/cloudy.svg	1970-01-01 00:00:00 +0000
+++ app/graphics/cloudy.svg	2015-03-14 20:11:20 +0000
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd";>
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg"; xmlns:xlink="http://www.w3.org/1999/xlink"; x="0px" y="0px"
+	 width="100px" height="100px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">
+<path fill="#818285" d="M53.295,21.823c-11.104,0-20.535,6.845-25.036,16.612l-0.246,0.534l-0.59,0.043
+	c-10.279,0.748-18.41,9.636-18.41,20.439c0,11.291,8.845,20.57,19.783,20.57h24.499H76.13c8.231,0,14.883-6.986,14.883-15.449
+	c0-6.416-3.855-11.809-9.324-14.13l-0.58-0.246l-0.028-0.629C80.383,34.195,68.294,21.823,53.295,21.823 M53.524,25.834h0.008
+	c12.847,0,23.281,10.436,23.281,23.281v0.002v0.004c-0.008,1.425-0.246,2.832-0.512,4.232c5.966,0.365,10.747,5.225,10.746,11.279
+	c0.001,6.295-5.123,11.418-11.418,11.418L29.604,75.91c-9.086-0.002-16.473-7.389-16.473-16.475c0-9.089,7.389-16.478,16.477-16.478
+	c0.507,0.004,1.011,0.07,1.517,0.121C33.848,32.968,42.981,25.844,53.524,25.834"/>
+</svg>

=== added file 'app/graphics/fog.svg'
--- app/graphics/fog.svg	1970-01-01 00:00:00 +0000
+++ app/graphics/fog.svg	2015-03-14 20:11:20 +0000
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd";>
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg"; xmlns:xlink="http://www.w3.org/1999/xlink"; x="0px" y="0px"
+	 width="100px" height="100px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">
+<path fill="#818181" d="M52.593,12.922c-10.004,0.005-18.5,6.132-22.555,14.873l-0.246,0.536l-0.588,0.043
+	c-9.254,0.669-16.569,8.611-16.568,18.269v1.711v0.568v4h4.169v-4v-0.568v-1.635c0-7.826,6.401-14.187,14.268-14.186
+	c0.548,0.004,1.09,0.09,1.633,0.154c2.178-9.186,10.397-15.778,19.969-15.783c11.354,0,20.587,9.168,20.586,20.459v0.002v0.005
+	c-0.008,1.419-0.303,2.811-0.602,4.198c0.026,0,0.053-0.006,0.08-0.006c5.362,0,9.737,4.343,9.737,9.683
+	c0,5.338-4.375,9.68-9.737,9.68l-60.115-0.004v4h60.631c7.406,0,13.369-6.129,13.369-13.676c0-5.726-3.46-10.533-8.375-12.607
+	l-0.582-0.246l-0.028-0.631c-0.627-13.758-11.518-24.833-25.035-24.84H52.593z M12.624,76.922h74v-4h-74V76.922z M12.624,88.922h74
+	v-4h-74V88.922z"/>
+</svg>

=== added file 'app/graphics/partly-cloudy.svg'
--- app/graphics/partly-cloudy.svg	1970-01-01 00:00:00 +0000
+++ app/graphics/partly-cloudy.svg	2015-03-14 20:11:20 +0000
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd";>
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg"; xmlns:xlink="http://www.w3.org/1999/xlink"; x="0px" y="0px"
+	 width="100px" height="100px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">
+<path fill="#818285" d="M52.013,12.326l-4,1.332v7.279h4V12.326z M31.745,17.295l-3.465,2l4,6.929l3.465-2L31.745,17.295z
+	 M68.28,17.295l-4,6.929l3.465,2l4-6.929L68.28,17.295z M50.013,26.937c-14.371,0-26,11.629-26,26
+	c0.008,11.83,7.988,22.154,19.436,25.141l0.131,0.033c0.488,5.753,5.141,10.326,10.828,10.326h13.412h12.498
+	c4.533,0,8.195-3.854,8.195-8.518c0-3.537-2.122-6.509-5.132-7.789l-0.289-0.125l-0.015-0.312
+	c-0.255-5.624-3.288-10.506-7.711-13.15l0.039-0.172c0.394-1.785,0.596-3.609,0.607-5.438
+	C76.011,38.564,64.383,26.937,50.013,26.937 M50.013,30.937c12.139,0,22,9.862,22,22v0.004v0.004
+	c-0.01,1.154-0.109,2.306-0.298,3.445l-0.095,0.564c-1.216-0.326-2.483-0.518-3.801-0.518c-6.097,0-11.277,3.765-13.748,9.135
+	l-0.123,0.268l-0.295,0.021c-4.655,0.339-8.502,3.721-9.717,8.225l-0.468-0.146c-9.189-2.863-15.455-11.379-15.456-21.002
+	C28.013,40.799,37.874,30.937,50.013,30.937 M16.37,31.205l-2,3.465l6.931,4l2-3.465L16.37,31.205z M83.655,31.205l-6.932,4
+	l2.002,3.465l6.93-4L83.655,31.205z M10.013,54.938h8v-4h-8V54.938z M82.013,54.938h8v-4h-8V54.938z M67.872,60.551h0.002h0.009
+	c6.065,0.004,10.993,4.935,10.993,11.002c0,0.201-0.018,0.392-0.029,0.586v0.287c-0.011,0.986-0.192,1.777-0.291,2.279
+	c0.302-0.049,0.428-0.098,1.002-0.107h0.012c2.706,0,4.91,2.203,4.91,4.91s-2.203,4.914-4.91,4.914l-24.717,0.01
+	c-4.032,0-7.314-3.28-7.314-7.312c-0.001-4.033,3.281-7.312,7.314-7.312c0.759,0,1.493,0.205,2.213,0.438
+	C57.735,64.773,62.304,60.562,67.872,60.551 M21.301,67.205l-6.931,4l2,3.465l6.931-4L21.301,67.205z M32.28,79.65l-4,6.93l3.465,2
+	l4-6.93L32.28,79.65z"/>
+</svg>

=== added file 'app/graphics/rain.svg'
--- app/graphics/rain.svg	1970-01-01 00:00:00 +0000
+++ app/graphics/rain.svg	2015-03-14 20:11:20 +0000
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd";>
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg"; xmlns:xlink="http://www.w3.org/1999/xlink"; x="0px" y="0px"
+	 width="100px" height="100px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">
+<path fill="#818181" d="M67.463,68.004c-2.34,5.406-4.102,10.639-4.219,11.028l-0.016,0.043l-0.014,0.043
+	c0.009-0.024-0.006,0.019-0.043,0.132c-0.15,0.455-0.232,0.928-0.232,1.418c0,2.518,2.01,4.526,4.527,4.526
+	c2.513-0.006,4.516-2.013,4.516-4.526c0-0.496-0.08-0.969-0.229-1.418l-0.01-0.021l-0.01-0.022
+	C71.734,79.207,69.963,73.832,67.463,68.004 M32.829,69.213c-2.008,4.703-3.531,9.19-3.633,9.53l-0.016,0.046l-0.017,0.045
+	c0.017-0.043,0.003-0.002-0.034,0.11v0.002c-0.129,0.392-0.199,0.796-0.199,1.22c0,2.174,1.73,3.903,3.905,3.903
+	c2.167-0.007,3.892-1.733,3.892-3.903c0-0.427-0.069-0.834-0.197-1.222l-0.01-0.022l-0.01-0.021
+	C36.51,78.901,34.975,74.278,32.829,69.213 M50.146,70.35c-2.967,6.725-5.175,13.365-5.322,13.855l-0.012,0.041l-0.018,0.038
+	c0.012-0.034-0.003,0.028-0.052,0.175v0.002c-0.189,0.571-0.293,1.168-0.293,1.785c0,3.166,2.535,5.7,5.703,5.7
+	c3.161-0.007,5.687-2.538,5.687-5.7c0-0.625-0.102-1.223-0.287-1.787l-0.01-0.021l-0.01-0.022
+	C55.533,84.416,53.318,77.611,50.146,70.35"/>
+<path fill="#818181" d="M52.523,8.96c-9.938,0-18.38,6.125-22.41,14.871l-0.246,0.537l-0.59,0.043
+	C20.092,25.08,12.825,33.02,12.825,42.68c0,10.095,7.905,18.386,17.679,18.386h22.02h20.525c7.342,0,13.275-6.232,13.275-13.784
+	c0-5.727-3.439-10.535-8.318-12.608l-0.58-0.246l-0.027-0.629C76.775,20.033,65.949,8.96,52.523,8.96 M52.6,12.942h0.012
+	c11.287,0,20.457,9.172,20.457,20.459v0.002v0.002c-0.004,1.421-0.3,2.814-0.598,4.201c0.023,0,0.048-0.006,0.072-0.006
+	c5.336,0,9.682,4.347,9.682,9.682s-4.346,9.681-9.682,9.681l-41.402-0.021c-7.823,0-14.186-6.362-14.186-14.185
+	c0-7.822,6.363-14.185,14.186-14.185c0.543,0.003,1.079,0.089,1.617,0.154C34.922,19.542,43.085,12.947,52.6,12.942"/>
+</svg>

=== added file 'app/graphics/snow.svg'
--- app/graphics/snow.svg	1970-01-01 00:00:00 +0000
+++ app/graphics/snow.svg	2015-03-14 20:11:20 +0000
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd";>
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg"; xmlns:xlink="http://www.w3.org/1999/xlink"; x="0px" y="0px"
+	 width="100px" height="100px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">
+<path fill="#818181" d="M52.523,10.719c-9.938,0-18.379,6.125-22.41,14.871l-0.246,0.537l-0.59,0.043
+	c-9.186,0.669-16.453,8.609-16.453,18.269c0,10.094,7.905,18.387,17.679,18.387h22.02h20.525c7.342,0,13.275-6.232,13.275-13.786
+	c0-5.727-3.439-10.535-8.318-12.607l-0.58-0.246l-0.027-0.629C76.775,21.792,65.949,10.719,52.523,10.719 M52.6,14.701h0.012
+	c11.287,0,20.457,9.172,20.457,20.459v0.002v0.002c-0.004,1.421-0.299,2.814-0.598,4.201c0.024,0,0.049-0.006,0.072-0.006
+	c5.336,0,9.682,4.347,9.682,9.682c0,5.334-4.346,9.682-9.682,9.682l-41.402-0.021c-7.823,0-14.186-6.363-14.186-14.186
+	c0-7.822,6.363-14.186,14.186-14.186c0.543,0.003,1.079,0.089,1.617,0.154C34.922,21.301,43.085,14.706,52.6,14.701"/>
+<polygon fill="#818181" points="51.199,73.178 47.95,73.828 47.95,79.105 43.092,76.301 41.467,79.113 46.325,81.92 41.467,84.725 
+	43.092,87.537 47.95,84.732 47.95,90.344 51.199,90.344 51.199,84.732 56.057,87.537 57.682,84.725 52.824,81.92 57.682,79.113 
+	56.057,76.301 51.199,79.105 "/>
+<polygon fill="#818181" points="34.26,68.629 32.006,69.078 32.006,72.705 28.666,70.777 27.539,72.73 30.879,74.658 27.539,76.586 
+	28.666,78.539 32.006,76.611 32.006,80.469 34.26,80.469 34.26,76.611 37.602,78.539 38.729,76.586 35.389,74.658 38.729,72.73 
+	37.602,70.777 34.26,72.705 "/>
+<polygon fill="#818181" points="68.324,66.795 65.662,67.326 65.662,71.455 61.852,69.256 60.52,71.561 64.332,73.762 60.52,75.963 
+	61.852,78.268 65.662,76.066 65.662,80.469 68.324,80.469 68.324,76.066 72.138,78.268 73.467,75.963 69.654,73.762 73.467,71.561 
+	72.138,69.256 68.324,71.455 "/>
+</svg>

=== added file 'app/graphics/starry.svg'
--- app/graphics/starry.svg	1970-01-01 00:00:00 +0000
+++ app/graphics/starry.svg	2015-03-14 20:11:20 +0000
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd";>
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg"; xmlns:xlink="http://www.w3.org/1999/xlink"; x="0px" y="0px"
+	 width="100px" height="100px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">
+<path fill="#818181" d="M67.59,20.306c4.775,7.285,6.465,16.21,4.197,24.726v0.002C67.385,61.461,50.47,71.227,34.041,66.826v-0.002
+	c-3.2-0.867-6.142-2.391-8.893-4.217c3.757,9.717,11.88,17.166,22.051,19.908v-0.002c17.547,4.701,35.555-5.697,40.258-23.242
+	c4.365-16.322-4.422-32.944-19.865-38.965H67.59z M76.08,30.14h0.002l2.129,2.662c5.514,6.899,7.812,16.207,5.35,25.422v0.002
+	C79.425,73.662,63.679,82.75,48.244,78.615v-0.002c-3.729-1.006-6.977-2.896-9.885-5.123l-2.717-2.08l3.409,0.291
+	c16.48,1.4,32.213-9.111,36.639-25.625v-0.009c1.097-4.115,1.181-8.337,0.744-12.536L76.08,30.14z"/>
+<path fill="#818181" d="M33.936,14.203v0.007h-0.012c-1.445,2.071-2.622,4.173-3.442,5.758c-1.777,0.263-4.07,0.684-6.311,1.341
+	v0.007c0,0.004,0.012,0,0.012,0v0.007c1.472,2.028,3.06,3.815,4.286,5.108c-0.262,1.768-0.518,4.046-0.54,6.352
+	c0,0.003,0.012,0,0.012,0c0,0.003,0.012,0,0.012,0c2.333-0.807,4.469-1.787,6.055-2.578c1.612,0.814,3.758,1.802,6,2.564
+	c0-0.002,0.012,0.003,0.012,0c0-0.002,0-0.006,0-0.006c0-0.003,0-0.008,0-0.008c-0.037-2.434-0.288-4.73-0.539-6.477
+	c1.256-1.303,2.847-3.076,4.244-5.004c0-0.003,0,0-0.012,0l-0.012-0.007c-2.33-0.682-4.598-1.122-6.338-1.396
+	c-0.836-1.612-2.019-3.708-3.415-5.668C33.947,14.203,33.936,14.201,33.936,14.203z"/>
+<path fill="#818181" d="M14.719,34.928v0.005c-0.91,1.304-1.652,2.628-2.169,3.627c-1.119,0.166-2.563,0.431-3.975,0.845v0.004
+	c0,0.003,0,0,0,0v0.005c0.927,1.278,1.927,2.404,2.7,3.219c-0.165,1.113-0.326,2.549-0.34,4.002c1.47-0.51,2.815-1.126,3.814-1.624
+	c1.016,0.513,2.368,1.135,3.78,1.614c0-0.001,0,0.001,0,0s0-0.004,0-0.004c0-0.001,0-0.004,0-0.005
+	c-0.022-1.533-0.181-2.979-0.339-4.08c0.791-0.821,1.794-1.938,2.673-3.152c0-0.001,0,0,0,0v-0.006
+	c-1.468-0.429-2.896-0.706-3.993-0.879C16.344,37.483,15.599,36.163,14.719,34.928C14.719,34.928,14.719,34.927,14.719,34.928z"/>
+</svg>

=== added file 'app/graphics/sunny.svg'
--- app/graphics/sunny.svg	1970-01-01 00:00:00 +0000
+++ app/graphics/sunny.svg	2015-03-14 20:11:20 +0000
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd";>
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg"; xmlns:xlink="http://www.w3.org/1999/xlink"; x="0px" y="0px"
+	 width="100px" height="100px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">
+<path fill="#818285" d="M52.013,10.225l-4,1.332v7.279h4V10.225z M31.745,15.194l-3.464,2l4,6.93l3.464-2L31.745,15.194z
+	 M68.281,15.194l-4,6.93l3.464,2l4-6.93L68.281,15.194z M50.013,24.836c-14.371,0-26,11.629-26,26c0,14.373,11.629,26,26,26
+	s26-11.627,26-26C76.013,36.465,64.384,24.836,50.013,24.836 M50.013,28.836c12.139,0,22,9.862,22,22c0,12.138-9.861,22-22,22
+	c-12.138,0-22-9.861-22-22C28.013,38.698,37.875,28.836,50.013,28.836 M16.37,29.104l-2,3.465l6.93,4l2-3.465L16.37,29.104z
+	 M83.656,29.104l-6.933,4l2.002,3.465l6.931-4L83.656,29.104z M10.013,52.836h8v-4h-8V52.836z M82.013,52.836h8v-4h-8V52.836z
+	 M21.3,65.104l-6.93,4l2,3.465l6.93-4L21.3,65.104z M78.726,65.104l-2.002,3.465l6.933,4l2-3.465L78.726,65.104z M32.281,77.549
+	l-4,6.93l3.464,2l4-6.93L32.281,77.549z M67.745,77.549l-3.464,2l4,6.93l3.464-2L67.745,77.549z M48.013,90.836h4v-8h-4V90.836z"/>
+</svg>

=== added file 'app/graphics/thunder.svg'
--- app/graphics/thunder.svg	1970-01-01 00:00:00 +0000
+++ app/graphics/thunder.svg	2015-03-14 20:11:20 +0000
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd";>
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg"; xmlns:xlink="http://www.w3.org/1999/xlink"; x="0px" y="0px"
+	 width="100px" height="100px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">
+<path fill="#818181" d="M52.573,8.953c-9.938,0-18.38,6.125-22.41,14.871l-0.246,0.537l-0.59,0.043
+	c-9.186,0.669-16.453,8.609-16.453,18.269c0,10.094,7.905,18.387,17.679,18.387h22.02h20.524c7.343,0,13.276-6.233,13.276-13.785
+	c0-5.727-3.44-10.535-8.319-12.608l-0.58-0.246l-0.026-0.629C76.824,20.026,65.999,8.953,52.573,8.953 M52.649,12.935h0.012
+	c11.287,0,20.457,9.172,20.457,20.459v0.002v0.002c-0.005,1.421-0.3,2.814-0.599,4.201c0.024,0,0.049-0.006,0.072-0.006
+	c5.336,0,9.683,4.347,9.683,9.682s-4.347,9.682-9.683,9.682L31.19,56.936c-7.823,0-14.186-6.363-14.186-14.186
+	c0-7.822,6.363-14.185,14.186-14.185c0.543,0.003,1.079,0.089,1.617,0.154C34.971,19.535,43.134,12.94,52.649,12.935"/>
+<polygon fill="#818181" points="43.806,56.953 42.515,74.045 50.502,74.045 48.624,91.953 62.321,66.953 52.867,66.953 
+	54.472,56.953 "/>
+</svg>

=== added file 'app/graphics/windy-snow.svg'
--- app/graphics/windy-snow.svg	1970-01-01 00:00:00 +0000
+++ app/graphics/windy-snow.svg	2015-03-14 20:11:20 +0000
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd";>
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg"; xmlns:xlink="http://www.w3.org/1999/xlink"; x="0px" y="0px"
+	 width="100px" height="100px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">
+<path fill="#818181" d="M51.571,15.195c-0.906,0-1.818,0.151-2.74,0.459l-0.018,0.006l-0.02,0.007
+	c-0.888,0.258-1.664,0.676-2.365,1.267v0.002v0.002c-0.644,0.536-1.198,1.251-1.652,2.199v0.008v0.008
+	c-0.04,0.08-0.075,0.247-0.113,0.355l2.098,1.209c0.027-0.074,0.007-0.116,0.041-0.193l0.01-0.016l0.01-0.014
+	c0.312-0.624,0.756-1.17,1.326-1.572c0.501-0.393,1.048-0.687,1.635-0.85c0.592-0.193,1.179-0.293,1.746-0.293
+	c1.209,0,2.363,0.473,3.348,1.299c1.135,0.872,1.715,2.258,1.715,3.824c0,1.649-0.584,3.115-1.771,4.039
+	c-1.13,0.909-2.661,1.297-4.494,1.297H49.43h-5.028H41.47H25.175v3.912h23.486c3.466,0,6.062-0.713,7.869-2.031
+	c1.718-1.253,2.631-3.407,2.641-6.805v-0.025c0-1.669-0.282-2.966-0.782-3.924v-0.008v-0.008c-0.534-1.075-1.18-1.872-1.933-2.445
+	l-0.012-0.008l-0.012-0.01c-0.794-0.643-1.605-1.058-2.471-1.273l-0.025-0.006l-0.023-0.008c-0.908-0.276-1.685-0.404-2.317-0.404
+	H51.571z M81.45,26.9c-0.922,0-1.849,0.154-2.787,0.467l-0.018,0.006l-0.021,0.007c-0.903,0.263-1.695,0.688-2.408,1.291v0.001
+	v0.002c-0.654,0.546-1.218,1.272-1.68,2.238v0.009v0.007c-0.044,0.088-0.08,0.259-0.121,0.375l2.16,1.244
+	c0.03-0.082,0.006-0.129,0.045-0.215l0.01-0.016l0.012-0.014c0.314-0.63,0.763-1.183,1.34-1.589c0.509-0.399,1.062-0.695,1.657-0.86
+	c0.597-0.196,1.191-0.299,1.769-0.299c1.224,0,2.393,0.479,3.391,1.316c1.149,0.884,1.734,2.286,1.734,3.873
+	c0,1.672-0.591,3.153-1.793,4.088c-1.145,0.921-2.695,1.314-4.555,1.314h-0.906h-5.109h-2.977H18.825v0.004h-1
+	c-1.016,0.001-2.016,0.159-2.939,0.488v0.002v0.002c-0.942,0.327-1.824,0.867-2.525,1.567v0.002v0.002
+	c-0.729,0.717-1.272,1.624-1.627,2.606v0.002c-0.373,1.025-0.529,2.134-0.529,3.332c0,1.2,0.157,2.315,0.529,3.348l0.01,0.029
+	c0.357,0.967,0.894,1.863,1.607,2.578l0.012,0.012v0.004c0.699,0.684,1.567,1.215,2.496,1.551l0.01,0.002l0.041,0.016
+	c0.923,0.316,1.915,0.467,2.932,0.467h1v0.002h40.344h2.98h5.115h0.908c1.861,0,3.414,0.396,4.559,1.316
+	c1.203,0.934,1.797,2.418,1.797,4.092c0,1.588-0.587,2.99-1.736,3.875c-0.998,0.838-2.169,1.318-3.395,1.318
+	c-0.578,0-1.173-0.104-1.771-0.299c-0.595-0.166-1.149-0.463-1.658-0.861c-0.577-0.408-1.026-0.961-1.342-1.592l-0.01-0.016
+	l-0.01-0.016c-0.035-0.084-0.016-0.133-0.045-0.213l-2.166,1.248c0.04,0.115,0.078,0.289,0.121,0.375v0.008v0.008
+	c0.463,0.965,1.028,1.695,1.684,2.242v0.002v0.002c0.714,0.604,1.509,1.029,2.412,1.293l0.018,0.006l0.02,0.006
+	c0.939,0.312,1.867,0.467,2.791,0.467c0.645,0,1.438-0.131,2.361-0.412l0.021-0.008l0.025-0.006c0.883-0.221,1.71-0.643,2.518-1.297
+	l0.012-0.01l0.014-0.008c0.768-0.584,1.425-1.396,1.971-2.492v-0.008v-0.008c0.508-0.979,0.797-2.299,0.797-3.998
+	c0-3.477-0.94-5.678-2.697-6.959c-1.842-1.342-4.482-2.068-8.01-2.068H18.812v0.01h-1c-0.629,0-1.163-0.094-1.611-0.244
+	l-0.016-0.004l-0.014-0.006c-0.402-0.148-0.757-0.377-1.016-0.627l-0.01-0.01l-0.01-0.01c-0.242-0.248-0.49-0.62-0.67-1.104v-0.014
+	c-0.002-0.004-0.003-0.011-0.004-0.015l-0.053-0.14v-0.076c-0.139-0.466-0.234-1.025-0.234-1.758c0-0.723,0.092-1.27,0.227-1.725
+	v-0.088l0.059-0.164c0.185-0.511,0.439-0.89,0.676-1.121v-0.004l0.012-0.011c0.256-0.258,0.629-0.49,1.021-0.625l0.025-0.011v-0.002
+	c0.456-0.164,0.99-0.254,1.613-0.254h60.67c3.522,0,6.161-0.725,8-2.065c1.755-1.28,2.693-3.479,2.693-6.95
+	c0-1.696-0.288-3.017-0.795-3.992v-0.008v-0.007c-0.545-1.095-1.202-1.905-1.969-2.489l-0.012-0.008l-0.012-0.01
+	c-0.807-0.652-1.632-1.074-2.514-1.295l-0.025-0.006l-0.023-0.008c-0.923-0.281-1.717-0.412-2.359-0.412H81.45z"/>
+<polygon fill="#818181" points="51.683,69.762 48.683,70.361 48.683,75.754 43.746,72.902 42.244,75.5 47.183,78.352 42.244,81.201 
+	43.746,83.799 48.683,80.947 48.683,86.65 51.683,86.65 51.683,80.947 56.619,83.799 58.119,81.201 53.183,78.352 58.119,75.5 
+	56.619,72.902 51.683,75.754 "/>
+<polygon fill="#818181" points="30.938,63.518 28.558,63.994 28.558,68.199 24.702,65.975 23.513,68.037 27.368,70.262 
+	23.513,72.486 24.702,74.549 28.558,72.324 28.558,76.775 30.938,76.775 30.938,72.324 34.793,74.549 35.983,72.486 32.13,70.262 
+	35.983,68.037 34.793,65.975 30.938,68.199 "/>
+</svg>

=== added file 'app/ubuntu-weather-app.qml'
--- app/ubuntu-weather-app.qml	1970-01-01 00:00:00 +0000
+++ app/ubuntu-weather-app.qml	2015-03-14 20:11:20 +0000
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2015 Canonical Ltd
+ *
+ * This file is part of Ubuntu Weather App
+ *
+ * Ubuntu Weather App is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Ubuntu Weather App 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, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.3
+import Qt.labs.settings 1.0
+import Ubuntu.Components 1.1
+import "components"
+import "data" as Data
+import "data/key.js" as Key
+import "ui"
+
+MainView {
+    id: weatherApp
+
+    objectName: "weather"
+
+    applicationName: "com.ubuntu.weather"
+
+    automaticOrientation: false
+
+    width: units.gu(40)
+    height: units.gu(70)
+
+    backgroundColor: "#F5F5F5"
+
+    useDeprecatedToolbar: false
+    anchorToKeyboard: true
+
+    signal listItemSwiping(int i)
+
+    /*
+      List of locations and their data, accessible through index
+    */
+    property var locationsList: []    
+
+    /*
+      Index of Location before a refresh, to go back after
+    */
+    property int indexAtRefresh: -1
+
+    /*
+      (re)load the pages on completion
+    */
+    Component.onCompleted: {
+        storage.getLocations(fillPages);
+        refreshData();
+    }
+
+    /*
+      Handle response data from data backend. Checks if a location
+      was updated and has to be stored again.
+    */
+    WorkerScript {
+         id: lookupWorker
+         source: Qt.resolvedUrl("./data/WeatherApi.js")
+         onMessage: {
+             if(!messageObject.error) {
+                 if(messageObject.action === "updateData") {
+                     messageObject.result.forEach(function(loc) {
+                         // replace location data in cache with refreshed values
+                         if(loc["save"] === true) {
+                             storage.updateLocation(loc.db.id, loc);
+                         }
+                     });
+                     //print(JSON.stringify(messageObject.result));
+                     fillPages(messageObject.result);
+                 }
+             } else {
+                 console.log(messageObject.error.msg+" / "+messageObject.error.request.url)
+                 // TODO error handling
+             }
+        }
+     }
+
+    /* Fill the location pages with their data. */
+    function fillPages(locations) {
+        locationsList = []
+        locationsList = locations;
+    }
+
+    /*
+      Refresh data, either directly from storage or by checking against
+      API instead.
+    */
+    function refreshData(from_storage, force_refresh) {
+        from_storage = from_storage || false
+        force_refresh = force_refresh || false
+        if(from_storage === true && force_refresh !== true) {
+            storage.getLocations(fillPages);
+        } else {
+            storage.getLocations(function(locations) {
+                 lookupWorker.sendMessage({
+                    action: "updateData",
+                    params: {
+                        locations: locations,
+                        force: force_refresh,
+                        service: settings.service,
+                        api_key: Key.twcKey,
+                        interval: settings.refreshInterval
+                    }
+                })
+            });
+        }
+    }
+
+    Settings {
+        id: settings
+        category: "weatherSettings"
+        /*
+          Index of the current locationList of locations and their data, accessible through index
+        */
+        property int current: 0
+
+        property int refreshInterval: 1800
+        property string precipUnits
+        property string service
+        property string tempScale
+        property string units
+        property string windUnits
+
+        property bool migrated: false
+
+        Component.onCompleted: {
+            if (units === "") {  // No settings so load defaults
+                console.debug("No settings, using defaults")
+                var metric = Qt.locale().measurementSystem === Locale.MetricSystem
+
+                precipUnits = metric ? "mm" : "in"
+                service = "weatherchannel"
+                tempScale = "°" + (metric ? "C" : "F")
+                units = metric ? "metric" : "imperial"
+                windUnits = metric ? "kmh" : "mph"
+            }
+        }
+    }
+
+    Data.Storage {
+        id: storage
+
+        // Add the location to the storage and refresh the locationList
+        // Return true if a location is added
+        function addLocation(location) {
+            var exists = checkLocationExists(location)
+
+            if(!exists) {
+                if(location.dbId === undefined || location.dbId=== 0) {
+                    storage.insertLocation({location: location});
+                }
+                refreshData();
+            }
+
+            return !exists;
+        }
+
+        // Return true if the location given is already in the locationsList
+        function checkLocationExists(location) {
+            var exists = false;
+
+            for (var i=0; !exists && i < locationsList.length; i++) {
+                var loc = locationsList[i].location;
+
+                if (loc.services.geonames && (loc.services.geonames === location.services.geonames)) {
+                    exists = true;
+                }
+            }
+
+            return exists;
+        }
+
+        function moveLocation(from, to) {
+            // Update settings to respect new changes
+            if (from === settings.current) {
+                settings.current = to;
+            } else if (from < settings.current && to >= settings.current) {
+                settings.current -= 1;
+            } else if (from > settings.current && to <= settings.current) {
+                settings.current += 1;
+            }
+
+            storage.reorder(locationsList[from].db.id, locationsList[to].db.id);
+
+            refreshData(true, false);
+        }
+
+        // Remove a location from the list
+        function removeLocation(index) {
+            if (settings.current >= index) {  // Update settings to respect new changes
+                settings.current -= settings.current;
+            }
+
+            storage.clearLocation(locationsList[index].db.id);
+
+            refreshData(true, false);
+        }
+
+        function removeMultiLocations(indexes) {
+            var i;
+
+            // Sort the item indexes as loops below assume *numeric* sort
+            indexes.sort(function(a,b) { return a - b })
+
+            for (i=0; i < indexes.length; i++) {
+                if (settings.current >= i) {  // Update settings to respect new changes
+                    settings.current -= settings.current;
+                }
+            }
+
+            // Get location db ids to remove
+            var locations = []
+
+            for (i=0; i < indexes.length; i++) {
+                locations.push(locationsList[indexes[i]].db.id)
+            }
+
+            storage.clearMultiLocation(locations);
+
+            refreshData(true, false);
+        }
+    }
+
+    PageStack {
+        id: mainPageStack
+        Component.onCompleted: mainPageStack.push(Qt.resolvedUrl("ui/HomePage.qml"))
+    }
+}

=== added directory 'app/ui'
=== added file 'app/ui/AddLocationPage.qml'
--- app/ui/AddLocationPage.qml	1970-01-01 00:00:00 +0000
+++ app/ui/AddLocationPage.qml	2015-03-14 20:11:20 +0000
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2015 Canonical Ltd
+ *
+ * This file is part of Ubuntu Weather App
+ *
+ * Ubuntu Weather App is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Ubuntu Weather App 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, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.3
+import Ubuntu.Components 1.1
+import Ubuntu.Components.ListItems 1.0 as ListItem
+import Ubuntu.Components.Popups 1.0
+import "../components"
+import "../data/CitiesList.js" as Cities
+import "../data/WeatherApi.js" as WeatherApi
+
+Page {
+    id: addLocationPage
+
+    title: i18n.tr("Select a city")
+    visible: false
+
+    /*
+      Flickable is set to null to stop page header from hiding since the fast
+      scroll component hides top anchor margin is incorrect.
+    */
+    flickable: null
+
+    state: "default"
+    states: [
+        PageHeadState {
+            name: "default"
+            head: addLocationPage.head
+            actions: [
+                Action {
+                    iconName: "search"
+                    text: i18n.tr("City")
+                    onTriggered: {
+                        addLocationPage.state = "search"
+                        searchComponentLoader.sourceComponent = searchComponent
+                        searchComponentLoader.item.forceActiveFocus()
+                    }
+                }
+            ]
+        },
+
+        PageHeadState {
+            name: "search"
+            head: addLocationPage.head
+            backAction: Action {
+                iconName: "back"
+                text: i18n.tr("Back")
+                onTriggered: {
+                    locationList.forceActiveFocus()
+                    searchComponentLoader.item.text = ""
+                    addLocationPage.state = "default"
+                    searchComponentLoader.sourceComponent = undefined
+                }
+            }
+
+            contents: Loader {
+                id: searchComponentLoader
+                anchors {
+                    left: parent ? parent.left : undefined
+                    right: parent ? parent.right : undefined
+                    rightMargin: units.gu(2)
+                }
+            }
+        }
+    ]
+
+    Component {
+        id: searchComponent
+        TextField {
+            id: searchField
+            objectName: "searchField"
+
+            inputMethodHints: Qt.ImhNoPredictiveText
+            placeholderText: i18n.tr("Search city")
+            hasClearButton: true
+
+            onTextChanged: {
+                if (text.trim() === "") {
+                    loadEmpty()
+                } else {
+                    loadFromProvider(text)
+                }
+            }
+        }
+    }
+
+    function appendCities(list) {
+        list.forEach(function(loc) {
+            citiesModel.append(loc);
+        })
+    }
+
+    function clearModelForLoading() {
+        citiesModel.clear()
+        citiesModel.loading = true
+        citiesModel.httpError = false
+    }
+
+    function loadEmpty() {
+        clearModelForLoading()
+
+        appendCities(Cities.preList)
+
+        citiesModel.loading = false
+    }
+
+    function loadFromProvider(search) {
+        clearModelForLoading()
+
+        lookupWorker.sendMessage({
+                                   action: "searchByName",
+                                   params: {
+                                       name: search,
+                                       units: "metric"
+                                   }
+                               });
+    }
+
+
+    WorkerScript {
+        id: lookupWorker
+        source: Qt.resolvedUrl("../data/WeatherApi.js")
+        onMessage: {
+            if (!messageObject.error) {
+                appendCities(messageObject.result.locations)
+            } else {
+                citiesModel.httpError = true
+            }
+
+            citiesModel.loading = false
+        }
+    }
+
+    ListView {
+        id: locationList
+
+        clip: true
+        currentIndex: -1
+        anchors.fill: parent
+        anchors.rightMargin: fastScroll.showing ? fastScroll.width - units.gu(1)
+                                                : 0
+
+        function getSectionText(index) {
+            return citiesModel.get(index).name.substring(0,1)
+        }
+
+        onFlickStarted: {
+            forceActiveFocus()
+        }
+
+        section.property: "name"
+        section.criteria: ViewSection.FirstCharacter
+        section.labelPositioning: ViewSection.InlineLabels
+
+        section.delegate: ListItem.Header {
+            text: section
+        }
+
+        model: ListModel {
+            id: citiesModel
+
+            property bool loading: true
+            property bool httpError: false
+
+            onRowsAboutToBeInserted: loading = false
+        }
+
+        delegate: ListItem.Empty {
+            showDivider: false
+            Column {
+                anchors {
+                    left: parent.left
+                    leftMargin: units.gu(2)
+                    right: parent.right
+                    rightMargin: units.gu(2)
+                    verticalCenter: parent.verticalCenter
+                }
+
+                Label {
+                    color: UbuntuColors.darkGrey
+                    elide: Text.ElideRight
+                    fontSize: "medium"
+                    text: name
+                }
+
+                Label {
+                    color: UbuntuColors.lightGrey
+                    elide: Text.ElideRight
+                    fontSize: "xx-small"
+                    text: countryName
+                }
+            }
+
+            onClicked: {
+                if (storage.addLocation(citiesModel.get(index))) {
+                    mainPageStack.pop()
+                } else {
+                    PopupUtils.open(locationExistsComponent, addPage)
+                }
+            }
+        }
+
+        Component.onCompleted: loadEmpty()
+
+        Behavior on anchors.rightMargin {
+            UbuntuNumberAnimation {}
+        }
+    }
+
+    FastScroll {
+        id: fastScroll
+
+        listView: locationList
+
+        enabled: (locationList.contentHeight > (locationList.height * 2)) &&
+                 (locationList.height >= minimumHeight)
+
+        anchors {
+            top: locationList.top
+            topMargin: units.gu(1.5)
+            bottom: locationList.bottom
+            right: parent.right
+        }
+    }
+
+    ActivityIndicator {
+        anchors {
+            centerIn: parent
+        }
+        running: visible
+        visible: citiesModel.loading
+    }
+
+    Label {
+        id: noCity
+        anchors {
+            centerIn: parent
+        }
+        text: i18n.tr("No city found")
+        visible: citiesModel.count === 0 && !citiesModel.loading
+    }
+
+    Label {
+        id: httpFail
+        anchors {
+            left: parent.left
+            margins: units.gu(1)
+            right: parent.right
+            top: noCity.bottom
+        }
+        horizontalAlignment: Text.AlignHCenter
+        text: i18n.tr("Couldn't load weather data, please try later again!")
+        visible: citiesModel.httpError
+        wrapMode: Text.WordWrap
+    }
+
+    Component {
+        id: locationExistsComponent
+
+        Dialog {
+            id: locationExists
+            title: i18n.tr("Location already added.")
+
+            Button {
+                text: i18n.tr("OK")
+                onClicked: PopupUtils.close(locationExists)
+            }
+        }
+    }
+
+}

=== added file 'app/ui/CMakeLists.txt'
--- app/ui/CMakeLists.txt	1970-01-01 00:00:00 +0000
+++ app/ui/CMakeLists.txt	2015-03-14 20:11:20 +0000
@@ -0,0 +1,7 @@
+add_subdirectory(settings)
+
+file(GLOB UI_QML_JS_FILES *.qml *.js)
+
+add_custom_target(ubuntu-weather-app_ui_QMlFiles ALL SOURCES ${UI_QML_JS_FILES})
+
+install(FILES ${UI_QML_JS_FILES} DESTINATION ${UBUNTU-WEATHER_APP_DIR}/ui)

=== added file 'app/ui/HomePage.qml'
--- app/ui/HomePage.qml	1970-01-01 00:00:00 +0000
+++ app/ui/HomePage.qml	2015-03-14 20:11:20 +0000
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2015 Canonical Ltd
+ *
+ * This file is part of Ubuntu Weather App
+ *
+ * Ubuntu Weather App is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Ubuntu Weather App 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, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.3
+import Ubuntu.Components 1.1
+import "../components"
+
+
+PageWithBottomEdge {
+    // Set to null otherwise the header is shown (but blank) over the top of the listview
+    id: locationPage
+    flickable: null
+
+    bottomEdgePageSource: Qt.resolvedUrl("LocationsPage.qml")
+    bottomEdgeTitle: i18n.tr("Locations")
+    tipColor: UbuntuColors.orange
+    tipLabelColor: "#FFF"
+
+    property var iconMap: {
+        "sun": Qt.resolvedUrl("../graphics/sunny.svg"),
+        "moon": Qt.resolvedUrl("../graphics/starry.svg"),
+        "cloud_sun": Qt.resolvedUrl("../graphics/partly-cloudy.svg"),
+        "cloud_moon": Qt.resolvedUrl("../graphics/cloudy-night.svg"),
+        "cloud": Qt.resolvedUrl("../graphics/cloudy.svg"),
+        "rain": Qt.resolvedUrl("../graphics/rain.svg"),
+        "thunder": Qt.resolvedUrl("../graphics/thunder.svg"),
+        "snow_shower": Qt.resolvedUrl("../graphics/snow.svg"),
+        "fog": Qt.resolvedUrl("../graphics/fog.svg"),
+        "snow_rain": Qt.resolvedUrl("../graphics/snow.svg"),
+        "scattered": Qt.resolvedUrl("../graphics/rain.svg"),
+        "overcast": Qt.resolvedUrl("../graphics/cloudy.svg")
+    }
+
+    property var imageMap: {
+        "sun": Qt.resolvedUrl("../graphics/Sunny.png"),
+        "moon": Qt.resolvedUrl("../graphics/Starry-Night.png"),
+        "cloud_sun": Qt.resolvedUrl("../graphics/Cloudy-Circles.png"),
+        "cloud_moon": Qt.resolvedUrl("../graphics/Cloudy-Night.png"),
+        "cloud": Qt.resolvedUrl("../graphics/Cloudy.png"),
+        "rain": Qt.resolvedUrl("../graphics/Big-Rain.png"),
+        "thunder": Qt.resolvedUrl("../graphics/Stormy.png"),
+        "snow_shower": Qt.resolvedUrl("../graphics/Cloudy-Snow.png"),
+        "fog": Qt.resolvedUrl("../graphics/Fog.png"),
+        "snow_rain": Qt.resolvedUrl("../graphics/Cloudy-Snow.png"),
+        "scattered": Qt.resolvedUrl("../graphics/Showers.png"),
+        "overcast": Qt.resolvedUrl("../graphics/Cloudy.png")
+    }
+
+    /*
+      Format date object by given format.
+    */
+    function formatTimestamp(dateData, format) {
+        var date = new Date(dateData.year, dateData.month, dateData.date, dateData.hours, dateData.minutes)
+        return Qt.formatDate(date, i18n.tr(format))
+    }
+
+    /*
+      Format time object by given format.
+    */
+    function formatTime(dateData, format) {
+        var date = new Date(dateData.year, dateData.month, dateData.date, dateData.hours, dateData.minutes)
+        return Qt.formatTime(date, i18n.tr(format))
+    }
+
+    /*
+      Flickable to scroll the location vertically.
+      The respective contentHeight gets calculated after the Location is filled with data.
+    */
+    Flickable {
+        id: locationFlickable
+        width: parent.width
+        height: parent.height
+        contentWidth: parent.width
+
+        PullToRefresh {
+            id: pullToRefresh
+            parent: locationFlickable
+            refreshing: false
+            onRefresh: {
+                refreshing = true
+                refreshData(false, true)
+            }
+        }
+
+        /*
+          ListView for locations with snap-scrolling horizontally.
+        */
+        ListView {
+            id: locationPages
+            anchors.fill: parent
+            width: parent.width
+            height: childrenRect.height
+            contentWidth: parent.width
+            contentHeight: childrenRect.height
+            model: weatherApp.locationsList.length
+            // TODO with snapMode, currentIndex is not properly set and setting currentIndex fails
+            //snapMode: ListView.SnapOneItem
+            orientation: ListView.Horizontal
+            currentIndex: settings.current
+            highlightRangeMode: ListView.StrictlyEnforceRange
+            onCurrentIndexChanged: {
+                if (loaded) {
+                    // FIXME: when a model is reloaded this causes the currentIndex to be lost
+                    settings.current = currentIndex
+                }
+            }
+            onModelChanged: {
+                currentIndex = settings.current
+
+                if (model > 0) {
+                    pullToRefresh.refreshing = false
+                    loaded = true
+                }
+            }
+            delegate: LocationPane {}
+
+            property bool loaded: false
+            // TODO: workaround for not being able to use snapMode property
+            Component.onCompleted: {
+                var scaleFactor = units.gridUnit * 10;
+                maximumFlickVelocity = maximumFlickVelocity * scaleFactor;
+                flickDeceleration = flickDeceleration * scaleFactor;
+            }
+
+            Connections {
+                target: settings
+                onCurrentChanged: {
+                    locationPages.currentIndex = settings.current
+                }
+            }
+        }
+    }
+}

=== added file 'app/ui/LocationPane.qml'
--- app/ui/LocationPane.qml	1970-01-01 00:00:00 +0000
+++ app/ui/LocationPane.qml	2015-03-14 20:11:20 +0000
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2015 Canonical Ltd
+ *
+ * This file is part of Ubuntu Weather App
+ *
+ * Ubuntu Weather App is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Ubuntu Weather App 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, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.3
+import Ubuntu.Components 1.1
+import Ubuntu.Components.ListItems 0.1 as ListItem
+import "../components"
+
+Rectangle {
+    id: locationItem
+    /*
+      Data properties
+    */
+    property string name
+    property string conditionText
+    property string currentTemp
+    property string todayMaxTemp
+    property string todayMinTemp
+    property string icon
+    property string iconName
+
+    Component.onCompleted: renderData(index)
+
+    width: locationPage.width
+    height: childrenRect.height
+    anchors.fill: parent.fill
+
+    /*
+      Calculates the height of all location data components, to set the Flickable.contentHeight right.
+    */
+    function setFlickableContentHeight() {
+        var contentHeightGu = (homeTempInfo.height+homeGraphic.height
+                               +(weekdayColumn.height*mainPageWeekdayListView.model.count))/units.gridUnit;
+        locationFlickable.contentHeight = units.gu(contentHeightGu+25);
+    }
+
+    /*
+      Extracts values from the location weather data and puts them into the appropriate components
+      to display them.
+
+      Attention: Data access happens through "weatherApp.locationList[]" by index, since complex
+      data in models will lead to type problems.
+    */
+    function renderData(index) {
+        var data = weatherApp.locationsList[index],
+                current = data.data[0].current,
+                forecasts = data.data,
+                forecastsLength = forecasts.length,
+                today = forecasts[0],
+                hourlyForecasts = [];
+
+        var tempUnits = settings.tempScale === "°C" ? "metric" : "imperial"
+
+        // set general location data
+        name = data.location.name;
+
+        // set current temps and condition
+        iconName = (current.icon) ? current.icon : "";
+        icon = (imageMap[iconName] !== undefined) ? imageMap[iconName] : "";
+        conditionText = (current.condition.main) ? current.condition.main : current.condition; // difference TWC/OWM
+        todayMaxTemp = (today[tempUnits].tempMax !== undefined) ? Math.round(today[tempUnits].tempMax).toString() + settings.tempScale: "";
+        todayMinTemp = Math.round(today[tempUnits].tempMin).toString() + settings.tempScale;
+        currentTemp = Math.round(current[tempUnits].temp).toString() + String("°");
+
+        // reset days list
+        mainPageWeekdayListView.model.clear()
+
+        // set daily forecasts
+        if(forecastsLength > 0) {
+            for(var x=0;x<forecastsLength;x++) {
+                // collect hourly forecasts if available
+                if(forecasts[x].hourly !== undefined && forecasts[x].hourly.length > 0) {
+                    hourlyForecasts = hourlyForecasts.concat(forecasts[x].hourly)
+                }
+                if(x === 0) {
+                    // skip todays daydata
+                    continue;
+                }
+
+                // set daydata
+                var dayData = {
+                    day: formatTimestamp(forecasts[x].date, 'dddd'),
+                    low: Math.round(forecasts[x][tempUnits].tempMin).toString() + settings.tempScale,
+                    high: (forecasts[x][tempUnits].tempMax !== undefined) ? Math.round(forecasts[x][tempUnits].tempMax).toString() + settings.tempScale : "",
+                    image: (forecasts[x].icon !== undefined && iconMap[forecasts[x].icon] !== undefined) ? iconMap[forecasts[x].icon] : ""
+                }
+                mainPageWeekdayListView.model.append(dayData);
+            }
+        }        
+        setFlickableContentHeight();
+
+        // set data for hourly forecasts
+        if(hourlyForecasts.length > 0) {
+            homeHourlyLoader.forecasts = hourlyForecasts;
+            homeHourlyLoader.tempUnits = tempUnits;
+        }
+    }
+
+    Column {
+        id: locationTop
+        anchors {
+            left: parent.left
+            right: parent.right
+            margins: units.gu(2)
+        }
+        spacing: units.gu(1)
+
+        HeaderRow {
+            id: headerRow
+            locationName: locationItem.name
+        }
+
+        HomeGraphic {
+            id: homeGraphic
+            icon: locationItem.icon
+            MouseArea {
+                anchors.fill: parent
+                onClicked: {
+                    homeGraphic.visible = false;
+                }
+            }
+        }
+
+        Loader {
+            id: homeHourlyLoader
+            active: !homeGraphic.visible
+            asynchronous: true
+            height: units.gu(32)
+            source: "../components/HomeHourly.qml"
+            visible: active
+            width: parent.width
+
+            property var forecasts: []
+            property string tempUnits: ""
+        }
+
+        HomeTempInfo {
+            id: homeTempInfo
+            description: conditionText
+            high: locationItem.todayMaxTemp
+            low: locationItem.todayMinTemp
+            now: locationItem.currentTemp
+        }
+
+        ListItem.ThinDivider {}
+    }
+    Column {
+        id: weekdayColumn
+        width: parent.width
+        height: childrenRect.height
+        anchors {
+            top: locationTop.bottom
+            left: parent.left
+            right: parent.right
+            margins: units.gu(2)
+        }
+        Repeater {
+            id: mainPageWeekdayListView
+            model: ListModel{}
+            DayDelegate {
+                day: model.day
+                high: model.high
+                image: model.image
+                low: model.low
+            }
+        }
+    }
+}

=== added file 'app/ui/LocationsPage.qml'
--- app/ui/LocationsPage.qml	1970-01-01 00:00:00 +0000
+++ app/ui/LocationsPage.qml	2015-03-14 20:11:20 +0000
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2015 Canonical Ltd
+ *
+ * This file is part of Ubuntu Weather App
+ *
+ * Ubuntu Weather App is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Ubuntu Weather App 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, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.3
+import Ubuntu.Components 1.1
+import Ubuntu.Components.ListItems 0.1 as ListItem
+import "../components"
+import "../components/ListItemActions"
+
+
+Page {
+    id: locationsPage
+    // Set to null otherwise the first delegate appears +header.height down the page
+    flickable: null
+    title: i18n.tr("Locations")
+
+    state: locationsListView.state === "multiselectable" ? "selection" : "default"
+    states: [
+        PageHeadState {
+            id: defaultState
+            name: "default"
+            actions: [
+                Action {
+                    iconName: "add"
+                    onTriggered: mainPageStack.push(Qt.resolvedUrl("AddLocationPage.qml"))
+                }
+            ]
+            PropertyChanges {
+                target: locationsPage.head
+                actions: defaultState.actions
+            }
+        },
+        MultiSelectHeadState {
+            listview: locationsListView
+            removable: true
+            thisPage: locationsPage
+
+            onRemoved: storage.removeMultiLocations(selectedItems.slice())
+        }
+    ]
+
+    MultiSelectListView {
+        id: locationsListView
+        anchors {
+            fill: parent
+        }
+        model: ListModel {
+            id: locationsModel
+        }
+        delegate: WeatherListItem {
+            id: locationsListItem
+            leftSideAction: Remove {
+                onTriggered: storage.removeLocation(index)
+            }
+            multiselectable: true
+            reorderable: true
+
+            onItemClicked: {
+                settings.current = index;
+                pageStack.pop()
+            }
+            onReorder: {
+                console.debug("Move: ", from, to);
+
+                storage.moveLocation(from, to);
+            }
+
+            Row {
+                anchors{
+                    top: parent.top
+                    left: parent.left
+                    leftMargin: units.gu(2)
+                    right: parent.right
+                    rightMargin: units.gu(2)
+                    bottom: parent.bottom
+                }
+                spacing: units.gu(2)
+
+                Label {
+                    elide: Text.ElideRight
+                    height: locationsListItem.height
+                    text: model.location.name
+                    verticalAlignment: Text.AlignVCenter
+                    width: weatherImage.visible ? (locationsListItem.width / 2) - units.gu(4)
+                                                : locationsListItem.width - units.gu(16)
+                }
+
+                Image {
+                    id: weatherImage
+                    anchors {
+                        centerIn: parent
+                    }
+                    height: units.gu(3)
+                    source: locationPage.iconMap[locationPages.contentItem.children[index].iconName] || ""
+                    visible: locationsPage.state === "default"
+                    width: units.gu(3)
+                }
+
+                Label {
+                    id: nowLabel
+                    anchors {
+                        right: parent.right
+                    }
+                    color: UbuntuColors.orange
+                    font.pixelSize: units.gu(4)
+                    font.weight: Font.Light
+                    height: units.gu(6)
+                    text: locationPages.contentItem.children[index].currentTemp ? locationPages.contentItem.children[index].currentTemp + settings.tempScale[1]
+                                                                                : ""
+                    verticalAlignment: Text.AlignVCenter
+                    visible: locationsPage.state === "default"
+                }
+            }
+
+            ListItem.ThinDivider {
+                anchors {
+                    bottom: parent.bottom
+                }
+            }
+        }
+    }
+
+    function populateLocationsModel() {
+        locationsModel.clear()
+
+        for (var i=0; i < weatherApp.locationsList.length; i++) {
+            locationsModel.append(weatherApp.locationsList[i])
+        }
+    }
+
+    Connections {
+        target: weatherApp
+        onLocationsListChanged: populateLocationsModel()
+    }
+
+    Component.onCompleted: populateLocationsModel()
+}

=== added file 'app/ui/SettingsPage.qml'
--- app/ui/SettingsPage.qml	1970-01-01 00:00:00 +0000
+++ app/ui/SettingsPage.qml	2015-03-14 20:11:20 +0000
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 Canonical Ltd
+ *
+ * This file is part of Ubuntu Weather App
+ *
+ * Ubuntu Weather App is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Ubuntu Weather App 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, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.3
+import Ubuntu.Components 1.1
+import Ubuntu.Components.ListItems 0.1 as ListItem
+
+Page {
+    title: i18n.tr("Settings")
+
+    Flickable {
+        anchors {
+            fill: parent
+        }
+        height: parent.height
+        contentHeight: settingsColumn.childrenRect.height
+
+        Column {
+            id: settingsColumn
+            anchors {
+                fill: parent
+            }
+
+            ListItem.SingleValue {
+                progression: true
+                text: i18n.tr("Units")
+
+                onClicked: mainPageStack.push(Qt.resolvedUrl("settings/UnitsPage.qml"))
+            }
+
+            ListItem.SingleValue {
+                progression: true
+                text: i18n.tr("Data Provider")
+
+                onClicked: mainPageStack.push(Qt.resolvedUrl("settings/DataProviderPage.qml"))
+            }
+
+            ListItem.SingleValue {
+                progression: true
+                text: i18n.tr("Refresh Interval")
+
+                onClicked: mainPageStack.push(Qt.resolvedUrl("settings/RefreshIntervalPage.qml"))
+            }
+        }
+    }
+}

=== added directory 'app/ui/settings'
=== added file 'app/ui/settings/CMakeLists.txt'
--- app/ui/settings/CMakeLists.txt	1970-01-01 00:00:00 +0000
+++ app/ui/settings/CMakeLists.txt	2015-03-14 20:11:20 +0000
@@ -0,0 +1,5 @@
+file(GLOB UI_SETTINGS_QML_JS_FILES *.qml *.js)
+
+add_custom_target(ubuntu-weather-app_ui_settings_QMlFiles ALL SOURCES ${UI_SETTINGS_QML_JS_FILES})
+
+install(FILES ${UI_SETTINGS_QML_JS_FILES} DESTINATION ${UBUNTU-WEATHER_APP_DIR}/ui/settings)

=== added file 'app/ui/settings/DataProviderPage.qml'
--- app/ui/settings/DataProviderPage.qml	1970-01-01 00:00:00 +0000
+++ app/ui/settings/DataProviderPage.qml	2015-03-14 20:11:20 +0000
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 Canonical Ltd
+ *
+ * This file is part of Ubuntu Weather App
+ *
+ * Ubuntu Weather App is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Ubuntu Weather App 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, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.3
+import Ubuntu.Components 1.1
+import Ubuntu.Components.ListItems 0.1 as ListItem
+
+
+Page {
+    title: i18n.tr("Data Provider")
+
+    Flickable {
+        anchors {
+            fill: parent
+        }
+        height: parent.height
+        contentHeight: unitsColumn.childrenRect.height
+
+        Column {
+            id: unitsColumn
+            anchors {
+                fill: parent
+            }
+
+            ListItem.ItemSelector {
+                expanded: true
+                model: ["openweathermap", "weatherchannel"]  // "geonames", "geoip"]
+                selectedIndex: model.indexOf(settings.service)
+                text: i18n.tr("Provider")
+
+                onDelegateClicked: {
+                    settings.service = model[index]
+                    refreshData(false, true)
+                }
+            }
+        }
+    }
+}

=== added file 'app/ui/settings/RefreshIntervalPage.qml'
--- app/ui/settings/RefreshIntervalPage.qml	1970-01-01 00:00:00 +0000
+++ app/ui/settings/RefreshIntervalPage.qml	2015-03-14 20:11:20 +0000
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2015 Canonical Ltd
+ *
+ * This file is part of Ubuntu Weather App
+ *
+ * Ubuntu Weather App is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Ubuntu Weather App 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, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.3
+import Ubuntu.Components 1.1
+import Ubuntu.Components.ListItems 0.1 as ListItem
+
+
+Page {
+    title: i18n.tr("Refresh Interval")
+
+    Flickable {
+        anchors {
+            fill: parent
+        }
+        height: parent.height
+        contentHeight: unitsColumn.childrenRect.height
+
+        Column {
+            id: unitsColumn
+            anchors {
+                fill: parent
+            }
+
+            ListItem.ItemSelector {
+                delegate: OptionSelectorDelegate {
+                    text: Math.floor(modelData / 60).toString() + " " + i18n.tr("minutes")
+                }
+                expanded: true
+                model: [600, 900, 1800, 3600]
+                selectedIndex: model.indexOf(settings.refreshInterval)
+                text: i18n.tr("Interval")
+
+                onDelegateClicked: {
+                    settings.refreshInterval = model[index]
+                    refreshData(false, true)
+                }
+            }
+        }
+    }
+}

=== added file 'app/ui/settings/UnitsPage.qml'
--- app/ui/settings/UnitsPage.qml	1970-01-01 00:00:00 +0000
+++ app/ui/settings/UnitsPage.qml	2015-03-14 20:11:20 +0000
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2015 Canonical Ltd
+ *
+ * This file is part of Ubuntu Weather App
+ *
+ * Ubuntu Weather App is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Ubuntu Weather App 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, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.3
+import Ubuntu.Components 1.1
+import Ubuntu.Components.ListItems 0.1 as ListItem
+
+
+Page {
+    title: i18n.tr("Units")
+
+    Flickable {
+        anchors {
+            fill: parent
+        }
+        height: parent.height
+        contentHeight: unitsColumn.childrenRect.height
+
+        Column {
+            id: unitsColumn
+            anchors {
+                fill: parent
+            }
+
+            ListItem.ItemSelector {
+                expanded: true
+                model: ["°C", "°F"]
+                selectedIndex: model.indexOf(settings.tempScale)
+                text: i18n.tr("Temperature")
+
+                onDelegateClicked: {
+                    settings.tempScale = model[index]
+                    refreshData(true)
+                }
+            }
+
+            ListItem.ItemSelector {
+                expanded: true
+                model: ["mm", "in"]
+                selectedIndex: model.indexOf(settings.precipUnits)
+                text: i18n.tr("Precipitation")
+
+                onDelegateClicked: {
+                    settings.precipUnits = model[index]
+                    refreshData(true)
+                }
+            }
+
+            ListItem.ItemSelector {
+                expanded: true
+                model: ["kmh", "mph"]
+                text: i18n.tr("Wind Speed")
+                selectedIndex: model.indexOf(settings.windUnits)
+
+                onDelegateClicked: {
+                    settings.windUnits = model[index]
+                    refreshData(true)
+                }
+            }
+        }
+    }
+}

=== added file 'app/weather-app@xxxxxx'
Binary files app/weather-app@xxxxxx	1970-01-01 00:00:00 +0000 and app/weather-app@xxxxxx	2015-03-14 20:11:20 +0000 differ
=== added directory 'backend'
=== added file 'backend/CMakeLists.txt'
--- backend/CMakeLists.txt	1970-01-01 00:00:00 +0000
+++ backend/CMakeLists.txt	2015-03-14 20:11:20 +0000
@@ -0,0 +1,29 @@
+project(backend)
+
+include_directories(
+    ${CMAKE_CURRENT_SOURCE_DIR}
+)
+
+#set(
+#    timeZone_SRCS
+#    TimeZone/timezone.cpp
+#    TimeZone/timezone_plugin.cpp
+#)
+
+#add_library(TimeZoneBackend MODULE
+#    ${timeZone_SRCS}
+#)
+#set_target_properties(TimeZoneBackend PROPERTIES
+#         LIBRARY_OUTPUT_DIRECTORY TimeZone)
+
+#qt5_use_modules(TimeZoneBackend Gui Qml Quick)
+
+# Copy qmldir file to build dir for running in QtCreator
+#add_custom_target(TimeZoneBackend-qmldir ALL
+#    COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/TimeZone/qmldir ${CMAKE_CURRENT_BINARY_DIR}/TimeZone
+#    DEPENDS ${QML_JS_FILES}
+#)
+
+# Install plugin file
+#install(TARGETS TimeZoneBackend DESTINATION ${QT_IMPORTS_DIR}/qt5/qml/UbuntuWeather/TimeZone/)
+#install(FILES TimeZone/qmldir DESTINATION ${QT_IMPORTS_DIR}/qt5/qml/UbuntuWeather/TimeZone/)

=== added directory 'debian'
=== renamed directory 'debian' => 'debian.moved'
=== added file 'debian/changelog'
--- debian/changelog	1970-01-01 00:00:00 +0000
+++ debian/changelog	2015-03-14 20:11:20 +0000
@@ -0,0 +1,173 @@
+ubuntu-weather-app (2.1) UNRELEASED; urgency=medium
+
+  * 
+
+ -- Daniel Holbach <daniel.holbach@xxxxxxxxxx>  Wed, 04 Feb 2015 11:25:02 +0100
+
+ubuntu-weather-app (2.0) utopic; urgency=low
+
+  [ Andrew Hayzen ]
+  * Initial version of reboot
+
+ -- Andrew Hayzen <ahayzen@xxxxxxxxx>  Mon, 02 Feb 2014 15:10:00 +0000
+
+ubuntu-weather-app (1.8.3ubuntu1) utopic; urgency=low
+
+  [ Dan Chapman ]
+  * Add timezone c++ extension plugin
+
+ -- Dan Chapman <dpniel@xxxxxxxxxx>  Thu, 25 Sep 2014 10:46:29 +0100
+
+ubuntu-weather-app (1.8.3) raring; urgency=low
+
+  [ Martin Borho ]
+  * updated icons for rain and snow
+  * bug fixes and optimizations
+
+  [ Alan Pope ]
+  * Update click framework and apparmor profile version (LP: #1315318) 
+
+ -- Alan Pope <popey@xxxxxxxxxx>  Fri, 02 May 2014 13:51:57 +0100
+
+ubuntu-weather-app (1.8.1ubuntu1) UNRELEASED; urgency=low
+
+  * added missing icon
+
+ -- Martin Borho <martin@xxxxxxxxx>  Sun, 23 Mar 2014 13:06:17 +0000
+
+ubuntu-weather-app (1.8ubuntu1) UNRELEASED; urgency=low
+
+  [Leo Arias]
+  * Port to autopilot 1.4.
+  * Set the objectNames for the settings options.
+
+  [Nicholas Skaggs]
+  * Fixed test_del_add_bug test, simplify tests, re-enable disabled tests.
+  * Redo create blank db, ensure it's being used across all tests.
+  * Add cmake build.
+  * Set revno to match store.
+  * Updated tests to use more emulator standards.
+  * Tweaked activity indicator handling.
+
+  [Dennis O'Flaherty]
+  * Load weather for the selected city when clicking the OWM logo instead of loading for London.
+
+  [Raul Yeguas]
+  * Added an modified logo for OpenWeatherMap (have mercy on me, christina-li and michal) and an action to open its website on click.
+  * Added animations for daily and houly forecast scroll.
+  * Improved animation speed by reducing image size.
+  * Removes SideStage hint, fixes related to the sizes of the current weather data display on tablets.
+
+  [Victor Thompson]
+  * Added unit of measure for main temperature value.
+  * Fixed conversion of wind speed data for TWC data.
+  * Prevent predictive text in search bar.
+
+  [Arash Badie Modiri]
+  * Changed a translation string into something a little bit more general.
+
+  [Martin Borho]
+  * Optimized and added haptic effect to hourly scrolling .
+  * Added The Weather Channel as default weather data provider.
+  * Added keyboard shortcuts.
+  * Initial tablet design of the Weather app.
+  * Refactored all width:parent.width and height:parent.height through corresponding anchors
+  * Fixed edit dialog to fill window on N7 2013 landscape
+  * Fixed weather AP tests to not assume tablet mode
+  * Fixed content sticking to the top, when height was changed in phone mode
+  * Fixed StateNotFoundError failures in image 206
+  * Removed WorkerScriptfor API calls
+  * New icon names used in API clients
+  * Using the current condition icon for the current day
+
+  [David Planella]
+  * Fixed the rule to generate a .pot template after the migration to cmake.
+  * Minor improvements on the weather API: added parameterize(), added locale parameters to TWC calls
+  * Fixes empty TWC URL if locale is not supported.
+  * Adds documentation for the The Weather Channel API.
+  * Makes the provider footer to be full width in order for it to make it look good on a Nexus 7.
+  * Fixes the shorter length header on the N7 using anchors instead of widths.
+
+  [Alan Pope]
+  * New icon.
+
+ -- Martin Borho <martin@xxxxxxxxx>  Fri, 21 Mar 2014 11:26:17 +0000
+
+ubuntu-weather-app (1.0ubuntu1) UNRELEASED; urgency=medium
+
+  * New icon 
+
+ -- Alan Pope <alan.pope@xxxxxxxxxxxxx>  Tue, 18 Feb 2014 21:26:17 +0000
+
+ubuntu-weather-app (1.0) saucy; urgency=low
+
+  [Andrew Starr-Bochicchio]
+  * Use the system locale to determine both the default units and the display of time.
+  * Add predictive search for locations. (LP: #1218910)
+  * Increase visibility of ActivityIndicator (LP: #1218904)
+
+  [David Planella]
+  * Updated translatable strings, a couple of small fixes to ease translations
+
+  [Dennis O'Flaherty]
+  * Fixes for precipitation values.
+
+  [Martin Borho]
+  * Removing a location add adding a new one fails. (LP: #1230297)
+  * Added location lookup via geoip and geonames.org with test (LP: #1187312, #1188726)
+  * Added fixes for new removable ListItem and the changed LocalStorage path. (LP: #1234544)
+  * Added setting for precipitation unit, mm or in.
+  * Location search now via geonames.org. (LP: #1230153)
+  * Updated predefined cities list.
+  * Added component for notifications.
+  * Added bottom margin to scrolling area in tabs and moved warm colors up the gradient color scheme.
+  * Visual improvements and fixes to LocationManagerSheet and AddLocationSheet. (LP: #1198541, #1221734)
+  * Added updated label, removed years from date labels. (LP: #1219200, #1221149, #1221162)
+  * Improved scrolling, added slow/fast scrolling for day/hour scrolling distinction. (LP:# 1221169)
+  * Added option in settings to change the wind speed unit. (LP: #1214934, #1190784)
+  * Dynamic gradient backgrounds according to the visual design.
+  * Added missing localstorage dependency.
+
+  [Nicholas Skaags]
+  * Add support for testing via click packages.
+
+  [Raúl Yeguas]
+  * Updated colors in GradientsMap.js. (LP: #1226746)
+  * Returns the last element to primary information screen and skip animation. (LP: #1227150)
+  * improved CurrentTemp text size
+  * UbuntuAnimations should be used (LP: #1218805)
+  * Replaced ValueSelector with new OptionSelector in Settings.
+  * Implemented a new scrolling.
+  * Added pressure data and modified the transition animation.
+  * Font color should be always white, with drop shadow.
+
+  [Sergio Schvezov]
+  * changing applicationName to com.ubuntu.weather to write in the unconfined areas
+  * cleaning up python code
+  * add the click data into the package and added apparmor rules (LP: #121839)
+
+ -- Martin Borho <martin@xxxxxxxxx>  Tue, 8 Oct 2013 22:42:00 +0200
+
+ubuntu-weather-app (0.3) raring; urgency=low
+  
+  * LocationManager-Sheet added
+  * Settings-Sheet added
+  * Autopilot tests
+  * Data-provider calls now in WorkerScript
+  * ActivityIndicators while loading
+  * Suru theme added
+  * Displaying more weather data for every location
+
+ -- Martin Borho <martin@xxxxxxxxx>  Tue, 13 Aug 2013 21:33:33 +0200
+
+ubuntu-weather-app (0.2) raring; urgency=low
+
+  * Added support for packaging and installing translations 
+
+ -- David Planella <david.planella@xxxxxxxxxx>  Tue, 28 May 2013 14:21:33 +0200
+
+ubuntu-weather-app (0.1) raring; urgency=low
+
+  * Initial release
+
+ -- Michael Hall <mhall119@xxxxxxxxxx>  Mon, 11 Feb 2013 16:04:00 -0500

=== added file 'debian/compat'
--- debian/compat	1970-01-01 00:00:00 +0000
+++ debian/compat	2015-03-14 20:11:20 +0000
@@ -0,0 +1,1 @@
+9

=== added file 'debian/control'
--- debian/control	1970-01-01 00:00:00 +0000
+++ debian/control	2015-03-14 20:11:20 +0000
@@ -0,0 +1,41 @@
+Source: ubuntu-weather-app
+Priority: extra
+Maintainer: Ubuntu App Cats <ubuntu-touch-coreapps@xxxxxxxxxxxxxxxxxxx>
+Build-Depends: cmake,
+               debhelper (>= 9),
+               gettext,
+               intltool,
+               python3 | python-all | python3-dev | python3-all-dev,
+               python3-minimal,
+               qtbase5-dev,
+               qtbase5-dev-tools,
+               qtdeclarative5-dev,
+               qtdeclarative5-dev-tools,
+               qtdeclarative5-ubuntu-ui-toolkit-plugin,
+Standards-Version: 3.9.6
+Section: misc
+Homepage: https://launchpad.net/ubuntu-weather-app
+Vcs-Bzr: https://code.launchpad.net/~ubuntu-weather-dev/ubuntu-weather-app/trunk
+
+Package: ubuntu-weather-app
+Architecture: all
+Depends: qmlscene,
+         qtdeclarative5-localstorage-plugin,
+         qml-module-qt-labs-settings,
+         qtdeclarative5-qtquick2-plugin,
+         qtdeclarative5-ubuntu-ui-toolkit-plugin | qt-components-ubuntu,
+         suru-icon-theme | ubuntu-mobile-icons,
+         ${misc:Depends}
+Description: Weather application
+ Core Weather application
+
+Package: ubuntu-weather-app-autopilot
+Architecture: all
+Depends: libautopilot-qt (>= 1.4),
+         libqt5test5,
+         libqt5widgets5,
+         ubuntu-ui-toolkit-autopilot,
+         ubuntu-weather-app (>= ${source:Version}),
+         ${misc:Depends}
+Description: Test package for the weather app
+ Autopilot tests for the weather app package

=== added file 'debian/copyright'
--- debian/copyright	1970-01-01 00:00:00 +0000
+++ debian/copyright	2015-03-14 20:11:20 +0000
@@ -0,0 +1,54 @@
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: ubuntu-weather-app
+Source: https://launchpad.net/ubuntu-weather-app
+
+Files: *
+Copyright: 2013 Canonical Ltd.
+           2013 Jakub <jakub@xxxxxxxxxxx>
+           2013 Martin Borho <martin@xxxxxxxxx>
+           2013 Nekhelesh Ramananthan <krnekhelesh@xxxxxxxxx>
+           2013 Omer Akram <om26er@xxxxxxxxxx>
+           2013 Raúl Yeguas <neokore@xxxxxxxxx>
+           2013 Riccardo Padovani <rpadovani@xxxxxxxxxxxxx>
+           2013 Zonov Roman <roman2861@xxxxxxxxx>
+           2015 Andrew Hayzen <ahayzen@xxxxxxxxx>
+           2015 Victor Thompson <victor.thompson@xxxxxxxxx>
+License: GPL-3
+
+Files: debian/*
+Copyright: 2013 Canonical Ltd.
+License: LGPL-3
+
+License: GPL-3
+ This package is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License.
+ .
+ This package 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, see <http://www.gnu.org/licenses/>.
+ .
+ On Debian systems, the complete text of the GNU General
+ Public License can be found in "/usr/share/common-licenses/GPL-3".
+
+License: LGPL-3
+ This package is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License.
+ .
+ This package is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ Lesser General Public License for more details.
+ .
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ .
+ On Debian systems, the complete text of the GNU Lesser General
+ Public License can be found in "/usr/share/common-licenses/LGPL-3".

=== added file 'debian/rules'
--- debian/rules	1970-01-01 00:00:00 +0000
+++ debian/rules	2015-03-14 20:11:20 +0000
@@ -0,0 +1,14 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+%:
+	dh $@
+
+override_dh_auto_configure:
+	dh_auto_configure -- -DCLICK_MODE=OFF
+
+override_dh_install:
+	dh_install --list-missing

=== added directory 'debian/source'
=== added file 'debian/source/format'
--- debian/source/format	1970-01-01 00:00:00 +0000
+++ debian/source/format	2015-03-14 20:11:20 +0000
@@ -0,0 +1,1 @@
+1.0

=== added file 'debian/ubuntu-weather-app-autopilot.install'
--- debian/ubuntu-weather-app-autopilot.install	1970-01-01 00:00:00 +0000
+++ debian/ubuntu-weather-app-autopilot.install	2015-03-14 20:11:20 +0000
@@ -0,0 +1,1 @@
+usr/lib

=== added file 'debian/ubuntu-weather-app.install'
--- debian/ubuntu-weather-app.install	1970-01-01 00:00:00 +0000
+++ debian/ubuntu-weather-app.install	2015-03-14 20:11:20 +0000
@@ -0,0 +1,1 @@
+usr/share

=== added file 'manifest.json.in'
--- manifest.json.in	1970-01-01 00:00:00 +0000
+++ manifest.json.in	2015-03-14 20:11:20 +0000
@@ -0,0 +1,23 @@
+{
+    "architecture": "@CLICK_ARCH@",
+    "description": "A weather forecast application for Ubuntu with support for multiple online weather data sources",
+    "framework": "ubuntu-sdk-14.10",
+    "hooks": {
+        "weather": {
+            "apparmor": "ubuntu-weather-app.apparmor",
+            "desktop": "@CMAKE_INSTALL_DATADIR@/applications/ubuntu-weather-app.desktop"
+        }
+    },
+    "icon": "@ICON@",
+    "maintainer": "Ubuntu App Cats <ubuntu-touch-coreapps@xxxxxxxxxxxxxxxxxxx>",
+    "name": "@PROJECT_NAME@",
+    "title": "Weather",
+    "version": "3.0.@BZR_REVNO@",
+    "x-source": {
+        "vcs-bzr": "@BZR_SOURCE@",
+        "vcs-bzr-revno": "@BZR_REVNO@"
+    },
+    "x-test": {
+        "autopilot": "@AUTOPILOT_DIR@"
+    }
+}

=== added directory 'po'
=== renamed directory 'po' => 'po.moved'
=== added file 'po/CMakeLists.txt'
--- po/CMakeLists.txt	1970-01-01 00:00:00 +0000
+++ po/CMakeLists.txt	2015-03-14 20:11:20 +0000
@@ -0,0 +1,33 @@
+include(FindGettext)
+find_program(GETTEXT_XGETTEXT_EXECUTABLE xgettext)
+
+set(DOMAIN ${PROJECT_NAME})
+set(POT_FILE ${DOMAIN}.pot)
+file(GLOB PO_FILES *.po)
+
+# Creates the .pot file containing the translations template
+add_custom_target(${POT_FILE} ALL
+    COMMENT "Generating translation template"
+    COMMAND ${INTLTOOL_EXTRACT} --update --type=gettext/ini
+        --srcdir=${CMAKE_SOURCE_DIR} ${DESKTOP_FILE}.in.in
+    COMMAND ${GETTEXT_XGETTEXT_EXECUTABLE} -o ${POT_FILE}
+        -D ${CMAKE_CURRENT_SOURCE_DIR}
+        -D ${CMAKE_CURRENT_BINARY_DIR}
+        --from-code=UTF-8
+        --c++ --qt --add-comments=TRANSLATORS
+        --keyword=tr --keyword=tr:1,2 --keyword=N_
+        --package-name='${APP_HARDCODE}'
+        --copyright-holder='Canonical Ltd.'
+        ${I18N_SRC_FILES}
+    COMMAND ${CMAKE_COMMAND} -E copy ${POT_FILE} ${CMAKE_CURRENT_SOURCE_DIR})
+
+# Builds the binary translations catalog for each language
+# it finds source translations (*.po) for
+foreach(PO_FILE ${PO_FILES})
+    get_filename_component(LANG ${PO_FILE} NAME_WE)
+    gettext_process_po_files(${LANG} ALL PO_FILES ${PO_FILE})
+    set(INSTALL_DIR ${CMAKE_INSTALL_LOCALEDIR}/${LANG}/LC_MESSAGES)
+    install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${LANG}.gmo
+            DESTINATION ${INSTALL_DIR}
+            RENAME ${DOMAIN}.mo)
+endforeach(PO_FILE)

=== added file 'po/com.ubuntu.weather.pot'
--- po/com.ubuntu.weather.pot	1970-01-01 00:00:00 +0000
+++ po/com.ubuntu.weather.pot	2015-03-14 20:11:20 +0000
@@ -0,0 +1,128 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR Canonical Ltd.
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: ubuntu-weather-app\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2015-03-14 11:54+0100\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@xxxxxx>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../app/components/HomeTempInfo.qml:38
+msgid "Today"
+msgstr ""
+
+#: ../app/components/ListItemActions/Remove.qml:26
+msgid "Remove"
+msgstr ""
+
+#: ../app/components/MultiSelectHeadState.qml:27
+msgid "Select All"
+msgstr ""
+
+#: ../app/components/MultiSelectHeadState.qml:39
+msgid "Delete"
+msgstr ""
+
+#: ../app/components/MultiSelectHeadState.qml:51
+msgid "Cancel selection"
+msgstr ""
+
+#: ../app/ui/AddLocationPage.qml:30
+msgid "Select a city"
+msgstr ""
+
+#: ../app/ui/AddLocationPage.qml:47
+msgid "City"
+msgstr ""
+
+#: ../app/ui/AddLocationPage.qml:62
+msgid "Back"
+msgstr ""
+
+#: ../app/ui/AddLocationPage.qml:89
+msgid "Search city"
+msgstr ""
+
+#: ../app/ui/AddLocationPage.qml:254
+msgid "No city found"
+msgstr ""
+
+#: ../app/ui/AddLocationPage.qml:267
+msgid "Couldn't load weather data, please try later again!"
+msgstr ""
+
+#: ../app/ui/AddLocationPage.qml:277
+msgid "Location already added."
+msgstr ""
+
+#: ../app/ui/AddLocationPage.qml:280
+msgid "OK"
+msgstr ""
+
+#: ../app/ui/HomePage.qml:30 ../app/ui/LocationsPage.qml:30
+msgid "Locations"
+msgstr ""
+
+#: ../app/ui/SettingsPage.qml:24
+msgid "Settings"
+msgstr ""
+
+#: ../app/ui/SettingsPage.qml:41 ../app/ui/settings/UnitsPage.qml:25
+msgid "Units"
+msgstr ""
+
+#: ../app/ui/SettingsPage.qml:48 ../app/ui/settings/DataProviderPage.qml:25
+msgid "Data Provider"
+msgstr ""
+
+#: ../app/ui/SettingsPage.qml:55 ../app/ui/settings/RefreshIntervalPage.qml:25
+msgid "Refresh Interval"
+msgstr ""
+
+#: ../app/ui/settings/DataProviderPage.qml:44
+msgid "Provider"
+msgstr ""
+
+#: ../app/ui/settings/RefreshIntervalPage.qml:42
+msgid "minutes"
+msgstr ""
+
+#: ../app/ui/settings/RefreshIntervalPage.qml:47
+msgid "Interval"
+msgstr ""
+
+#: ../app/ui/settings/UnitsPage.qml:44
+msgid "Temperature"
+msgstr ""
+
+#: ../app/ui/settings/UnitsPage.qml:56
+msgid "Precipitation"
+msgstr ""
+
+#: ../app/ui/settings/UnitsPage.qml:67
+msgid "Wind Speed"
+msgstr ""
+
+#: ubuntu-weather-app.desktop.in.in.h:1
+msgid "Weather"
+msgstr ""
+
+#: ubuntu-weather-app.desktop.in.in.h:2
+msgid ""
+"A weather forecast application for Ubuntu with support for multiple online "
+"weather data sources"
+msgstr ""
+
+#: ubuntu-weather-app.desktop.in.in.h:3
+msgid "weather;forecast;twc;openweathermap;the weather channel;"
+msgstr ""

=== added directory 'tests'
=== renamed directory 'tests' => 'tests.moved'
=== added file 'tests/CMakeLists.txt'
--- tests/CMakeLists.txt	1970-01-01 00:00:00 +0000
+++ tests/CMakeLists.txt	2015-03-14 20:11:20 +0000
@@ -0,0 +1,3 @@
+if(NOT CLICK_MODE)
+  add_subdirectory(autopilot)
+endif(NOT CLICK_MODE)

=== added directory 'tests/autopilot'
=== added file 'tests/autopilot/CMakeLists.txt'
--- tests/autopilot/CMakeLists.txt	1970-01-01 00:00:00 +0000
+++ tests/autopilot/CMakeLists.txt	2015-03-14 20:11:20 +0000
@@ -0,0 +1,8 @@
+if(INSTALL_TESTS)
+execute_process(COMMAND python3 -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())"
+    OUTPUT_VARIABLE PYTHON_PACKAGE_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+install(DIRECTORY ${AUTOPILOT_DIR}
+    DESTINATION ${PYTHON_PACKAGE_DIR}
+    )
+endif(INSTALL_TESTS)

=== added directory 'tests/autopilot/ubuntu_weather_app'
=== added file 'ubuntu-weather-app.apparmor'
--- ubuntu-weather-app.apparmor	1970-01-01 00:00:00 +0000
+++ ubuntu-weather-app.apparmor	2015-03-14 20:11:20 +0000
@@ -0,0 +1,6 @@
+{
+    "policy_groups": [
+    	"networking"
+    ], 
+    "policy_version": 1.2
+}

=== added file 'ubuntu-weather-app.desktop.in.in'
--- ubuntu-weather-app.desktop.in.in	1970-01-01 00:00:00 +0000
+++ ubuntu-weather-app.desktop.in.in	2015-03-14 20:11:20 +0000
@@ -0,0 +1,12 @@
+[Desktop Entry]
+Version=1.0
+Type=Application
+Terminal=false
+Exec=@EXEC@
+Icon=@ICON@
+_Name=Weather
+_Comment=A weather forecast application for Ubuntu with support for multiple online weather data sources
+_Keywords=weather;forecast;twc;openweathermap;the weather channel;
+X-Ubuntu-Touch=true
+X-Ubuntu-Default-Department-ID=accessories
+X-Ubuntu-Splash-Color=#F5F5F5