Merge lp:~ubuntu-weather-dev/ubuntu-weather-app/reboot-finish-listitem-migration into lp:ubuntu-weather-app/obsolete.trunk

Proposed by Victor Thompson on 2015-08-05
Status: Superseded
Proposed branch: lp:~ubuntu-weather-dev/ubuntu-weather-app/reboot-finish-listitem-migration
Merge into: lp:ubuntu-weather-app/obsolete.trunk
Diff against target: 13647 lines (+13151/-0) (has conflicts)
89 files modified
.bzrignore (+19/-0)
AUTHORS (+9/-0)
CMakeLists.txt (+101/-0)
COPYING (+674/-0)
README (+11/-0)
README.autopilot (+57/-0)
README.translations (+37/-0)
app/CMakeLists.txt (+16/-0)
app/components/CMakeLists.txt (+5/-0)
app/components/CurrentLocation.qml (+97/-0)
app/components/DayDelegate.qml (+248/-0)
app/components/EmptyStateComponent.qml (+67/-0)
app/components/ExpandableListItem.qml (+90/-0)
app/components/FakeHeader.qml (+39/-0)
app/components/FastScroll.js (+130/-0)
app/components/FastScroll.qml (+321/-0)
app/components/ForecastDetailsDelegate.qml (+49/-0)
app/components/HeaderRow.qml (+44/-0)
app/components/HomeGraphic.qml (+39/-0)
app/components/HomeHourly.qml (+101/-0)
app/components/HomeTempInfo.qml (+73/-0)
app/components/LoadingIndicator.qml (+87/-0)
app/components/MultiSelectListView.qml (+71/-0)
app/components/PageWithBottomEdge.qml (+461/-0)
app/components/SettingsButton.qml (+43/-0)
app/components/StandardListItem.qml (+48/-0)
app/components/WeatherListView.qml (+31/-0)
app/data/CMakeLists.txt (+5/-0)
app/data/CitiesList.js (+65/-0)
app/data/Storage.qml (+214/-0)
app/data/WeatherApi.js (+760/-0)
app/data/key.js (+1/-0)
app/data/suncalc.js (+300/-0)
app/graphics/CMakeLists.txt (+5/-0)
app/graphics/extended-information_chance-of-rain.svg (+153/-0)
app/graphics/extended-information_humidity.svg (+147/-0)
app/graphics/extended-information_sunrise.svg (+88/-0)
app/graphics/extended-information_sunset.svg (+88/-0)
app/graphics/extended-information_uv-level.svg (+270/-0)
app/graphics/extended-information_wind.svg (+155/-0)
app/ubuntu-weather-app.qml (+292/-0)
app/ui/AddLocationPage.qml (+310/-0)
app/ui/CMakeLists.txt (+7/-0)
app/ui/HomePage.qml (+198/-0)
app/ui/LocationPane.qml (+200/-0)
app/ui/LocationsPage.qml (+308/-0)
app/ui/SettingsPage.qml (+51/-0)
app/ui/settings/CMakeLists.txt (+5/-0)
app/ui/settings/DataProviderPage.qml (+61/-0)
app/ui/settings/LocationPage.qml (+36/-0)
app/ui/settings/RefreshIntervalPage.qml (+55/-0)
app/ui/settings/UnitsPage.qml (+111/-0)
debian/changelog (+197/-0)
debian/compat (+1/-0)
debian/control (+41/-0)
debian/copyright (+79/-0)
debian/rules (+14/-0)
debian/source/format (+1/-0)
debian/ubuntu-weather-app-autopilot.install (+1/-0)
debian/ubuntu-weather-app.install (+1/-0)
manifest.json.in (+23/-0)
po/CMakeLists.txt (+33/-0)
po/com.ubuntu.weather.pot (+196/-0)
po/de.po (+216/-0)
po/en_GB.po (+214/-0)
po/fr.po (+216/-0)
po/gl.po (+214/-0)
po/hu.po (+213/-0)
po/pl.po (+216/-0)
po/pt.po (+213/-0)
po/ru.po (+213/-0)
po/sv.po (+212/-0)
po/zh_TW.po (+212/-0)
tests/CMakeLists.txt (+1/-0)
tests/autopilot/CMakeLists.txt (+10/-0)
tests/autopilot/ubuntu_weather_app/CMakeLists.txt (+10/-0)
tests/autopilot/ubuntu_weather_app/__init__.py (+113/-0)
tests/autopilot/ubuntu_weather_app/databases/34e1e542f2f083ff18f537b07a380071.ini (+6/-0)
tests/autopilot/ubuntu_weather_app/databases/CMakeLists.txt (+6/-0)
tests/autopilot/ubuntu_weather_app/databases/location_added.conf (+3/-0)
tests/autopilot/ubuntu_weather_app/files/1.json (+1509/-0)
tests/autopilot/ubuntu_weather_app/files/2.json (+1509/-0)
tests/autopilot/ubuntu_weather_app/files/CMakeLists.txt (+6/-0)
tests/autopilot/ubuntu_weather_app/tests/CMakeLists.txt (+6/-0)
tests/autopilot/ubuntu_weather_app/tests/__init__.py (+303/-0)
tests/autopilot/ubuntu_weather_app/tests/test_empty_state.py (+37/-0)
tests/autopilot/ubuntu_weather_app/tests/test_home_page.py (+32/-0)
ubuntu-weather-app.apparmor (+8/-0)
ubuntu-weather-app.desktop.in.in (+13/-0)
Conflict adding file .bzrignore.  Moved existing file to .bzrignore.moved.
Conflict adding file AUTHORS.  Moved existing file to AUTHORS.moved.
Conflict adding file CMakeLists.txt.  Moved existing file to CMakeLists.txt.moved.
Conflict adding file COPYING.  Moved existing file to COPYING.moved.
Conflict adding file README.  Moved existing file to README.moved.
Conflict adding file README.translations.  Moved existing file to README.translations.moved.
Conflict adding file debian.  Moved existing file to debian.moved.
Conflict adding file po.  Moved existing file to po.moved.
Conflict adding file tests.  Moved existing file to tests.moved.
To merge this branch: bzr merge lp:~ubuntu-weather-dev/ubuntu-weather-app/reboot-finish-listitem-migration
Reviewer Review Type Date Requested Status
Ubuntu Weather Developers 2015-08-05 Pending
Review via email: mp+266980@code.launchpad.net

Commit message

This MP implements the following,

* Finish the migration to the new SDK list items
* Lock weather app in the Portrait orientation until we get some landscape designs
* Make the page with bottom edge animation less jarring when it reaches the top by adding a fake header (similar to what the clock app does)

Description of the change

* Finished the migration to the new SDK list items
* Locked weather app in the Portrait orientation until we get some landscape designs
* Made the page with bottom edge animation less jarring when it reaches the top by adding a fake header (similar to what the clock app does)
* Buy nik90 a beer for starting this effort

To post a comment you must log in.
71. By Victor Thompson on 2015-08-14

Update for comments

72. By Victor Thompson on 2015-08-14

Fix visual issues

73. By Victor Thompson on 2015-08-22

Merge and resolve conflicts

74. By Victor Thompson on 2015-09-01

Merge of trunk

75. By Victor Thompson on 2015-09-25

Merge and resolve conflicts.

76. By Victor Thompson on 2015-09-25

fix bad conflict resolution

77. By Victor Thompson on 2015-10-18

Merge of trunk and resolve conflicts.

78. By Victor Thompson on 2015-10-18

Removed bad FakeHeader

79. By Victor Thompson on 2015-10-18

Fix pot file

80. By Victor Thompson on 2015-11-20

Merge of trunk

81. By Victor Thompson on 2015-11-20

rebase on UC1.3 branch

82. By Victor Thompson on 2015-12-10

Merge trunk, resolve conflicts, and add changelog entry

Unmerged revisions

82. By Victor Thompson on 2015-12-10

Merge trunk, resolve conflicts, and add changelog entry

81. By Victor Thompson on 2015-11-20

rebase on UC1.3 branch

80. By Victor Thompson on 2015-11-20

Merge of trunk

79. By Victor Thompson on 2015-10-18

Fix pot file

78. By Victor Thompson on 2015-10-18

Removed bad FakeHeader

77. By Victor Thompson on 2015-10-18

Merge of trunk and resolve conflicts.

76. By Victor Thompson on 2015-09-25

fix bad conflict resolution

75. By Victor Thompson on 2015-09-25

Merge and resolve conflicts.

74. By Victor Thompson on 2015-09-01

Merge of trunk

73. By Victor Thompson on 2015-08-22

Merge and resolve conflicts

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file '.bzrignore'
2--- .bzrignore 1970-01-01 00:00:00 +0000
3+++ .bzrignore 2015-08-05 00:17:22 +0000
4@@ -0,0 +1,19 @@
5+*.user*
6+debian/files
7+debian/tmp
8+debian/ubuntu-weather-app*
9+debian/app-template/
10+debian/*.debhelper.log
11+debian/*.substvars
12+.build
13+Makefile
14+CMakeCache.txt
15+CMakeFiles/
16+*.cmake
17+*.gmo
18+*.mo
19+*.desktop
20+*.desktop.in
21+*.desktop.in.in.h
22+.excludes
23+ubuntu-weather-app.json
24
25=== renamed file '.bzrignore' => '.bzrignore.moved'
26=== added file 'AUTHORS'
27--- AUTHORS 1970-01-01 00:00:00 +0000
28+++ AUTHORS 2015-08-05 00:17:22 +0000
29@@ -0,0 +1,9 @@
30+Weather reboot was started in 2015 and has received many contributions from the following people.
31+
32+In addition, numerous translations, bug reports and other non-code contributions have been made which are equally valued.
33+
34+Andrew Hayzen <ahayzen@gmail.com>
35+Daniel Holbach <daniel.holbach@canonical.com>
36+Martin Borho <martin@borho.net>
37+Nekhelesh Ramananthan <krnekhelesh@gmail.com>
38+Victor Thompson <victor.thompson@gmail.com>
39
40=== renamed file 'AUTHORS' => 'AUTHORS.moved'
41=== added file 'CMakeLists.txt'
42--- CMakeLists.txt 1970-01-01 00:00:00 +0000
43+++ CMakeLists.txt 2015-08-05 00:17:22 +0000
44@@ -0,0 +1,101 @@
45+project(com.ubuntu.weather)
46+cmake_minimum_required(VERSION 2.8.9)
47+
48+find_program(INTLTOOL_MERGE intltool-merge)
49+if(NOT INTLTOOL_MERGE)
50+ message(FATAL_ERROR "Could not find intltool-merge, please install the intltool package")
51+endif()
52+find_program(INTLTOOL_EXTRACT intltool-extract)
53+if(NOT INTLTOOL_EXTRACT)
54+ message(FATAL_ERROR "Could not find intltool-extract, please install the intltool package")
55+endif()
56+
57+set (UBUNTU_MANIFEST_PATH "manifest.json.in" CACHE INTERNAL "Relative path to the manifest file")
58+set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-permissive -pedantic -Wall -Wextra")
59+
60+find_package(Qt5Core REQUIRED)
61+find_package(Qt5Qml REQUIRED)
62+find_package(Qt5Quick REQUIRED)
63+
64+# Automatically create moc files
65+set(CMAKE_AUTOMOC ON)
66+
67+option(INSTALL_TESTS "Install the tests on make install" on)
68+option(CLICK_MODE "Build as a click package" on)
69+
70+# Tests
71+enable_testing()
72+
73+# Standard install paths
74+include(GNUInstallDirs)
75+
76+set(APP_NAME weather)
77+set(APP_HARDCODE ubuntu-weather-app)
78+set(MAIN_QML ${APP_HARDCODE}.qml)
79+set(DESKTOP_FILE "${APP_HARDCODE}.desktop")
80+set(ICON_FILE weather-app@30.png)
81+set(AUTOPILOT_DIR ubuntu_weather_app)
82+
83+# Set install paths
84+if(CLICK_MODE)
85+ set(CMAKE_INSTALL_PREFIX "/")
86+ set(UBUNTU-WEATHER_APP_DIR "${CMAKE_INSTALL_DATADIR}/qml")
87+
88+ set(QT_IMPORTS_DIR "${CMAKE_INSTALL_LIBDIR}")
89+ set(EXEC "qmlscene $@ ${UBUNTU-WEATHER_APP_DIR}/${MAIN_QML}")
90+ set(MODULE_PATH ${QT_IMPORTS_DIR})
91+ if(NOT BZR_REVNO)
92+ execute_process(
93+ COMMAND bzr revno
94+ OUTPUT_VARIABLE BZR_REVNO
95+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
96+ OUTPUT_STRIP_TRAILING_WHITESPACE
97+ )
98+ endif(NOT BZR_REVNO)
99+ if(NOT BZR_SOURCE)
100+ set(BZR_SOURCE "lp:${APP_HARDCODE}/reboot")
101+ message("-- Setting BZR_SOURCE to ${BZR_SOURCE}")
102+ endif(NOT BZR_SOURCE)
103+else(CLICK_MODE)
104+ set(UBUNTU-WEATHER_APP_DIR "${CMAKE_INSTALL_DATADIR}/ubuntu-weather-app")
105+ execute_process(
106+ COMMAND qmake -query QT_INSTALL_QML
107+ OUTPUT_VARIABLE QT_IMPORTS_DIR
108+ OUTPUT_STRIP_TRAILING_WHITESPACE
109+ )
110+ set(MODULE_PATH ${QT_IMPORTS_DIR}/WeatherApp)
111+endif(CLICK_MODE)
112+
113+if(${CLICK_MODE})
114+ message("-- Configuring manifest.json")
115+
116+ configure_file(${UBUNTU_MANIFEST_PATH} ${CMAKE_CURRENT_BINARY_DIR}/manifest.json)
117+ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/manifest.json DESTINATION ${CMAKE_INSTALL_PREFIX})
118+ install(FILES "${APP_HARDCODE}.apparmor" DESTINATION ${CMAKE_INSTALL_PREFIX})
119+else(CLICK_MODE)
120+ set(EXEC "qmlscene $@ -I ${MODULE_PATH} ${CMAKE_INSTALL_PREFIX}/${UBUNTU-WEATHER_APP_DIR}/${MAIN_QML}")
121+endif()
122+
123+
124+file(GLOB_RECURSE I18N_SRC_FILES
125+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/po
126+ *.qml *.js)
127+list(APPEND I18N_SRC_FILES ${DESKTOP_FILE}.in.in.h)
128+list(SORT I18N_SRC_FILES)
129+
130+configure_file(${DESKTOP_FILE}.in.in ${DESKTOP_FILE}.in)
131+
132+add_custom_target(${DESKTOP_FILE} ALL
133+ COMMENT "Merging translations into ${DESKTOP_FILE}..."
134+ COMMAND LC_ALL=C ${INTLTOOL_MERGE} -d -u ${CMAKE_SOURCE_DIR}/po ${DESKTOP_FILE}.in ${DESKTOP_FILE}
135+)
136+
137+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${DESKTOP_FILE}
138+ DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
139+
140+add_subdirectory(app)
141+add_subdirectory(po)
142+add_subdirectory(tests)
143+
144+# TODO: Add custom target for autopilot and run.
145+
146
147=== renamed file 'CMakeLists.txt' => 'CMakeLists.txt.moved'
148=== added file 'COPYING'
149--- COPYING 1970-01-01 00:00:00 +0000
150+++ COPYING 2015-08-05 00:17:22 +0000
151@@ -0,0 +1,674 @@
152+ GNU GENERAL PUBLIC LICENSE
153+ Version 3, 29 June 2007
154+
155+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
156+ Everyone is permitted to copy and distribute verbatim copies
157+ of this license document, but changing it is not allowed.
158+
159+ Preamble
160+
161+ The GNU General Public License is a free, copyleft license for
162+software and other kinds of works.
163+
164+ The licenses for most software and other practical works are designed
165+to take away your freedom to share and change the works. By contrast,
166+the GNU General Public License is intended to guarantee your freedom to
167+share and change all versions of a program--to make sure it remains free
168+software for all its users. We, the Free Software Foundation, use the
169+GNU General Public License for most of our software; it applies also to
170+any other work released this way by its authors. You can apply it to
171+your programs, too.
172+
173+ When we speak of free software, we are referring to freedom, not
174+price. Our General Public Licenses are designed to make sure that you
175+have the freedom to distribute copies of free software (and charge for
176+them if you wish), that you receive source code or can get it if you
177+want it, that you can change the software or use pieces of it in new
178+free programs, and that you know you can do these things.
179+
180+ To protect your rights, we need to prevent others from denying you
181+these rights or asking you to surrender the rights. Therefore, you have
182+certain responsibilities if you distribute copies of the software, or if
183+you modify it: responsibilities to respect the freedom of others.
184+
185+ For example, if you distribute copies of such a program, whether
186+gratis or for a fee, you must pass on to the recipients the same
187+freedoms that you received. You must make sure that they, too, receive
188+or can get the source code. And you must show them these terms so they
189+know their rights.
190+
191+ Developers that use the GNU GPL protect your rights with two steps:
192+(1) assert copyright on the software, and (2) offer you this License
193+giving you legal permission to copy, distribute and/or modify it.
194+
195+ For the developers' and authors' protection, the GPL clearly explains
196+that there is no warranty for this free software. For both users' and
197+authors' sake, the GPL requires that modified versions be marked as
198+changed, so that their problems will not be attributed erroneously to
199+authors of previous versions.
200+
201+ Some devices are designed to deny users access to install or run
202+modified versions of the software inside them, although the manufacturer
203+can do so. This is fundamentally incompatible with the aim of
204+protecting users' freedom to change the software. The systematic
205+pattern of such abuse occurs in the area of products for individuals to
206+use, which is precisely where it is most unacceptable. Therefore, we
207+have designed this version of the GPL to prohibit the practice for those
208+products. If such problems arise substantially in other domains, we
209+stand ready to extend this provision to those domains in future versions
210+of the GPL, as needed to protect the freedom of users.
211+
212+ Finally, every program is threatened constantly by software patents.
213+States should not allow patents to restrict development and use of
214+software on general-purpose computers, but in those that do, we wish to
215+avoid the special danger that patents applied to a free program could
216+make it effectively proprietary. To prevent this, the GPL assures that
217+patents cannot be used to render the program non-free.
218+
219+ The precise terms and conditions for copying, distribution and
220+modification follow.
221+
222+ TERMS AND CONDITIONS
223+
224+ 0. Definitions.
225+
226+ "This License" refers to version 3 of the GNU General Public License.
227+
228+ "Copyright" also means copyright-like laws that apply to other kinds of
229+works, such as semiconductor masks.
230+
231+ "The Program" refers to any copyrightable work licensed under this
232+License. Each licensee is addressed as "you". "Licensees" and
233+"recipients" may be individuals or organizations.
234+
235+ To "modify" a work means to copy from or adapt all or part of the work
236+in a fashion requiring copyright permission, other than the making of an
237+exact copy. The resulting work is called a "modified version" of the
238+earlier work or a work "based on" the earlier work.
239+
240+ A "covered work" means either the unmodified Program or a work based
241+on the Program.
242+
243+ To "propagate" a work means to do anything with it that, without
244+permission, would make you directly or secondarily liable for
245+infringement under applicable copyright law, except executing it on a
246+computer or modifying a private copy. Propagation includes copying,
247+distribution (with or without modification), making available to the
248+public, and in some countries other activities as well.
249+
250+ To "convey" a work means any kind of propagation that enables other
251+parties to make or receive copies. Mere interaction with a user through
252+a computer network, with no transfer of a copy, is not conveying.
253+
254+ An interactive user interface displays "Appropriate Legal Notices"
255+to the extent that it includes a convenient and prominently visible
256+feature that (1) displays an appropriate copyright notice, and (2)
257+tells the user that there is no warranty for the work (except to the
258+extent that warranties are provided), that licensees may convey the
259+work under this License, and how to view a copy of this License. If
260+the interface presents a list of user commands or options, such as a
261+menu, a prominent item in the list meets this criterion.
262+
263+ 1. Source Code.
264+
265+ The "source code" for a work means the preferred form of the work
266+for making modifications to it. "Object code" means any non-source
267+form of a work.
268+
269+ A "Standard Interface" means an interface that either is an official
270+standard defined by a recognized standards body, or, in the case of
271+interfaces specified for a particular programming language, one that
272+is widely used among developers working in that language.
273+
274+ The "System Libraries" of an executable work include anything, other
275+than the work as a whole, that (a) is included in the normal form of
276+packaging a Major Component, but which is not part of that Major
277+Component, and (b) serves only to enable use of the work with that
278+Major Component, or to implement a Standard Interface for which an
279+implementation is available to the public in source code form. A
280+"Major Component", in this context, means a major essential component
281+(kernel, window system, and so on) of the specific operating system
282+(if any) on which the executable work runs, or a compiler used to
283+produce the work, or an object code interpreter used to run it.
284+
285+ The "Corresponding Source" for a work in object code form means all
286+the source code needed to generate, install, and (for an executable
287+work) run the object code and to modify the work, including scripts to
288+control those activities. However, it does not include the work's
289+System Libraries, or general-purpose tools or generally available free
290+programs which are used unmodified in performing those activities but
291+which are not part of the work. For example, Corresponding Source
292+includes interface definition files associated with source files for
293+the work, and the source code for shared libraries and dynamically
294+linked subprograms that the work is specifically designed to require,
295+such as by intimate data communication or control flow between those
296+subprograms and other parts of the work.
297+
298+ The Corresponding Source need not include anything that users
299+can regenerate automatically from other parts of the Corresponding
300+Source.
301+
302+ The Corresponding Source for a work in source code form is that
303+same work.
304+
305+ 2. Basic Permissions.
306+
307+ All rights granted under this License are granted for the term of
308+copyright on the Program, and are irrevocable provided the stated
309+conditions are met. This License explicitly affirms your unlimited
310+permission to run the unmodified Program. The output from running a
311+covered work is covered by this License only if the output, given its
312+content, constitutes a covered work. This License acknowledges your
313+rights of fair use or other equivalent, as provided by copyright law.
314+
315+ You may make, run and propagate covered works that you do not
316+convey, without conditions so long as your license otherwise remains
317+in force. You may convey covered works to others for the sole purpose
318+of having them make modifications exclusively for you, or provide you
319+with facilities for running those works, provided that you comply with
320+the terms of this License in conveying all material for which you do
321+not control copyright. Those thus making or running the covered works
322+for you must do so exclusively on your behalf, under your direction
323+and control, on terms that prohibit them from making any copies of
324+your copyrighted material outside their relationship with you.
325+
326+ Conveying under any other circumstances is permitted solely under
327+the conditions stated below. Sublicensing is not allowed; section 10
328+makes it unnecessary.
329+
330+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
331+
332+ No covered work shall be deemed part of an effective technological
333+measure under any applicable law fulfilling obligations under article
334+11 of the WIPO copyright treaty adopted on 20 December 1996, or
335+similar laws prohibiting or restricting circumvention of such
336+measures.
337+
338+ When you convey a covered work, you waive any legal power to forbid
339+circumvention of technological measures to the extent such circumvention
340+is effected by exercising rights under this License with respect to
341+the covered work, and you disclaim any intention to limit operation or
342+modification of the work as a means of enforcing, against the work's
343+users, your or third parties' legal rights to forbid circumvention of
344+technological measures.
345+
346+ 4. Conveying Verbatim Copies.
347+
348+ You may convey verbatim copies of the Program's source code as you
349+receive it, in any medium, provided that you conspicuously and
350+appropriately publish on each copy an appropriate copyright notice;
351+keep intact all notices stating that this License and any
352+non-permissive terms added in accord with section 7 apply to the code;
353+keep intact all notices of the absence of any warranty; and give all
354+recipients a copy of this License along with the Program.
355+
356+ You may charge any price or no price for each copy that you convey,
357+and you may offer support or warranty protection for a fee.
358+
359+ 5. Conveying Modified Source Versions.
360+
361+ You may convey a work based on the Program, or the modifications to
362+produce it from the Program, in the form of source code under the
363+terms of section 4, provided that you also meet all of these conditions:
364+
365+ a) The work must carry prominent notices stating that you modified
366+ it, and giving a relevant date.
367+
368+ b) The work must carry prominent notices stating that it is
369+ released under this License and any conditions added under section
370+ 7. This requirement modifies the requirement in section 4 to
371+ "keep intact all notices".
372+
373+ c) You must license the entire work, as a whole, under this
374+ License to anyone who comes into possession of a copy. This
375+ License will therefore apply, along with any applicable section 7
376+ additional terms, to the whole of the work, and all its parts,
377+ regardless of how they are packaged. This License gives no
378+ permission to license the work in any other way, but it does not
379+ invalidate such permission if you have separately received it.
380+
381+ d) If the work has interactive user interfaces, each must display
382+ Appropriate Legal Notices; however, if the Program has interactive
383+ interfaces that do not display Appropriate Legal Notices, your
384+ work need not make them do so.
385+
386+ A compilation of a covered work with other separate and independent
387+works, which are not by their nature extensions of the covered work,
388+and which are not combined with it such as to form a larger program,
389+in or on a volume of a storage or distribution medium, is called an
390+"aggregate" if the compilation and its resulting copyright are not
391+used to limit the access or legal rights of the compilation's users
392+beyond what the individual works permit. Inclusion of a covered work
393+in an aggregate does not cause this License to apply to the other
394+parts of the aggregate.
395+
396+ 6. Conveying Non-Source Forms.
397+
398+ You may convey a covered work in object code form under the terms
399+of sections 4 and 5, provided that you also convey the
400+machine-readable Corresponding Source under the terms of this License,
401+in one of these ways:
402+
403+ a) Convey the object code in, or embodied in, a physical product
404+ (including a physical distribution medium), accompanied by the
405+ Corresponding Source fixed on a durable physical medium
406+ customarily used for software interchange.
407+
408+ b) Convey the object code in, or embodied in, a physical product
409+ (including a physical distribution medium), accompanied by a
410+ written offer, valid for at least three years and valid for as
411+ long as you offer spare parts or customer support for that product
412+ model, to give anyone who possesses the object code either (1) a
413+ copy of the Corresponding Source for all the software in the
414+ product that is covered by this License, on a durable physical
415+ medium customarily used for software interchange, for a price no
416+ more than your reasonable cost of physically performing this
417+ conveying of source, or (2) access to copy the
418+ Corresponding Source from a network server at no charge.
419+
420+ c) Convey individual copies of the object code with a copy of the
421+ written offer to provide the Corresponding Source. This
422+ alternative is allowed only occasionally and noncommercially, and
423+ only if you received the object code with such an offer, in accord
424+ with subsection 6b.
425+
426+ d) Convey the object code by offering access from a designated
427+ place (gratis or for a charge), and offer equivalent access to the
428+ Corresponding Source in the same way through the same place at no
429+ further charge. You need not require recipients to copy the
430+ Corresponding Source along with the object code. If the place to
431+ copy the object code is a network server, the Corresponding Source
432+ may be on a different server (operated by you or a third party)
433+ that supports equivalent copying facilities, provided you maintain
434+ clear directions next to the object code saying where to find the
435+ Corresponding Source. Regardless of what server hosts the
436+ Corresponding Source, you remain obligated to ensure that it is
437+ available for as long as needed to satisfy these requirements.
438+
439+ e) Convey the object code using peer-to-peer transmission, provided
440+ you inform other peers where the object code and Corresponding
441+ Source of the work are being offered to the general public at no
442+ charge under subsection 6d.
443+
444+ A separable portion of the object code, whose source code is excluded
445+from the Corresponding Source as a System Library, need not be
446+included in conveying the object code work.
447+
448+ A "User Product" is either (1) a "consumer product", which means any
449+tangible personal property which is normally used for personal, family,
450+or household purposes, or (2) anything designed or sold for incorporation
451+into a dwelling. In determining whether a product is a consumer product,
452+doubtful cases shall be resolved in favor of coverage. For a particular
453+product received by a particular user, "normally used" refers to a
454+typical or common use of that class of product, regardless of the status
455+of the particular user or of the way in which the particular user
456+actually uses, or expects or is expected to use, the product. A product
457+is a consumer product regardless of whether the product has substantial
458+commercial, industrial or non-consumer uses, unless such uses represent
459+the only significant mode of use of the product.
460+
461+ "Installation Information" for a User Product means any methods,
462+procedures, authorization keys, or other information required to install
463+and execute modified versions of a covered work in that User Product from
464+a modified version of its Corresponding Source. The information must
465+suffice to ensure that the continued functioning of the modified object
466+code is in no case prevented or interfered with solely because
467+modification has been made.
468+
469+ If you convey an object code work under this section in, or with, or
470+specifically for use in, a User Product, and the conveying occurs as
471+part of a transaction in which the right of possession and use of the
472+User Product is transferred to the recipient in perpetuity or for a
473+fixed term (regardless of how the transaction is characterized), the
474+Corresponding Source conveyed under this section must be accompanied
475+by the Installation Information. But this requirement does not apply
476+if neither you nor any third party retains the ability to install
477+modified object code on the User Product (for example, the work has
478+been installed in ROM).
479+
480+ The requirement to provide Installation Information does not include a
481+requirement to continue to provide support service, warranty, or updates
482+for a work that has been modified or installed by the recipient, or for
483+the User Product in which it has been modified or installed. Access to a
484+network may be denied when the modification itself materially and
485+adversely affects the operation of the network or violates the rules and
486+protocols for communication across the network.
487+
488+ Corresponding Source conveyed, and Installation Information provided,
489+in accord with this section must be in a format that is publicly
490+documented (and with an implementation available to the public in
491+source code form), and must require no special password or key for
492+unpacking, reading or copying.
493+
494+ 7. Additional Terms.
495+
496+ "Additional permissions" are terms that supplement the terms of this
497+License by making exceptions from one or more of its conditions.
498+Additional permissions that are applicable to the entire Program shall
499+be treated as though they were included in this License, to the extent
500+that they are valid under applicable law. If additional permissions
501+apply only to part of the Program, that part may be used separately
502+under those permissions, but the entire Program remains governed by
503+this License without regard to the additional permissions.
504+
505+ When you convey a copy of a covered work, you may at your option
506+remove any additional permissions from that copy, or from any part of
507+it. (Additional permissions may be written to require their own
508+removal in certain cases when you modify the work.) You may place
509+additional permissions on material, added by you to a covered work,
510+for which you have or can give appropriate copyright permission.
511+
512+ Notwithstanding any other provision of this License, for material you
513+add to a covered work, you may (if authorized by the copyright holders of
514+that material) supplement the terms of this License with terms:
515+
516+ a) Disclaiming warranty or limiting liability differently from the
517+ terms of sections 15 and 16 of this License; or
518+
519+ b) Requiring preservation of specified reasonable legal notices or
520+ author attributions in that material or in the Appropriate Legal
521+ Notices displayed by works containing it; or
522+
523+ c) Prohibiting misrepresentation of the origin of that material, or
524+ requiring that modified versions of such material be marked in
525+ reasonable ways as different from the original version; or
526+
527+ d) Limiting the use for publicity purposes of names of licensors or
528+ authors of the material; or
529+
530+ e) Declining to grant rights under trademark law for use of some
531+ trade names, trademarks, or service marks; or
532+
533+ f) Requiring indemnification of licensors and authors of that
534+ material by anyone who conveys the material (or modified versions of
535+ it) with contractual assumptions of liability to the recipient, for
536+ any liability that these contractual assumptions directly impose on
537+ those licensors and authors.
538+
539+ All other non-permissive additional terms are considered "further
540+restrictions" within the meaning of section 10. If the Program as you
541+received it, or any part of it, contains a notice stating that it is
542+governed by this License along with a term that is a further
543+restriction, you may remove that term. If a license document contains
544+a further restriction but permits relicensing or conveying under this
545+License, you may add to a covered work material governed by the terms
546+of that license document, provided that the further restriction does
547+not survive such relicensing or conveying.
548+
549+ If you add terms to a covered work in accord with this section, you
550+must place, in the relevant source files, a statement of the
551+additional terms that apply to those files, or a notice indicating
552+where to find the applicable terms.
553+
554+ Additional terms, permissive or non-permissive, may be stated in the
555+form of a separately written license, or stated as exceptions;
556+the above requirements apply either way.
557+
558+ 8. Termination.
559+
560+ You may not propagate or modify a covered work except as expressly
561+provided under this License. Any attempt otherwise to propagate or
562+modify it is void, and will automatically terminate your rights under
563+this License (including any patent licenses granted under the third
564+paragraph of section 11).
565+
566+ However, if you cease all violation of this License, then your
567+license from a particular copyright holder is reinstated (a)
568+provisionally, unless and until the copyright holder explicitly and
569+finally terminates your license, and (b) permanently, if the copyright
570+holder fails to notify you of the violation by some reasonable means
571+prior to 60 days after the cessation.
572+
573+ Moreover, your license from a particular copyright holder is
574+reinstated permanently if the copyright holder notifies you of the
575+violation by some reasonable means, this is the first time you have
576+received notice of violation of this License (for any work) from that
577+copyright holder, and you cure the violation prior to 30 days after
578+your receipt of the notice.
579+
580+ Termination of your rights under this section does not terminate the
581+licenses of parties who have received copies or rights from you under
582+this License. If your rights have been terminated and not permanently
583+reinstated, you do not qualify to receive new licenses for the same
584+material under section 10.
585+
586+ 9. Acceptance Not Required for Having Copies.
587+
588+ You are not required to accept this License in order to receive or
589+run a copy of the Program. Ancillary propagation of a covered work
590+occurring solely as a consequence of using peer-to-peer transmission
591+to receive a copy likewise does not require acceptance. However,
592+nothing other than this License grants you permission to propagate or
593+modify any covered work. These actions infringe copyright if you do
594+not accept this License. Therefore, by modifying or propagating a
595+covered work, you indicate your acceptance of this License to do so.
596+
597+ 10. Automatic Licensing of Downstream Recipients.
598+
599+ Each time you convey a covered work, the recipient automatically
600+receives a license from the original licensors, to run, modify and
601+propagate that work, subject to this License. You are not responsible
602+for enforcing compliance by third parties with this License.
603+
604+ An "entity transaction" is a transaction transferring control of an
605+organization, or substantially all assets of one, or subdividing an
606+organization, or merging organizations. If propagation of a covered
607+work results from an entity transaction, each party to that
608+transaction who receives a copy of the work also receives whatever
609+licenses to the work the party's predecessor in interest had or could
610+give under the previous paragraph, plus a right to possession of the
611+Corresponding Source of the work from the predecessor in interest, if
612+the predecessor has it or can get it with reasonable efforts.
613+
614+ You may not impose any further restrictions on the exercise of the
615+rights granted or affirmed under this License. For example, you may
616+not impose a license fee, royalty, or other charge for exercise of
617+rights granted under this License, and you may not initiate litigation
618+(including a cross-claim or counterclaim in a lawsuit) alleging that
619+any patent claim is infringed by making, using, selling, offering for
620+sale, or importing the Program or any portion of it.
621+
622+ 11. Patents.
623+
624+ A "contributor" is a copyright holder who authorizes use under this
625+License of the Program or a work on which the Program is based. The
626+work thus licensed is called the contributor's "contributor version".
627+
628+ A contributor's "essential patent claims" are all patent claims
629+owned or controlled by the contributor, whether already acquired or
630+hereafter acquired, that would be infringed by some manner, permitted
631+by this License, of making, using, or selling its contributor version,
632+but do not include claims that would be infringed only as a
633+consequence of further modification of the contributor version. For
634+purposes of this definition, "control" includes the right to grant
635+patent sublicenses in a manner consistent with the requirements of
636+this License.
637+
638+ Each contributor grants you a non-exclusive, worldwide, royalty-free
639+patent license under the contributor's essential patent claims, to
640+make, use, sell, offer for sale, import and otherwise run, modify and
641+propagate the contents of its contributor version.
642+
643+ In the following three paragraphs, a "patent license" is any express
644+agreement or commitment, however denominated, not to enforce a patent
645+(such as an express permission to practice a patent or covenant not to
646+sue for patent infringement). To "grant" such a patent license to a
647+party means to make such an agreement or commitment not to enforce a
648+patent against the party.
649+
650+ If you convey a covered work, knowingly relying on a patent license,
651+and the Corresponding Source of the work is not available for anyone
652+to copy, free of charge and under the terms of this License, through a
653+publicly available network server or other readily accessible means,
654+then you must either (1) cause the Corresponding Source to be so
655+available, or (2) arrange to deprive yourself of the benefit of the
656+patent license for this particular work, or (3) arrange, in a manner
657+consistent with the requirements of this License, to extend the patent
658+license to downstream recipients. "Knowingly relying" means you have
659+actual knowledge that, but for the patent license, your conveying the
660+covered work in a country, or your recipient's use of the covered work
661+in a country, would infringe one or more identifiable patents in that
662+country that you have reason to believe are valid.
663+
664+ If, pursuant to or in connection with a single transaction or
665+arrangement, you convey, or propagate by procuring conveyance of, a
666+covered work, and grant a patent license to some of the parties
667+receiving the covered work authorizing them to use, propagate, modify
668+or convey a specific copy of the covered work, then the patent license
669+you grant is automatically extended to all recipients of the covered
670+work and works based on it.
671+
672+ A patent license is "discriminatory" if it does not include within
673+the scope of its coverage, prohibits the exercise of, or is
674+conditioned on the non-exercise of one or more of the rights that are
675+specifically granted under this License. You may not convey a covered
676+work if you are a party to an arrangement with a third party that is
677+in the business of distributing software, under which you make payment
678+to the third party based on the extent of your activity of conveying
679+the work, and under which the third party grants, to any of the
680+parties who would receive the covered work from you, a discriminatory
681+patent license (a) in connection with copies of the covered work
682+conveyed by you (or copies made from those copies), or (b) primarily
683+for and in connection with specific products or compilations that
684+contain the covered work, unless you entered into that arrangement,
685+or that patent license was granted, prior to 28 March 2007.
686+
687+ Nothing in this License shall be construed as excluding or limiting
688+any implied license or other defenses to infringement that may
689+otherwise be available to you under applicable patent law.
690+
691+ 12. No Surrender of Others' Freedom.
692+
693+ If conditions are imposed on you (whether by court order, agreement or
694+otherwise) that contradict the conditions of this License, they do not
695+excuse you from the conditions of this License. If you cannot convey a
696+covered work so as to satisfy simultaneously your obligations under this
697+License and any other pertinent obligations, then as a consequence you may
698+not convey it at all. For example, if you agree to terms that obligate you
699+to collect a royalty for further conveying from those to whom you convey
700+the Program, the only way you could satisfy both those terms and this
701+License would be to refrain entirely from conveying the Program.
702+
703+ 13. Use with the GNU Affero General Public License.
704+
705+ Notwithstanding any other provision of this License, you have
706+permission to link or combine any covered work with a work licensed
707+under version 3 of the GNU Affero General Public License into a single
708+combined work, and to convey the resulting work. The terms of this
709+License will continue to apply to the part which is the covered work,
710+but the special requirements of the GNU Affero General Public License,
711+section 13, concerning interaction through a network will apply to the
712+combination as such.
713+
714+ 14. Revised Versions of this License.
715+
716+ The Free Software Foundation may publish revised and/or new versions of
717+the GNU General Public License from time to time. Such new versions will
718+be similar in spirit to the present version, but may differ in detail to
719+address new problems or concerns.
720+
721+ Each version is given a distinguishing version number. If the
722+Program specifies that a certain numbered version of the GNU General
723+Public License "or any later version" applies to it, you have the
724+option of following the terms and conditions either of that numbered
725+version or of any later version published by the Free Software
726+Foundation. If the Program does not specify a version number of the
727+GNU General Public License, you may choose any version ever published
728+by the Free Software Foundation.
729+
730+ If the Program specifies that a proxy can decide which future
731+versions of the GNU General Public License can be used, that proxy's
732+public statement of acceptance of a version permanently authorizes you
733+to choose that version for the Program.
734+
735+ Later license versions may give you additional or different
736+permissions. However, no additional obligations are imposed on any
737+author or copyright holder as a result of your choosing to follow a
738+later version.
739+
740+ 15. Disclaimer of Warranty.
741+
742+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
743+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
744+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
745+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
746+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
747+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
748+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
749+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
750+
751+ 16. Limitation of Liability.
752+
753+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
754+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
755+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
756+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
757+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
758+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
759+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
760+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
761+SUCH DAMAGES.
762+
763+ 17. Interpretation of Sections 15 and 16.
764+
765+ If the disclaimer of warranty and limitation of liability provided
766+above cannot be given local legal effect according to their terms,
767+reviewing courts shall apply local law that most closely approximates
768+an absolute waiver of all civil liability in connection with the
769+Program, unless a warranty or assumption of liability accompanies a
770+copy of the Program in return for a fee.
771+
772+ END OF TERMS AND CONDITIONS
773+
774+ How to Apply These Terms to Your New Programs
775+
776+ If you develop a new program, and you want it to be of the greatest
777+possible use to the public, the best way to achieve this is to make it
778+free software which everyone can redistribute and change under these terms.
779+
780+ To do so, attach the following notices to the program. It is safest
781+to attach them to the start of each source file to most effectively
782+state the exclusion of warranty; and each file should have at least
783+the "copyright" line and a pointer to where the full notice is found.
784+
785+ <one line to give the program's name and a brief idea of what it does.>
786+ Copyright (C) <year> <name of author>
787+
788+ This program is free software: you can redistribute it and/or modify
789+ it under the terms of the GNU General Public License as published by
790+ the Free Software Foundation, either version 3 of the License, or
791+ (at your option) any later version.
792+
793+ This program is distributed in the hope that it will be useful,
794+ but WITHOUT ANY WARRANTY; without even the implied warranty of
795+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
796+ GNU General Public License for more details.
797+
798+ You should have received a copy of the GNU General Public License
799+ along with this program. If not, see <http://www.gnu.org/licenses/>.
800+
801+Also add information on how to contact you by electronic and paper mail.
802+
803+ If the program does terminal interaction, make it output a short
804+notice like this when it starts in an interactive mode:
805+
806+ <program> Copyright (C) <year> <name of author>
807+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
808+ This is free software, and you are welcome to redistribute it
809+ under certain conditions; type `show c' for details.
810+
811+The hypothetical commands `show w' and `show c' should show the appropriate
812+parts of the General Public License. Of course, your program's commands
813+might be different; for a GUI interface, you would use an "about box".
814+
815+ You should also get your employer (if you work as a programmer) or school,
816+if any, to sign a "copyright disclaimer" for the program, if necessary.
817+For more information on this, and how to apply and follow the GNU GPL, see
818+<http://www.gnu.org/licenses/>.
819+
820+ The GNU General Public License does not permit incorporating your program
821+into proprietary programs. If your program is a subroutine library, you
822+may consider it more useful to permit linking proprietary applications with
823+the library. If this is what you want to do, use the GNU Lesser General
824+Public License instead of this License. But first, please read
825+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
826
827=== renamed file 'COPYING' => 'COPYING.moved'
828=== added file 'README'
829--- README 1970-01-01 00:00:00 +0000
830+++ README 2015-08-05 00:17:22 +0000
831@@ -0,0 +1,11 @@
832+Weather app for Ubuntu devices
833+
834+To contribute:
835+ https://wiki.ubuntu.com/Touch/CoreApps/Weather
836+ https://wiki.ubuntu.com/Touch/CoreApps/DevelopmentGuide
837+
838+The following essential packages are required to develop this app:
839+- ubuntu-sdk (see http://developer.ubuntu.com/start)
840+- intltool - run the `sudo apt-get install intltool` command for installation
841+
842+See the debian/control file for an up-to-date list of dependencies
843
844=== added file 'README.autopilot'
845--- README.autopilot 1970-01-01 00:00:00 +0000
846+++ README.autopilot 2015-08-05 00:17:22 +0000
847@@ -0,0 +1,57 @@
848+# Running Autopilot tests
849+
850+The Weather app follows a test driven development where autopilot tests are run before every merge into trunk. If you are submitting your bugfix/patch to the Weather app, please follow the following steps below to ensure that all tests pass before proposing a merge request.
851+
852+If you are looking for more info about Autopilot or writing AP tests for the weather app, here are some useful links to help you:
853+
854+- http://developer.ubuntu.com/start/quality
855+- https://developer.ubuntu.com/api/autopilot/python/1.5.0/
856+
857+For help and options on running tests, see:
858+
859+- https://developer.ubuntu.com/en/start/platform/guides/running-autopilot-tests/
860+
861+## Prerequisites
862+
863+Install the following autopilot packages required to run the tests,
864+$ sudo apt-get install python3-autopilot libautopilot-qt ubuntu-ui-toolkit-autopilot python3-autopilot-vis
865+
866+## Running tests on the desktop
867+
868+Using terminal:
869+
870+* Branch the Weather app code, for example,
871+ $ bzr branch lp:ubuntu-weather-app
872+
873+* Navigate to the tests/autopilot directory.
874+ $ cd ubuntu-weather-app/tests/autopilot
875+
876+* run all tests.
877+ $ autopilot3 run -vv ubuntu_weather_app
878+
879+ to list all tests:
880+ $ autopilot3 list ubuntu_weather_app
881+
882+ To run only one test (for instance: ubuntu_weather_app.tests.test_empty_state.TestEmptyState.test_add_location_button)
883+ $ autopilot3 run -vv ubuntu_weather_app.tests.test_empty_state.TestEmptyState.test_add_location_button
884+
885+ Debugging tests using autopilot vis
886+ $ autopilot3 launch -i Qt qmlscene app/ubuntu-weather-app.qml
887+ $ autopilot3 vis
888+
889+
890+## Running tests on device or emulator:
891+
892+Using autopkg:
893+
894+* Branch the Weather app code, for example,
895+ $ bzr branch lp:ubuntu-weather-app
896+
897+* Navigate to the source directory.
898+ $ cd ubuntu-weather-app
899+
900+* Build a click package
901+ $ click-buddy .
902+
903+* Run the tests on device (assumes only one click package in the directory)
904+ $ adt-run . *.click --- ssh -s adb -- -p <PASSWORD>
905
906=== renamed file 'README' => 'README.moved'
907=== added file 'README.translations'
908--- README.translations 1970-01-01 00:00:00 +0000
909+++ README.translations 2015-08-05 00:17:22 +0000
910@@ -0,0 +1,37 @@
911+# Updating translations
912+
913+Translations for the Weather app happen in [Launchpad Translations][] and
914+are automatically committed daily on the trunk branch in the po/ folder.
915+
916+They are then built and installed as part of the package build, so that
917+developers don't really need to worry about them.
918+
919+However, there is one task that needs to be taken care of: exposing new
920+translatable messages to translators. So whenever you add new translatable
921+messages in the code, make sure to follow these steps:
922+
923+ 1. Run click-buddy to build the project and generate a new .pot file:
924+ `click-buddy --dir .`
925+ 2. Commit the generated .pot file:
926+ `bzr commit -m"Updated translation template"`
927+ 3. Push the branch and send a merge proposal as usual
928+
929+And that's it, once the branch lands Launchpad should take care of all the rest!
930+
931+# Behind the scenes
932+
933+Behind the scenes, whenever the po/*.pot file (also known as translations template)
934+is committed to trunk Launchpad reads it and updates the translatable strings
935+exposed in the web UI. This will enable translators to work on the new strings.
936+The translations template contains all translatable strings that have been
937+extracted from the source code files.
938+
939+Launchpad will then store translations in its database and will commit them daily
940+in the form of textual po/*.po files to trunk. The PO files are also usually
941+referred to as the translations files. You'll find a translation file for each
942+language the app has got at least a translated message available for.
943+
944+Translations for core apps follow the standard [gettext format].
945+
946+ [Launchpad Translations]: https://translations.launchpad.net/ubuntu-weather-app
947+ [gettext format]: https://www.gnu.org/software/gettext/
948
949=== renamed file 'README.translations' => 'README.translations.moved'
950=== added directory 'app'
951=== added file 'app/CMakeLists.txt'
952--- app/CMakeLists.txt 1970-01-01 00:00:00 +0000
953+++ app/CMakeLists.txt 2015-08-05 00:17:22 +0000
954@@ -0,0 +1,16 @@
955+if(NOT "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
956+ file(GLOB QML_JS_FILES *.qml *.js)
957+ add_custom_target(ubuntu-weather-app_QMlFiles ALL SOURCES ${QML_JS_FILES})
958+endif(NOT "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
959+
960+
961+set(ICON ${ICON_FILE})
962+install(FILES ${ICON} DESTINATION ${CMAKE_INSTALL_PREFIX})
963+
964+install(FILES ${MAIN_QML} DESTINATION ${UBUNTU-WEATHER_APP_DIR})
965+
966+add_subdirectory(components)
967+add_subdirectory(data)
968+add_subdirectory(graphics)
969+add_subdirectory(ui)
970+
971
972=== added directory 'app/components'
973=== added file 'app/components/CMakeLists.txt'
974--- app/components/CMakeLists.txt 1970-01-01 00:00:00 +0000
975+++ app/components/CMakeLists.txt 2015-08-05 00:17:22 +0000
976@@ -0,0 +1,5 @@
977+file(GLOB COMPONENTS_QML_JS_FILES *.qml *.js)
978+
979+add_custom_target(ubuntu-weather-app_components_QMlFiles ALL SOURCES ${COMPONENTS_QML_JS_FILES})
980+
981+install(FILES ${COMPONENTS_QML_JS_FILES} DESTINATION ${UBUNTU-WEATHER_APP_DIR}/components)
982
983=== added file 'app/components/CurrentLocation.qml'
984--- app/components/CurrentLocation.qml 1970-01-01 00:00:00 +0000
985+++ app/components/CurrentLocation.qml 2015-08-05 00:17:22 +0000
986@@ -0,0 +1,97 @@
987+/*
988+ * Copyright (C) 2015 Canonical Ltd
989+ *
990+ * This file is part of Ubuntu Weather App
991+ *
992+ * Ubuntu Weather App is free software: you can redistribute it and/or modify
993+ * it under the terms of the GNU General Public License version 3 as
994+ * published by the Free Software Foundation.
995+ *
996+ * Ubuntu Weather App is distributed in the hope that it will be useful,
997+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
998+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
999+ * GNU General Public License for more details.
1000+ *
1001+ * You should have received a copy of the GNU General Public License
1002+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1003+ */
1004+
1005+import QtLocation 5.3
1006+import QtPositioning 5.2
1007+import QtQuick 2.4
1008+import Ubuntu.Components 1.2
1009+import "../data/WeatherApi.js" as WeatherApi
1010+
1011+
1012+Item {
1013+ id: currentLocation
1014+
1015+ property string string: "Undefined"
1016+
1017+ function searchForLocation(lat, lon) {
1018+ WeatherApi.sendRequest({
1019+ action: "searchByPoint",
1020+ params: {
1021+ coords: {
1022+ lat: lat,
1023+ lon: lon
1024+ }
1025+ }
1026+ }, searchResponseHandler)
1027+ }
1028+
1029+ function searchResponseHandler(msgObject) {
1030+ if (!msgObject.error && settings.detectCurrentLocation) {
1031+ console.log("Loc to add:", JSON.stringify(msgObject.result.locations[0]))
1032+ storage.updateCurrentLocation(msgObject.result.locations[0])
1033+ }
1034+ }
1035+
1036+
1037+ PositionSource {
1038+ id: currentPosition
1039+ updateInterval: 1000
1040+ active: settings.detectCurrentLocation
1041+
1042+ onPositionChanged: {
1043+ var coord = currentPosition.position.coordinate
1044+ if (coord.isValid) {
1045+ geocodeModel.query = coord
1046+ geocodeModel.update()
1047+ }
1048+ }
1049+ }
1050+
1051+ Plugin {
1052+ id: osmPlugin
1053+ name: "osm"
1054+ }
1055+
1056+ GeocodeModel {
1057+ id: geocodeModel
1058+ autoUpdate: false
1059+ plugin: osmPlugin
1060+
1061+ onCountChanged: {
1062+ // Update the currentLocation if one is found and it does not match the stored location
1063+ if (count > 0 && currentLocation.string !== geocodeModel.get(0).address.city) {
1064+ search();
1065+ }
1066+ }
1067+
1068+ function search() {
1069+ var loc = geocodeModel.get(0)
1070+ currentLocation.string = loc.address.city
1071+ searchForLocation(loc.coordinate.latitude, loc.coordinate.longitude)
1072+ }
1073+ }
1074+
1075+ Connections {
1076+ target: settings
1077+ onDetectCurrentLocationChanged: {
1078+ if (settings.detectCurrentLocation) {
1079+ geocodeModel.search();
1080+ }
1081+ }
1082+ }
1083+}
1084
1085=== added file 'app/components/DayDelegate.qml'
1086--- app/components/DayDelegate.qml 1970-01-01 00:00:00 +0000
1087+++ app/components/DayDelegate.qml 2015-08-05 00:17:22 +0000
1088@@ -0,0 +1,248 @@
1089+/*
1090+ * Copyright (C) 2015 Canonical Ltd
1091+ *
1092+ * This file is part of Ubuntu Weather App
1093+ *
1094+ * Ubuntu Weather App is free software: you can redistribute it and/or modify
1095+ * it under the terms of the GNU General Public License version 3 as
1096+ * published by the Free Software Foundation.
1097+ *
1098+ * Ubuntu Weather App is distributed in the hope that it will be useful,
1099+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1100+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1101+ * GNU General Public License for more details.
1102+ *
1103+ * You should have received a copy of the GNU General Public License
1104+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1105+ */
1106+
1107+import QtQuick 2.4
1108+import Ubuntu.Components 1.2
1109+
1110+ListItem {
1111+ id: dayDelegate
1112+ height: collapsedHeight
1113+
1114+ property int collapsedHeight: units.gu(8)
1115+ property int expandedHeight: collapsedHeight + units.gu(4) + extraInfoColumn.height
1116+
1117+ property alias day: dayLabel.text
1118+ property alias image: weatherImage.name
1119+ property alias high: highLabel.text
1120+ property alias low: lowLabel.text
1121+
1122+ property alias condition: conditionForecast.text
1123+ property alias chanceOfRain: chanceOfRainForecast.value
1124+ property alias humidity: humidityForecast.value
1125+ property alias pollen: pollenForecast.value
1126+ property alias sunrise: sunriseForecast.value
1127+ property alias sunset: sunsetForecast.value
1128+ property alias wind: windForecast.value
1129+ property alias uvIndex: uvIndexForecast.value
1130+
1131+ state: "normal"
1132+ states: [
1133+ State {
1134+ name: "normal"
1135+ PropertyChanges {
1136+ target: dayDelegate
1137+ height: collapsedHeight
1138+ }
1139+ },
1140+ State {
1141+ name: "expanded"
1142+ PropertyChanges {
1143+ target: dayDelegate
1144+ height: expandedHeight
1145+ }
1146+ PropertyChanges {
1147+ target: expandedInfo
1148+ opacity: 1
1149+ }
1150+ }
1151+ ]
1152+
1153+ transitions: [
1154+ Transition {
1155+ from: "normal"
1156+ to: "expanded"
1157+ SequentialAnimation {
1158+ NumberAnimation {
1159+ easing.type: Easing.InOutQuad
1160+ properties: "height"
1161+ }
1162+ NumberAnimation {
1163+ easing.type: Easing.InOutQuad
1164+ properties: "opacity"
1165+ }
1166+ }
1167+ },
1168+ Transition {
1169+ from: "expanded"
1170+ to: "normal"
1171+ SequentialAnimation {
1172+ NumberAnimation {
1173+ easing.type: Easing.InOutQuad
1174+ properties: "opacity"
1175+ }
1176+ NumberAnimation {
1177+ easing.type: Easing.InOutQuad
1178+ properties: "height"
1179+ }
1180+ }
1181+ }
1182+ ]
1183+
1184+ onClicked: {
1185+ state = state === "normal" ? "expanded" : "normal"
1186+ locationPages.collapseOtherDelegates(index)
1187+ }
1188+
1189+ Item {
1190+ id: mainInfo
1191+
1192+ height: collapsedHeight
1193+ anchors {
1194+ left: parent.left
1195+ right: parent.right
1196+ margins: units.gu(2)
1197+ }
1198+
1199+ Label {
1200+ id: dayLabel
1201+ anchors {
1202+ left: parent.left
1203+ right: weatherImage.left
1204+ rightMargin: units.gu(1)
1205+ top: parent.top
1206+ topMargin: (collapsedHeight - dayLabel.height) / 2
1207+ }
1208+ elide: Text.ElideRight
1209+ font.weight: Font.Light
1210+ fontSize: "medium"
1211+ }
1212+
1213+ Icon {
1214+ id: weatherImage
1215+ anchors {
1216+ horizontalCenter: parent.horizontalCenter
1217+ verticalCenter: dayLabel.verticalCenter
1218+ }
1219+ height: units.gu(3)
1220+ width: units.gu(3)
1221+ }
1222+
1223+ Label {
1224+ id: lowLabel
1225+ anchors {
1226+ left: weatherImage.right
1227+ right: highLabel.left
1228+ rightMargin: units.gu(1)
1229+ verticalCenter: dayLabel.verticalCenter
1230+ }
1231+ elide: Text.ElideRight
1232+ font.pixelSize: units.gu(2)
1233+ font.weight: Font.Light
1234+ fontSize: "medium"
1235+ height: units.gu(2)
1236+ horizontalAlignment: Text.AlignRight
1237+ verticalAlignment: Text.AlignTop // AlignTop appears to align bottom?
1238+ }
1239+
1240+ Label {
1241+ id: highLabel
1242+ anchors {
1243+ bottom: lowLabel.bottom
1244+ right: parent.right
1245+ }
1246+ color: UbuntuColors.orange
1247+ elide: Text.ElideRight
1248+ font.pixelSize: units.gu(3)
1249+ font.weight: Font.Normal
1250+ height: units.gu(3)
1251+ verticalAlignment: Text.AlignTop // AlignTop appears to align bottom?
1252+ }
1253+ }
1254+
1255+ Item {
1256+ id: expandedInfo
1257+ anchors {
1258+ bottom: parent.bottom
1259+ left: parent.left
1260+ right: parent.right
1261+ top: mainInfo.bottom
1262+ bottomMargin: units.gu(2)
1263+ }
1264+ opacity: 0
1265+ visible: opacity !== 0
1266+
1267+ Column {
1268+ id: extraInfoColumn
1269+ anchors {
1270+ centerIn: parent
1271+ }
1272+ spacing: units.gu(2)
1273+
1274+ // FIXME: extended-infomation_* aren't actually on device
1275+
1276+ // Overview text
1277+ Label {
1278+ id: conditionForecast
1279+ color: UbuntuColors.coolGrey
1280+ fontSize: "x-large"
1281+ horizontalAlignment: Text.AlignHCenter
1282+ width: parent.width
1283+ }
1284+
1285+ ForecastDetailsDelegate {
1286+ id: chanceOfRainForecast
1287+ forecast: i18n.tr("Chance of rain")
1288+ imageSource: "../graphics/extended-information_chance-of-rain.svg"
1289+ }
1290+
1291+ ForecastDetailsDelegate {
1292+ id: windForecast
1293+ forecast: i18n.tr("Winds")
1294+ imageSource: "../graphics/extended-information_wind.svg"
1295+ }
1296+
1297+ ForecastDetailsDelegate {
1298+ id: uvIndexForecast
1299+ imageSource: "../graphics/extended-information_uv-level.svg"
1300+ forecast: i18n.tr("UV Index")
1301+ }
1302+
1303+ ForecastDetailsDelegate {
1304+ id: pollenForecast
1305+ forecast: i18n.tr("Pollen")
1306+ // FIXME: need icon
1307+ }
1308+
1309+ ForecastDetailsDelegate {
1310+ id: humidityForecast
1311+ forecast: i18n.tr("Humidity")
1312+ imageSource: "../graphics/extended-information_humidity.svg"
1313+ }
1314+
1315+ ForecastDetailsDelegate {
1316+ id: sunriseForecast
1317+ forecast: i18n.tr("Sunrise")
1318+ imageSource: "../graphics/extended-information_sunrise.svg"
1319+ }
1320+
1321+ ForecastDetailsDelegate {
1322+ id: sunsetForecast
1323+ forecast: i18n.tr("Sunset")
1324+ imageSource: "../graphics/extended-information_sunset.svg"
1325+ }
1326+ }
1327+ }
1328+
1329+ Component.onCompleted: {
1330+ locationPages.collapseOtherDelegates.connect(function(otherIndex) {
1331+ if (dayDelegate && typeof index !== "undefined" && otherIndex !== index) {
1332+ state = "normal"
1333+ }
1334+ });
1335+ }
1336+}
1337
1338=== added file 'app/components/EmptyStateComponent.qml'
1339--- app/components/EmptyStateComponent.qml 1970-01-01 00:00:00 +0000
1340+++ app/components/EmptyStateComponent.qml 2015-08-05 00:17:22 +0000
1341@@ -0,0 +1,67 @@
1342+/*
1343+ * Copyright (C) 2015 Canonical Ltd
1344+ *
1345+ * This file is part of Ubuntu Weather App
1346+ *
1347+ * Ubuntu Weather App is free software: you can redistribute it and/or modify
1348+ * it under the terms of the GNU General Public License version 3 as
1349+ * published by the Free Software Foundation.
1350+ *
1351+ * Ubuntu Weather App is distributed in the hope that it will be useful,
1352+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1353+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1354+ * GNU General Public License for more details.
1355+ *
1356+ * You should have received a copy of the GNU General Public License
1357+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1358+ */
1359+
1360+import QtQuick 2.4
1361+import Ubuntu.Components 1.2
1362+import "../components"
1363+
1364+
1365+Item {
1366+ anchors {
1367+ fill: parent
1368+ margins: units.gu(2)
1369+ }
1370+
1371+ SettingsButton {
1372+ anchors {
1373+ right: parent.right
1374+ top: parent.top
1375+ }
1376+ }
1377+
1378+ Column {
1379+ anchors {
1380+ centerIn: parent
1381+ }
1382+ spacing: units.gu(4)
1383+ width: parent.width - units.gu(4)
1384+
1385+ Label {
1386+ id: emptyStateLabel
1387+ anchors {
1388+ horizontalCenter: parent.horizontalCenter
1389+ }
1390+ horizontalAlignment: Text.AlignHCenter
1391+ text: settings.detectCurrentLoaction
1392+ ? i18n.tr("Searching for current location...")
1393+ : i18n.tr("Cannot determine your location")
1394+ width: parent.width
1395+ wrapMode: Text.WordWrap
1396+ }
1397+
1398+ Label {
1399+ anchors {
1400+ horizontalCenter: parent.horizontalCenter
1401+ }
1402+ horizontalAlignment: Text.AlignHCenter
1403+ text: i18n.tr("Manually add a location by swiping up from the bottom of the display")
1404+ width: parent.width
1405+ wrapMode: Text.WordWrap
1406+ }
1407+ }
1408+}
1409
1410=== added file 'app/components/ExpandableListItem.qml'
1411--- app/components/ExpandableListItem.qml 1970-01-01 00:00:00 +0000
1412+++ app/components/ExpandableListItem.qml 2015-08-05 00:17:22 +0000
1413@@ -0,0 +1,90 @@
1414+/*
1415+ * Copyright (C) 2015 Canonical Ltd
1416+ *
1417+ * This file is part of Ubuntu Weather App
1418+ *
1419+ * Ubuntu Weather App is free software: you can redistribute it and/or modify
1420+ * it under the terms of the GNU General Public License version 3 as
1421+ * published by the Free Software Foundation.
1422+ *
1423+ * Ubuntu Weather App is distributed in the hope that it will be useful,
1424+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1425+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1426+ * GNU General Public License for more details.
1427+ *
1428+ * You should have received a copy of the GNU General Public License
1429+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1430+ */
1431+
1432+import QtQuick 2.4
1433+import Ubuntu.Components 1.2
1434+import Ubuntu.Components.ListItems 1.0 as ListItem
1435+
1436+/*
1437+ Component which extends the SDK Expandable list item and provides a easy
1438+ to use component where the title, subtitle and listview can be displayed. It
1439+ matches the design specification provided for clock.
1440+*/
1441+
1442+ListItem.Expandable {
1443+ id: expandableListItem
1444+
1445+ property ListModel model
1446+ property Component delegate
1447+ property alias text: expandableHeader.text
1448+ property alias subText: expandableHeader.subText
1449+ property alias listViewHeight: expandableList.height
1450+
1451+ anchors {
1452+ left: parent.left
1453+ right: parent.right
1454+ margins: units.gu(-2)
1455+ }
1456+
1457+ collapseOnClick: true
1458+ expandedHeight: contentColumn.height + units.gu(1)
1459+
1460+ Column {
1461+ id: contentColumn
1462+
1463+ anchors {
1464+ left: parent.left
1465+ right: parent.right
1466+ }
1467+
1468+ Item {
1469+ width: parent.width
1470+ height: expandableListItem.collapsedHeight
1471+
1472+ ListItem.Subtitled {
1473+ id: expandableHeader
1474+ onClicked: expandableListItem.expanded = true
1475+
1476+ Icon {
1477+ id: arrow
1478+
1479+ width: units.gu(2)
1480+ height: width
1481+ anchors.right: parent.right
1482+ anchors.verticalCenter: parent.verticalCenter
1483+
1484+ name: "go-down"
1485+ color: "Grey"
1486+ rotation: expandableListItem.expanded ? 180 : 0
1487+
1488+ Behavior on rotation {
1489+ UbuntuNumberAnimation {}
1490+ }
1491+ }
1492+ }
1493+ }
1494+
1495+ ListView {
1496+ id: expandableList
1497+ width: parent.width
1498+ interactive: false
1499+ model: expandableListItem.model
1500+ delegate: expandableListItem.delegate
1501+ }
1502+ }
1503+}
1504
1505=== added file 'app/components/FakeHeader.qml'
1506--- app/components/FakeHeader.qml 1970-01-01 00:00:00 +0000
1507+++ app/components/FakeHeader.qml 2015-08-05 00:17:22 +0000
1508@@ -0,0 +1,39 @@
1509+/*
1510+ * Copyright (C) 2014 Canonical Ltd
1511+ *
1512+ * This file is part of Ubuntu Clock App
1513+ *
1514+ * Ubuntu Clock App is free software: you can redistribute it and/or modify
1515+ * it under the terms of the GNU General Public License version 3 as
1516+ * published by the Free Software Foundation.
1517+ *
1518+ * Ubuntu Clock App is distributed in the hope that it will be useful,
1519+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1520+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1521+ * GNU General Public License for more details.
1522+ *
1523+ * You should have received a copy of the GNU General Public License
1524+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1525+ */
1526+
1527+import QtQuick 2.4
1528+import Ubuntu.Components 1.2
1529+
1530+Column {
1531+ id: fakeHeader
1532+
1533+ height: units.gu(9)
1534+
1535+ Rectangle {
1536+ height: units.gu(7)
1537+ width: parent.width
1538+ color: Theme.palette.normal.background
1539+ }
1540+
1541+ Rectangle {
1542+ color: "#C9C9C9"
1543+ height: units.gu(2)
1544+ anchors.left: parent.left
1545+ anchors.right: parent.right
1546+ }
1547+}
1548
1549=== added file 'app/components/FastScroll.js'
1550--- app/components/FastScroll.js 1970-01-01 00:00:00 +0000
1551+++ app/components/FastScroll.js 2015-08-05 00:17:22 +0000
1552@@ -0,0 +1,130 @@
1553+/****************************************************************************
1554+**
1555+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
1556+** Copyright (C) 2014 Canonical Ltda
1557+** All rights reserved.
1558+** Contact: Nokia Corporation (qt-info@nokia.com)
1559+**
1560+** This file is part of the Qt Components project.
1561+**
1562+** $QT_BEGIN_LICENSE:BSD$
1563+** You may use this file under the terms of the BSD license as follows:
1564+**
1565+** "Redistribution and use in source and binary forms, with or without
1566+** modification, are permitted provided that the following conditions are
1567+** met:
1568+** * Redistributions of source code must retain the above copyright
1569+** notice, this list of conditions and the following disclaimer.
1570+** * Redistributions in binary form must reproduce the above copyright
1571+** notice, this list of conditions and the following disclaimer in
1572+** the documentation and/or other materials provided with the
1573+** distribution.
1574+** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
1575+** the names of its contributors may be used to endorse or promote
1576+** products derived from this software without specific prior written
1577+** permission.
1578+**
1579+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1580+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1581+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1582+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1583+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1584+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
1585+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1586+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1587+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1588+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1589+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
1590+** $QT_END_LICENSE$
1591+**
1592+****************************************************************************/
1593+
1594+// FastScroll.js - this is just SectionScroller.js with a fix for
1595+// section.criteria == ViewSection.FirstCharacter
1596+var sectionData = [];
1597+var _sections = [];
1598+
1599+function initialize(list) {
1600+ initSectionData(list);
1601+}
1602+
1603+function contains(name) {
1604+ return (_sections.indexOf(name) > -1)
1605+}
1606+
1607+function initSectionData(list) {
1608+ if (!list || !list.model) return;
1609+ sectionData = [];
1610+ _sections = [];
1611+ var current = "",
1612+ prop = list.section.property,
1613+ sectionText;
1614+
1615+ if (list.section.criteria === ViewSection.FullString) {
1616+ for (var i = 0, count = list.model.count; i < count; i++) {
1617+ sectionText = list.getSectionText(i)
1618+ if (sectionText !== current) {
1619+ current = sectionText;
1620+ _sections.push(current);
1621+ sectionData.push({ index: i, header: current });
1622+ }
1623+ }
1624+ } else if (list.section.criteria === ViewSection.FirstCharacter) {
1625+ for (var i = 0, count = list.model.count; i < count; i++) {
1626+ sectionText = list.getSectionText(i).substring(0, 1)
1627+ if (sectionText !== current) {
1628+ current = sectionText
1629+ _sections.push(sectionText);
1630+ sectionData.push({ index: i, header: current });
1631+ }
1632+ }
1633+ }
1634+}
1635+
1636+function getSectionPositionString(name) {
1637+ var val = _sections.indexOf(name);
1638+ return val === 0 ? "first" :
1639+ val === _sections.length - 1 ? "last" : false;
1640+}
1641+
1642+function getAt(pos) {
1643+ return _sections[pos] ? _sections[pos] : "";
1644+}
1645+
1646+function getRelativeSections(current) {
1647+ var val = _sections.indexOf(current),
1648+ sect = [],
1649+ sl = _sections.length;
1650+
1651+ val = val < 1 ? 1 : val >= sl-1 ? sl-2 : val;
1652+ sect = [getAt(val - 1), getAt(val), getAt(val + 1)];
1653+
1654+ return sect;
1655+}
1656+
1657+function getClosestSection(pos, down) {
1658+ var tmp = (_sections.length) * pos;
1659+ var val = Math.ceil(tmp) // TODO: better algorithm
1660+ val = val < 2 ? 1 : val;
1661+ return _sections[val-1];
1662+}
1663+
1664+function getNextSection(current) {
1665+ var val = _sections.indexOf(current);
1666+ return (val > -1 ? _sections[(val < _sections.length - 1 ? val + 1 : val)] : _sections[0]) || "";
1667+}
1668+
1669+function getPreviousSection(current) {
1670+ var val = _sections.indexOf(current);
1671+ return (val > -1 ? _sections[(val > 0 ? val - 1 : val)] : _sections[0]) || "";
1672+}
1673+
1674+function getIndexFor(sectionName) {
1675+ var data = sectionData[_sections.indexOf(sectionName)]
1676+ if (data) {
1677+ var val = data.index;
1678+ return val === 0 || val > 0 ? val : -1;
1679+ } else {
1680+ return -1
1681+ }
1682+}
1683
1684=== added file 'app/components/FastScroll.qml'
1685--- app/components/FastScroll.qml 1970-01-01 00:00:00 +0000
1686+++ app/components/FastScroll.qml 2015-08-05 00:17:22 +0000
1687@@ -0,0 +1,321 @@
1688+/****************************************************************************
1689+**
1690+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
1691+** Copyright (C) 2014, 2015 Canonical Ltda
1692+** All rights reserved.
1693+** Contact: Nokia Corporation (qt-info@nokia.com)
1694+**
1695+** This file is part of the Qt Components project.
1696+**
1697+** $QT_BEGIN_LICENSE:BSD$
1698+** You may use this file under the terms of the BSD license as follows:
1699+**
1700+** "Redistribution and use in source and binary forms, with or without
1701+** modification, are permitted provided that the following conditions are
1702+** met:
1703+** * Redistributions of source code must retain the above copyright
1704+** notice, this list of conditions and the following disclaimer.
1705+** * Redistributions in binary form must reproduce the above copyright
1706+** notice, this list of conditions and the following disclaimer in
1707+** the documentation and/or other materials provided with the
1708+** distribution.
1709+** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
1710+** the names of its contributors may be used to endorse or promote
1711+** products derived from this software without specific prior written
1712+** permission.
1713+**
1714+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1715+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1716+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1717+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1718+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1719+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
1720+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1721+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1722+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1723+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1724+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
1725+** $QT_END_LICENSE$
1726+**
1727+****************************************************************************/
1728+
1729+// FastScroll.qml
1730+import QtQuick 2.4
1731+import Ubuntu.Components 1.2
1732+import "FastScroll.js" as Sections
1733+
1734+Item {
1735+ id: root
1736+
1737+ property ListView listView
1738+ property int pinSize: units.gu(2)
1739+
1740+ 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"]
1741+ readonly property alias fastScrolling: internal.fastScrolling
1742+ readonly property bool showing: (rail.opacity !== 0.0)
1743+ readonly property double minimumHeight: rail.height
1744+
1745+ width: units.gu(7)
1746+ height: rail.height
1747+
1748+ onListViewChanged: {
1749+ if (listView && listView.model) {
1750+ internal.initDirtyObserver();
1751+ } else if (listView) {
1752+ listView.modelChanged.connect(function() {
1753+ if (listView.model) {
1754+ internal.initDirtyObserver();
1755+ }
1756+ });
1757+ }
1758+ }
1759+
1760+ Connections {
1761+ target: listView
1762+ onCurrentIndexChanged: {
1763+ if (currentIndex != -1) {
1764+ rail.opacity = 0.0
1765+ }
1766+ }
1767+ }
1768+
1769+ Rectangle {
1770+ id: magnified
1771+
1772+ color: Theme.palette.normal.overlay
1773+ radius: height * 0.3
1774+ height: pinSize * 2
1775+ width: height
1776+ opacity: internal.fastScrolling && root.enabled ? 1.0 : 0.0
1777+ x: -cursor.width - units.gu(3)
1778+ y: {
1779+ if (internal.currentItem) {
1780+ var itemCenterY = rail.y + internal.currentItem.y + (internal.currentItem.height / 2)
1781+ return (itemCenterY - (magnified.height / 2))
1782+ } else {
1783+ return 0
1784+ }
1785+ }
1786+
1787+ Label {
1788+ anchors.fill: parent
1789+ horizontalAlignment: Text.AlignHCenter
1790+ verticalAlignment: Text.AlignVCenter
1791+ text: internal.desireSection
1792+ fontSize: "small"
1793+ }
1794+
1795+ Behavior on opacity {
1796+ UbuntuNumberAnimation {}
1797+ }
1798+ }
1799+
1800+ Rectangle {
1801+ id: cursor
1802+
1803+ property bool showLabel: false
1804+ property string currentSectionName: ""
1805+
1806+ radius: pinSize * 0.3
1807+ height: pinSize
1808+ width: height
1809+ color: Theme.palette.normal.foreground
1810+ opacity: rail.opacity
1811+ x: rail.x
1812+ y: {
1813+ if (internal.currentItem) {
1814+ var itemCenterY = rail.y + internal.currentItem.y + (internal.currentItem.height / 2)
1815+ return (itemCenterY - (cursor.height / 2))
1816+ } else {
1817+ return 0
1818+ }
1819+ }
1820+ Behavior on y {
1821+ enabled: !internal.fastScrolling
1822+ UbuntuNumberAnimation { }
1823+ }
1824+ }
1825+
1826+ Column {
1827+ id: rail
1828+
1829+ property bool isVisible: root.enabled &&
1830+ (listView.flicking || dragArea.pressed) &&
1831+ (listView.currentIndex == -1)
1832+ anchors {
1833+ right: parent.right
1834+ rightMargin: units.gu(2)
1835+ left: parent.left
1836+ leftMargin: units.gu(2)
1837+ top: parent.top
1838+ }
1839+ height: childrenRect.height
1840+ opacity: 0.0
1841+ onIsVisibleChanged: {
1842+ if (isVisible) {
1843+ rail.opacity = 1.0
1844+ hideTimer.stop()
1845+ } else if (!root.enabled) {
1846+ rail.opacity = 0.0
1847+ } else {
1848+ hideTimer.restart()
1849+ }
1850+ }
1851+
1852+ Behavior on opacity {
1853+ UbuntuNumberAnimation { }
1854+ }
1855+
1856+ Repeater {
1857+ id: sectionsRepeater
1858+
1859+ model: root.letters
1860+ Label {
1861+ id: lbl
1862+
1863+ anchors.left: parent.left
1864+ height: pinSize
1865+ width: pinSize
1866+ verticalAlignment: Text.AlignVCenter
1867+ horizontalAlignment: Text.AlignHCenter
1868+ text: modelData
1869+ fontSize: "x-small"
1870+ color: cursor.y === y ? "white" : Theme.palette.selected.backgroundText
1871+ opacity: !internal.modelDirty && Sections.contains(text) ? 1.0 : 0.5
1872+ }
1873+ }
1874+
1875+ Timer {
1876+ id: hideTimer
1877+
1878+ running: false
1879+ interval: 2000
1880+ onTriggered: rail.opacity = 0.0
1881+ }
1882+ }
1883+
1884+ MouseArea {
1885+ id: dragArea
1886+
1887+ anchors {
1888+ left: parent.left
1889+ right: parent.right
1890+ }
1891+ y: rail.y
1892+ height: rail.height
1893+ visible: rail.opacity == 1.0
1894+
1895+ preventStealing: true
1896+ onPressed: {
1897+ internal.adjustContentPosition(mouseY)
1898+ dragginTimer.start()
1899+ }
1900+
1901+ onReleased: {
1902+ dragginTimer.stop()
1903+ internal.desireSection = ""
1904+ internal.fastScrolling = false
1905+ }
1906+
1907+ onPositionChanged: internal.adjustContentPosition(mouseY)
1908+
1909+ Timer {
1910+ id: dragginTimer
1911+
1912+ running: false
1913+ interval: 150
1914+ onTriggered: {
1915+ internal.fastScrolling = true
1916+ }
1917+ }
1918+ }
1919+
1920+ Timer {
1921+ id: dirtyTimer
1922+ interval: 500
1923+ running: false
1924+ onTriggered: {
1925+ Sections.initSectionData(listView);
1926+ internal.modelDirty = false;
1927+ }
1928+ }
1929+
1930+ Timer {
1931+ id: timerScroll
1932+
1933+ running: false
1934+ interval: 10
1935+ onTriggered: {
1936+ if (internal.desireSection != internal.currentSection) {
1937+ var idx = Sections.getIndexFor(internal.desireSection)
1938+ if (idx !== -1) {
1939+ listView.cancelFlick()
1940+ listView.positionViewAtIndex(idx, ListView.Beginning)
1941+ }
1942+ }
1943+ }
1944+ }
1945+
1946+ QtObject {
1947+ id: internal
1948+
1949+ property string currentSection: listView.currentSection
1950+ property string desireSection: ""
1951+ property string targetSection: fastScrolling ? desireSection : currentSection
1952+ property int oldY: 0
1953+ property bool modelDirty: false
1954+ property bool down: true
1955+ property bool fastScrolling: false
1956+ property var currentItem: null
1957+
1958+ onTargetSectionChanged: moveIndicator(targetSection)
1959+
1960+ function initDirtyObserver() {
1961+ Sections.initialize(listView);
1962+ function dirtyObserver() {
1963+ if (!internal.modelDirty) {
1964+ internal.modelDirty = true;
1965+ dirtyTimer.running = true;
1966+ }
1967+ }
1968+
1969+ if (listView.model.countChanged)
1970+ listView.model.countChanged.connect(dirtyObserver);
1971+
1972+ if (listView.model.itemsChanged)
1973+ listView.model.itemsChanged.connect(dirtyObserver);
1974+
1975+ if (listView.model.itemsInserted)
1976+ listView.model.itemsInserted.connect(dirtyObserver);
1977+
1978+ if (listView.model.itemsMoved)
1979+ listView.model.itemsMoved.connect(dirtyObserver);
1980+
1981+ if (listView.model.itemsRemoved)
1982+ listView.model.itemsRemoved.connect(dirtyObserver);
1983+ }
1984+
1985+ function adjustContentPosition(mouseY) {
1986+ var child = rail.childAt(rail.width / 2, mouseY)
1987+ if (!child || child.text === "") {
1988+ return
1989+ }
1990+ var section = child.text
1991+ if (internal.desireSection !== section) {
1992+ internal.desireSection = section
1993+ moveIndicator(section)
1994+ if (dragArea.pressed) {
1995+ timerScroll.restart()
1996+ }
1997+ }
1998+ }
1999+
2000+ function moveIndicator(section)
2001+ {
2002+ var index = root.letters.indexOf(section)
2003+ if (index != -1) {
2004+ currentItem = sectionsRepeater.itemAt(index)
2005+ }
2006+ }
2007+ }
2008+}
2009
2010=== added file 'app/components/ForecastDetailsDelegate.qml'
2011--- app/components/ForecastDetailsDelegate.qml 1970-01-01 00:00:00 +0000
2012+++ app/components/ForecastDetailsDelegate.qml 2015-08-05 00:17:22 +0000
2013@@ -0,0 +1,49 @@
2014+/*
2015+ * Copyright (C) 2015 Canonical Ltd
2016+ *
2017+ * This file is part of Ubuntu Weather App
2018+ *
2019+ * Ubuntu Weather App is free software: you can redistribute it and/or modify
2020+ * it under the terms of the GNU General Public License version 3 as
2021+ * published by the Free Software Foundation.
2022+ *
2023+ * Ubuntu Weather App is distributed in the hope that it will be useful,
2024+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2025+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2026+ * GNU General Public License for more details.
2027+ *
2028+ * You should have received a copy of the GNU General Public License
2029+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2030+ */
2031+
2032+import QtQuick 2.4
2033+import Ubuntu.Components 1.2
2034+
2035+Row {
2036+ height: icon.height
2037+ spacing: units.gu(2)
2038+ visible: value !== ""
2039+
2040+ property alias imageName: icon.name
2041+ property alias imageSource: icon.source
2042+ property alias forecast: forecastLabel.text
2043+ property alias value: forecastValue.text
2044+
2045+ Icon {
2046+ id: icon
2047+ color: "#000"
2048+ height: units.gu(2)
2049+ width: height
2050+ }
2051+
2052+ Label {
2053+ id: forecastLabel
2054+ elide: Text.ElideRight
2055+ width: units.gu(14)
2056+ }
2057+
2058+ Label {
2059+ id: forecastValue
2060+ width: units.gu(10)
2061+ }
2062+}
2063
2064=== added file 'app/components/HeaderRow.qml'
2065--- app/components/HeaderRow.qml 1970-01-01 00:00:00 +0000
2066+++ app/components/HeaderRow.qml 2015-08-05 00:17:22 +0000
2067@@ -0,0 +1,44 @@
2068+/*
2069+ * Copyright (C) 2015 Canonical Ltd
2070+ *
2071+ * This file is part of Ubuntu Weather App
2072+ *
2073+ * Ubuntu Weather App is free software: you can redistribute it and/or modify
2074+ * it under the terms of the GNU General Public License version 3 as
2075+ * published by the Free Software Foundation.
2076+ *
2077+ * Ubuntu Weather App is distributed in the hope that it will be useful,
2078+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2079+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2080+ * GNU General Public License for more details.
2081+ *
2082+ * You should have received a copy of the GNU General Public License
2083+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2084+ */
2085+
2086+import QtQuick 2.4
2087+import QtQuick.Layouts 1.1
2088+import Ubuntu.Components 1.2
2089+
2090+RowLayout {
2091+ id: headerRow
2092+
2093+ property alias locationName: locationNameLabel.text
2094+
2095+ width: parent.width
2096+
2097+ Label {
2098+ id: locationNameLabel
2099+ color: UbuntuColors.darkGrey
2100+ elide: Text.ElideRight
2101+ font.weight: Font.Normal
2102+ fontSize: "large"
2103+ height: settingsButton.height
2104+ Layout.fillWidth: true
2105+ verticalAlignment: Text.AlignVCenter
2106+ }
2107+
2108+ SettingsButton {
2109+ id: settingsButton
2110+ }
2111+}
2112
2113=== added file 'app/components/HomeGraphic.qml'
2114--- app/components/HomeGraphic.qml 1970-01-01 00:00:00 +0000
2115+++ app/components/HomeGraphic.qml 2015-08-05 00:17:22 +0000
2116@@ -0,0 +1,39 @@
2117+/*
2118+ * Copyright (C) 2015 Canonical Ltd
2119+ *
2120+ * This file is part of Ubuntu Weather App
2121+ *
2122+ * Ubuntu Weather App is free software: you can redistribute it and/or modify
2123+ * it under the terms of the GNU General Public License version 3 as
2124+ * published by the Free Software Foundation.
2125+ *
2126+ * Ubuntu Weather App is distributed in the hope that it will be useful,
2127+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2128+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2129+ * GNU General Public License for more details.
2130+ *
2131+ * You should have received a copy of the GNU General Public License
2132+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2133+ */
2134+
2135+import QtQuick 2.4
2136+import Ubuntu.Components 1.2
2137+
2138+Item {
2139+ height: units.gu(32)
2140+ width: parent.width
2141+
2142+ property alias icon: iconImage.source
2143+
2144+ // TODO: will be on 'rails' (horizontal listview?) to reveal hourly forecast
2145+ Image {
2146+ id: iconImage
2147+ anchors {
2148+ centerIn: parent
2149+ }
2150+ fillMode: Image.PreserveAspectFit
2151+ height: parent.height
2152+ width: parent.width
2153+ }
2154+}
2155+
2156
2157=== added file 'app/components/HomeHourly.qml'
2158--- app/components/HomeHourly.qml 1970-01-01 00:00:00 +0000
2159+++ app/components/HomeHourly.qml 2015-08-05 00:17:22 +0000
2160@@ -0,0 +1,101 @@
2161+/*
2162+ * Copyright (C) 2015 Canonical Ltd
2163+ *
2164+ * This file is part of Ubuntu Weather App
2165+ *
2166+ * Ubuntu Weather App is free software: you can redistribute it and/or modify
2167+ * it under the terms of the GNU General Public License version 3 as
2168+ * published by the Free Software Foundation.
2169+ *
2170+ * Ubuntu Weather App is distributed in the hope that it will be useful,
2171+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2172+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2173+ * GNU General Public License for more details.
2174+ *
2175+ * You should have received a copy of the GNU General Public License
2176+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2177+ */
2178+
2179+import QtQuick 2.4
2180+import Ubuntu.Components 1.2
2181+
2182+ListView {
2183+ id: homeHourly
2184+
2185+ clip:true
2186+ height: parent ? parent.height : undefined
2187+ width: parent ? parent.width : undefined
2188+ model: forecasts.length
2189+ orientation: ListView.Horizontal
2190+
2191+ property string currentDate: Qt.formatTime(new Date())
2192+
2193+ onVisibleChanged: {
2194+ if(visible) {
2195+ ListView.model = forecasts.length
2196+ }
2197+ }
2198+
2199+ MouseArea {
2200+ anchors.fill: parent
2201+ onClicked: {
2202+ homeGraphic.visible = true
2203+ }
2204+ }
2205+
2206+ delegate: Item {
2207+ id: delegate
2208+
2209+ property var hourData: forecasts[index]
2210+
2211+ height: parent.height
2212+ width: childrenRect.width
2213+
2214+ Column {
2215+ id: hourColumn
2216+
2217+ anchors.verticalCenter: parent.verticalCenter
2218+ height: childrenRect.height
2219+ width: units.gu(10)
2220+
2221+ Label {
2222+ anchors.horizontalCenter: parent.horizontalCenter
2223+ fontSize: "small"
2224+ font.weight: Font.Light
2225+ text: currentDate.search(Qt.locale().amText) !== -1 || currentDate.search(Qt.locale().pmText) !== -1 ? "%1 %2".arg(formatTimestamp(hourData.date, 'ddd')).arg(formatTime(hourData.date, 'hap')) : "%1 %2".arg(formatTimestamp(hourData.date, 'ddd')).arg(formatTime(hourData.date, 'h:mm'))
2226+ }
2227+
2228+ Item {
2229+ anchors.horizontalCenter: parent.horizontalCenter
2230+ height: units.gu(7)
2231+ width: units.gu(7)
2232+
2233+ Icon {
2234+ anchors {
2235+ fill: parent
2236+ margins: units.gu(0.5)
2237+ }
2238+ color: UbuntuColors.orange
2239+ name: (hourData.icon !== undefined && iconMap[hourData.icon] !== undefined) ? iconMap[hourData.icon] : ""
2240+ }
2241+ }
2242+
2243+ Label {
2244+ anchors.horizontalCenter: parent.horizontalCenter
2245+ font.pixelSize: units.gu(3)
2246+ font.weight: Font.Light
2247+ text: Math.round(hourData[tempUnits].temp).toString()+settings.tempScale
2248+ }
2249+ }
2250+
2251+ Rectangle {
2252+ anchors.verticalCenter: parent.verticalCenter
2253+ color: UbuntuColors.darkGrey
2254+ height: hourColumn.height
2255+ opacity: 0.2
2256+ visible: index > 0
2257+ width: units.gu(0.1)
2258+ }
2259+ }
2260+}
2261+
2262
2263=== added file 'app/components/HomeTempInfo.qml'
2264--- app/components/HomeTempInfo.qml 1970-01-01 00:00:00 +0000
2265+++ app/components/HomeTempInfo.qml 2015-08-05 00:17:22 +0000
2266@@ -0,0 +1,73 @@
2267+/*
2268+ * Copyright (C) 2015 Canonical Ltd
2269+ *
2270+ * This file is part of Ubuntu Weather App
2271+ *
2272+ * Ubuntu Weather App is free software: you can redistribute it and/or modify
2273+ * it under the terms of the GNU General Public License version 3 as
2274+ * published by the Free Software Foundation.
2275+ *
2276+ * Ubuntu Weather App is distributed in the hope that it will be useful,
2277+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2278+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2279+ * GNU General Public License for more details.
2280+ *
2281+ * You should have received a copy of the GNU General Public License
2282+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2283+ */
2284+
2285+import QtQuick 2.4
2286+import Ubuntu.Components 1.2
2287+
2288+
2289+Column {
2290+ anchors {
2291+ left: parent.left
2292+ right: parent.right
2293+ }
2294+ spacing: units.gu(1)
2295+
2296+ property alias description: descriptionLabel.text
2297+ property alias high: highLabel.text
2298+ property alias low: lowLabel.text
2299+ property alias now: nowLabel.text
2300+
2301+ Label {
2302+ font.weight: Font.Light
2303+ fontSize: "small"
2304+ text: i18n.tr("Today")
2305+ }
2306+
2307+ Label {
2308+ id: descriptionLabel
2309+ font.weight: Font.Normal
2310+ fontSize: "large"
2311+ }
2312+
2313+ Row {
2314+ spacing: units.gu(2)
2315+
2316+ Label {
2317+ id: nowLabel
2318+ color: UbuntuColors.orange
2319+ font.pixelSize: units.gu(8)
2320+ font.weight: Font.Light
2321+ height: units.gu(8)
2322+ verticalAlignment: Text.AlignBottom // AlignBottom seems to put it at the top?
2323+ }
2324+
2325+ Column {
2326+ Label {
2327+ id: lowLabel
2328+ font.weight: Font.Light
2329+ fontSize: "medium"
2330+ }
2331+
2332+ Label {
2333+ id: highLabel
2334+ font.weight: Font.Light
2335+ fontSize: "medium"
2336+ }
2337+ }
2338+ }
2339+}
2340
2341=== added file 'app/components/LoadingIndicator.qml'
2342--- app/components/LoadingIndicator.qml 1970-01-01 00:00:00 +0000
2343+++ app/components/LoadingIndicator.qml 2015-08-05 00:17:22 +0000
2344@@ -0,0 +1,87 @@
2345+/*
2346+ * Copyright (C) 2015 Canonical Ltd
2347+ *
2348+ * This file is part of Ubuntu Weather App
2349+ *
2350+ * Ubuntu Weather App is free software: you can redistribute it and/or modify
2351+ * it under the terms of the GNU General Public License version 3 as
2352+ * published by the Free Software Foundation.
2353+ *
2354+ * Ubuntu Weather App is distributed in the hope that it will be useful,
2355+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2356+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2357+ * GNU General Public License for more details.
2358+ *
2359+ * You should have received a copy of the GNU General Public License
2360+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2361+ */
2362+
2363+import QtQuick 2.4
2364+import Ubuntu.Components 1.2
2365+
2366+Rectangle {
2367+ id: indicator
2368+ objectName: "processingIndicator"
2369+ anchors {
2370+ left: parent.left
2371+ right: parent.right
2372+ bottom: parent.bottom
2373+ bottomMargin: Qt.inputMethod.keyboardRectangle.height
2374+ }
2375+ height: units.dp(3)
2376+ color: "white"
2377+ opacity: 0
2378+ visible: opacity > 0
2379+
2380+ readonly property bool processing: loading
2381+
2382+ Behavior on opacity {
2383+ UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration }
2384+ }
2385+
2386+ onProcessingChanged: {
2387+ if (processing) delay.start();
2388+ else if (!persist.running) indicator.opacity = 0;
2389+ }
2390+
2391+ Timer {
2392+ id: delay
2393+ interval: 200
2394+ onTriggered: if (indicator.processing) {
2395+ persist.restart();
2396+ indicator.opacity = 1;
2397+ }
2398+ }
2399+
2400+ Timer {
2401+ id: persist
2402+ interval: 2 * UbuntuAnimation.SleepyDuration - UbuntuAnimation.FastDuration
2403+ onTriggered: if (!indicator.processing) indicator.opacity = 0
2404+ }
2405+
2406+ Rectangle {
2407+ id: orange
2408+ anchors { top: parent.top; bottom: parent.bottom }
2409+ width: parent.width / 4
2410+ color: UbuntuColors.orange
2411+
2412+ SequentialAnimation {
2413+ running: indicator.visible
2414+ loops: Animation.Infinite
2415+ XAnimator {
2416+ from: -orange.width / 2
2417+ to: indicator.width - orange.width / 2
2418+ duration: UbuntuAnimation.SleepyDuration
2419+ easing.type: Easing.InOutSine
2420+ target: orange
2421+ }
2422+ XAnimator {
2423+ from: indicator.width - orange.width / 2
2424+ to: -orange.width / 2
2425+ duration: UbuntuAnimation.SleepyDuration
2426+ easing.type: Easing.InOutSine
2427+ target: orange
2428+ }
2429+ }
2430+ }
2431+}
2432
2433=== added file 'app/components/MultiSelectListView.qml'
2434--- app/components/MultiSelectListView.qml 1970-01-01 00:00:00 +0000
2435+++ app/components/MultiSelectListView.qml 2015-08-05 00:17:22 +0000
2436@@ -0,0 +1,71 @@
2437+/*
2438+ * Copyright (C) 2013, 2014, 2015
2439+ * Andrew Hayzen <ahayzen@gmail.com>
2440+ * Daniel Holm <d.holmen@gmail.com>
2441+ * Victor Thompson <victor.thompson@gmail.com>
2442+ *
2443+ * This program is free software; you can redistribute it and/or modify
2444+ * it under the terms of the GNU General Public License as published by
2445+ * the Free Software Foundation; version 3.
2446+ *
2447+ * This program is distributed in the hope that it will be useful,
2448+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2449+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2450+ * GNU General Public License for more details.
2451+ *
2452+ * You should have received a copy of the GNU General Public License
2453+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2454+ */
2455+
2456+import QtQuick 2.4
2457+import Ubuntu.Components 1.2
2458+
2459+WeatherListView {
2460+ signal clearSelection()
2461+ signal closeSelection()
2462+ signal selectAll()
2463+ signal reorder(int from, int to)
2464+
2465+ onClearSelection: {
2466+ ViewItems.selectedIndices = []
2467+ }
2468+
2469+ onCloseSelection: {
2470+ clearSelection()
2471+ ViewItems.selectMode = false
2472+ ViewItems.dragMode = false
2473+ }
2474+
2475+ onSelectAll: {
2476+ var tmp = []
2477+
2478+ for (var i=0; i < model.count; i++) {
2479+ if (tmp.indexOf(i) === -1) {
2480+ tmp.push(i)
2481+ }
2482+ }
2483+
2484+ ViewItems.selectedIndices = tmp
2485+ }
2486+
2487+ onVisibleChanged: {
2488+ if (!visible) {
2489+ closeSelection()
2490+ }
2491+ }
2492+
2493+ moveDisplaced: Transition {
2494+ UbuntuNumberAnimation {
2495+ property: "y"
2496+ }
2497+ }
2498+
2499+ ViewItems.onDragUpdated: {
2500+ if (event.status === ListItemDrag.Moving) {
2501+ event.accept = false
2502+ } else if (event.status === ListItemDrag.Dropped) {
2503+ model.move(event.from, event.to, 1)
2504+ reorder(event.from, event.to)
2505+ }
2506+ }
2507+}
2508
2509=== added file 'app/components/PageWithBottomEdge.qml'
2510--- app/components/PageWithBottomEdge.qml 1970-01-01 00:00:00 +0000
2511+++ app/components/PageWithBottomEdge.qml 2015-08-05 00:17:22 +0000
2512@@ -0,0 +1,461 @@
2513+/*
2514+ * Copyright (C) 2014, 2015 Canonical, Ltd.
2515+ *
2516+ * This program is free software; you can redistribute it and/or modify
2517+ * it under the terms of the GNU General Public License as published by
2518+ * the Free Software Foundation; version 3.
2519+ *
2520+ * This program is distributed in the hope that it will be useful,
2521+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2522+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2523+ * GNU General Public License for more details.
2524+ *
2525+ * You should have received a copy of the GNU General Public License
2526+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2527+ */
2528+
2529+/*
2530+ Example:
2531+
2532+ MainView {
2533+ objectName: "mainView"
2534+
2535+ applicationName: "com.ubuntu.developer.boiko.bottomedge"
2536+
2537+ width: units.gu(100)
2538+ height: units.gu(75)
2539+
2540+ Component {
2541+ id: pageComponent
2542+
2543+ PageWithBottomEdge {
2544+ id: mainPage
2545+ title: i18n.tr("Main Page")
2546+
2547+ Rectangle {
2548+ anchors.fill: parent
2549+ color: "white"
2550+ }
2551+
2552+ bottomEdgePageComponent: Page {
2553+ title: "Contents"
2554+ anchors.fill: parent
2555+ //anchors.topMargin: contentsPage.flickable.contentY
2556+
2557+ ListView {
2558+ anchors.fill: parent
2559+ model: 50
2560+ delegate: ListItems.Standard {
2561+ text: "One Content Item: " + index
2562+ }
2563+ }
2564+ }
2565+ bottomEdgeTitle: i18n.tr("Bottom edge action")
2566+ }
2567+ }
2568+
2569+ PageStack {
2570+ id: stack
2571+ Component.onCompleted: stack.push(pageComponent)
2572+ }
2573+ }
2574+
2575+*/
2576+
2577+import QtQuick 2.4
2578+import Ubuntu.Components 1.2
2579+
2580+Page {
2581+ id: page
2582+
2583+ property alias bottomEdgePageComponent: edgeLoader.sourceComponent
2584+ property alias bottomEdgePageSource: edgeLoader.source
2585+ property alias bottomEdgeTitle: tipLabel.text
2586+ property bool bottomEdgeEnabled: true
2587+ property int bottomEdgeExpandThreshold: page.height * 0.2
2588+ property int bottomEdgeExposedArea: bottomEdge.state !== "expanded" ? (page.height - bottomEdge.y - bottomEdge.tipHeight) : _areaWhenExpanded
2589+ property bool reloadBottomEdgePage: true
2590+
2591+ readonly property alias bottomEdgePage: edgeLoader.item
2592+ readonly property bool isReady: ((bottomEdge.y === fakeHeader.height) && bottomEdgePageLoaded && edgeLoader.item.active)
2593+ readonly property bool isCollapsed: (bottomEdge.y === page.height)
2594+ readonly property bool bottomEdgePageLoaded: (edgeLoader.status == Loader.Ready)
2595+
2596+ property bool _showEdgePageWhenReady: false
2597+ property int _areaWhenExpanded: 0
2598+
2599+ // CUSTOM properties to allow changing of color
2600+ property alias tipColor: tip.color
2601+ property alias tipLabelColor: tipLabel.color
2602+
2603+ signal bottomEdgeReleased()
2604+ signal bottomEdgeDismissed()
2605+
2606+ function showBottomEdgePage(source, properties)
2607+ {
2608+ edgeLoader.setSource(source, properties)
2609+ _showEdgePageWhenReady = true
2610+ }
2611+
2612+ function setBottomEdgePage(source, properties)
2613+ {
2614+ edgeLoader.setSource(source, properties)
2615+ }
2616+
2617+ function _pushPage()
2618+ {
2619+ if (edgeLoader.status === Loader.Ready) {
2620+ edgeLoader.item.active = true
2621+ page.pageStack.push(edgeLoader.item)
2622+ if (edgeLoader.item.flickable) {
2623+ edgeLoader.item.flickable.contentY = -page.header.height
2624+ edgeLoader.item.flickable.returnToBounds()
2625+ }
2626+ if (edgeLoader.item.ready)
2627+ edgeLoader.item.ready()
2628+ }
2629+ }
2630+
2631+
2632+ Component.onCompleted: {
2633+ // avoid a binding on the expanded height value
2634+ var expandedHeight = height;
2635+ _areaWhenExpanded = expandedHeight;
2636+ }
2637+
2638+ onActiveChanged: {
2639+ if (active) {
2640+ bottomEdge.state = "collapsed"
2641+ }
2642+ }
2643+
2644+ onBottomEdgePageLoadedChanged: {
2645+ if (_showEdgePageWhenReady && bottomEdgePageLoaded) {
2646+ bottomEdge.state = "expanded"
2647+ _showEdgePageWhenReady = false
2648+ }
2649+ }
2650+
2651+ Rectangle {
2652+ id: bgVisual
2653+
2654+ color: "black"
2655+ anchors.fill: page
2656+ opacity: 0.7 * ((page.height - bottomEdge.y) / page.height)
2657+ z: 1
2658+ }
2659+
2660+ UbuntuShape {
2661+ id: tip
2662+ objectName: "bottomEdgeTip"
2663+
2664+ property bool hidden: (activeFocus === false) || ((bottomEdge.y - units.gu(1)) < tip.y)
2665+
2666+ // CUSTOM
2667+ property bool isAnimating: true
2668+
2669+ enabled: mouseArea.enabled
2670+ visible: page.bottomEdgeEnabled
2671+ anchors {
2672+ bottom: parent.bottom
2673+ horizontalCenter: bottomEdge.horizontalCenter
2674+ bottomMargin: hidden ? - height + units.gu(1) : -units.gu(1)
2675+ Behavior on bottomMargin {
2676+ SequentialAnimation {
2677+ // wait some msecs in case of the focus change again, to avoid flickering
2678+ PauseAnimation {
2679+ duration: 300
2680+ }
2681+ UbuntuNumberAnimation {
2682+ duration: UbuntuAnimation.SnapDuration
2683+ }
2684+ // CUSTOM
2685+ ScriptAction {
2686+ script: tip.isAnimating = false
2687+ }
2688+ }
2689+ }
2690+ }
2691+
2692+ z: 1
2693+ width: tipLabel.paintedWidth + units.gu(6)
2694+ height: bottomEdge.tipHeight + units.gu(1)
2695+ color: Theme.palette.normal.overlay
2696+ Label {
2697+ id: tipLabel
2698+
2699+ anchors {
2700+ top: parent.top
2701+ left: parent.left
2702+ right: parent.right
2703+ }
2704+ height: bottomEdge.tipHeight
2705+ verticalAlignment: Text.AlignVCenter
2706+ horizontalAlignment: Text.AlignHCenter
2707+ opacity: tip.hidden ? 0.0 : 1.0
2708+ Behavior on opacity {
2709+ UbuntuNumberAnimation {
2710+ duration: UbuntuAnimation.SnapDuration
2711+ }
2712+ }
2713+ }
2714+ }
2715+
2716+ Rectangle {
2717+ id: shadow
2718+
2719+ anchors {
2720+ left: parent.left
2721+ right: parent.right
2722+ bottom: parent.bottom
2723+ }
2724+ height: units.gu(1)
2725+ z: 1
2726+ opacity: 0.0
2727+ gradient: Gradient {
2728+ GradientStop { position: 0.0; color: "transparent" }
2729+ GradientStop { position: 1.0; color: Qt.rgba(0, 0, 0, 0.2) }
2730+ }
2731+ }
2732+
2733+ MouseArea {
2734+ id: mouseArea
2735+
2736+ property real previousY: -1
2737+ property string dragDirection: "None"
2738+
2739+ preventStealing: true
2740+ drag {
2741+ axis: Drag.YAxis
2742+ target: bottomEdge
2743+ minimumY: bottomEdge.pageStartY
2744+ maximumY: page.height
2745+ }
2746+ enabled: edgeLoader.status == Loader.Ready
2747+ visible: page.bottomEdgeEnabled
2748+
2749+ anchors {
2750+ left: parent.left
2751+ right: parent.right
2752+ bottom: parent.bottom
2753+ }
2754+ height: bottomEdge.tipHeight
2755+ z: 1
2756+
2757+ onReleased: {
2758+ page.bottomEdgeReleased()
2759+ if ((dragDirection === "BottomToTop") &&
2760+ bottomEdge.y < (page.height - bottomEdgeExpandThreshold - bottomEdge.tipHeight)) {
2761+ bottomEdge.state = "expanded"
2762+ } else {
2763+ bottomEdge.state = "collapsed"
2764+ }
2765+ previousY = -1
2766+ dragDirection = "None"
2767+ }
2768+
2769+ onPressed: {
2770+ previousY = mouse.y
2771+ tip.forceActiveFocus()
2772+ }
2773+
2774+ onMouseYChanged: {
2775+ var yOffset = previousY - mouseY
2776+ // skip if was a small move
2777+ if (Math.abs(yOffset) <= units.gu(2)) {
2778+ return
2779+ }
2780+ previousY = mouseY
2781+ dragDirection = yOffset > 0 ? "BottomToTop" : "TopToBottom"
2782+ }
2783+ }
2784+
2785+ // CUSTOM fake header to make the page with bottom edge transition smoother
2786+ FakeHeader {
2787+ id: fakeHeader
2788+
2789+ anchors {
2790+ left: parent.left
2791+ right: parent.right
2792+ }
2793+ y: -fakeHeader.height + (fakeHeader.height * (page.height - bottomEdge.y)) / (page.height - fakeHeader.height)
2794+ z: bgVisual.z + 1
2795+
2796+ Behavior on y {
2797+ UbuntuNumberAnimation {
2798+ duration: UbuntuAnimation.SnapDuration
2799+ }
2800+ }
2801+ }
2802+
2803+ Rectangle {
2804+ id: bottomEdge
2805+ objectName: "bottomEdge"
2806+
2807+ readonly property int tipHeight: units.gu(3)
2808+ // CUSTOM value
2809+ readonly property int pageStartY: fakeHeader.height
2810+
2811+ z: 1
2812+ color: Theme.palette.normal.background
2813+ clip: true
2814+ anchors {
2815+ left: parent.left
2816+ right: parent.right
2817+ }
2818+ height: page.height
2819+ y: height
2820+
2821+ visible: !page.isCollapsed
2822+ state: "collapsed"
2823+ states: [
2824+ State {
2825+ name: "collapsed"
2826+ PropertyChanges {
2827+ target: bottomEdge
2828+ y: bottomEdge.height
2829+ }
2830+ // CUSTOM
2831+ PropertyChanges {
2832+ target: fakeHeader
2833+ y: -fakeHeader.height
2834+ }
2835+ },
2836+ State {
2837+ name: "expanded"
2838+ PropertyChanges {
2839+ target: bottomEdge
2840+ y: bottomEdge.pageStartY
2841+ }
2842+ // CUSTOM
2843+ PropertyChanges {
2844+ target: fakeHeader
2845+ y: 0
2846+ }
2847+ },
2848+ State {
2849+ name: "floating"
2850+ when: mouseArea.drag.active
2851+ PropertyChanges {
2852+ target: shadow
2853+ opacity: 1.0
2854+ }
2855+ }
2856+ ]
2857+
2858+ transitions: [
2859+ Transition {
2860+ to: "expanded"
2861+ SequentialAnimation {
2862+ alwaysRunToEnd: true
2863+ ParallelAnimation {
2864+ SmoothedAnimation {
2865+ target: bottomEdge
2866+ property: "y"
2867+ duration: UbuntuAnimation.FastDuration
2868+ easing.type: Easing.Linear
2869+ }
2870+ // CUSTOM
2871+ SmoothedAnimation {
2872+ target: fakeHeader
2873+ property: "y"
2874+ duration: UbuntuAnimation.FastDuration
2875+ easing.type: Easing.Linear
2876+ }
2877+ }
2878+ SmoothedAnimation {
2879+ target: edgeLoader
2880+ property: "anchors.topMargin"
2881+ to: - units.gu(4)
2882+ duration: UbuntuAnimation.FastDuration
2883+ easing.type: Easing.Linear
2884+ }
2885+ SmoothedAnimation {
2886+ target: edgeLoader
2887+ property: "anchors.topMargin"
2888+ to: 0
2889+ duration: UbuntuAnimation.FastDuration
2890+ easing: UbuntuAnimation.StandardEasing
2891+ }
2892+ ScriptAction {
2893+ script: page._pushPage()
2894+ }
2895+ }
2896+ },
2897+ Transition {
2898+ from: "expanded"
2899+ to: "collapsed"
2900+ SequentialAnimation {
2901+ alwaysRunToEnd: true
2902+
2903+ ScriptAction {
2904+ script: {
2905+ Qt.inputMethod.hide()
2906+ edgeLoader.item.parent = edgeLoader
2907+ edgeLoader.item.anchors.fill = edgeLoader
2908+ edgeLoader.item.active = false
2909+ }
2910+ }
2911+ ParallelAnimation {
2912+ SmoothedAnimation {
2913+ target: bottomEdge
2914+ property: "y"
2915+ duration: UbuntuAnimation.SlowDuration
2916+ }
2917+ // CUSTOM
2918+ SmoothedAnimation {
2919+ target: fakeHeader
2920+ property: "y"
2921+ duration: UbuntuAnimation.SlowDuration
2922+ }
2923+ }
2924+ ScriptAction {
2925+ script: {
2926+ // destroy current bottom page
2927+ if (page.reloadBottomEdgePage) {
2928+ edgeLoader.active = false
2929+ } else {
2930+ tip.forceActiveFocus()
2931+ }
2932+
2933+ // notify
2934+ page.bottomEdgeDismissed()
2935+
2936+ edgeLoader.active = true
2937+ }
2938+ }
2939+ }
2940+ },
2941+ Transition {
2942+ from: "floating"
2943+ to: "collapsed"
2944+ // MODIFIED
2945+ UbuntuNumberAnimation {
2946+ target: bottomEdge
2947+ property: "opacity"
2948+ }
2949+ }
2950+ ]
2951+
2952+ Loader {
2953+ id: edgeLoader
2954+
2955+ asynchronous: true
2956+ anchors.fill: parent
2957+ //WORKAROUND: The SDK move the page contents down to allocate space for the header we need to avoid that during the page dragging
2958+ Binding {
2959+ target: edgeLoader.status === Loader.Ready ? edgeLoader : null
2960+ property: "anchors.topMargin"
2961+ value: edgeLoader.item && edgeLoader.item.flickable ? edgeLoader.item.flickable.contentY : 0
2962+ when: !page.isReady
2963+ }
2964+
2965+ onLoaded: {
2966+ tip.forceActiveFocus()
2967+ if (page.isReady && edgeLoader.item.active !== true) {
2968+ page._pushPage()
2969+ }
2970+ }
2971+ }
2972+ }
2973+}
2974
2975=== added file 'app/components/SettingsButton.qml'
2976--- app/components/SettingsButton.qml 1970-01-01 00:00:00 +0000
2977+++ app/components/SettingsButton.qml 2015-08-05 00:17:22 +0000
2978@@ -0,0 +1,43 @@
2979+/*
2980+ * Copyright (C) 2015 Canonical Ltd
2981+ *
2982+ * This file is part of Ubuntu Weather App
2983+ *
2984+ * Ubuntu Weather App is free software: you can redistribute it and/or modify
2985+ * it under the terms of the GNU General Public License version 3 as
2986+ * published by the Free Software Foundation.
2987+ *
2988+ * Ubuntu Weather App is distributed in the hope that it will be useful,
2989+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2990+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2991+ * GNU General Public License for more details.
2992+ *
2993+ * You should have received a copy of the GNU General Public License
2994+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2995+ */
2996+
2997+import QtQuick 2.4
2998+import Ubuntu.Components 1.2
2999+
3000+
3001+AbstractButton {
3002+ id: settingsButton
3003+ height: width
3004+ width: units.gu(4)
3005+
3006+ onClicked: mainPageStack.push(Qt.resolvedUrl("../ui/SettingsPage.qml"))
3007+
3008+ Rectangle {
3009+ anchors.fill: parent
3010+ color: Theme.palette.selected.background
3011+ visible: parent.pressed
3012+ }
3013+
3014+ Icon {
3015+ anchors.centerIn: parent
3016+ color: UbuntuColors.darkGrey
3017+ height: width
3018+ name: "settings"
3019+ width: units.gu(2.5)
3020+ }
3021+}
3022
3023=== added file 'app/components/StandardListItem.qml'
3024--- app/components/StandardListItem.qml 1970-01-01 00:00:00 +0000
3025+++ app/components/StandardListItem.qml 2015-08-05 00:17:22 +0000
3026@@ -0,0 +1,48 @@
3027+/*
3028+ * Copyright (C) 2015 Canonical Ltd
3029+ *
3030+ * This file is part of Ubuntu Weather App
3031+ *
3032+ * Ubuntu Weather App is free software: you can redistribute it and/or modify
3033+ * it under the terms of the GNU General Public License version 3 as
3034+ * published by the Free Software Foundation.
3035+ *
3036+ * Ubuntu Weather App is distributed in the hope that it will be useful,
3037+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3038+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3039+ * GNU General Public License for more details.
3040+ *
3041+ * You should have received a copy of the GNU General Public License
3042+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3043+ */
3044+
3045+import QtQuick 2.4
3046+import QtQuick.Layouts 1.1
3047+import Ubuntu.Components 1.2
3048+
3049+ListItem {
3050+ id: listItem
3051+
3052+ property alias title: _title.text
3053+ property alias icon: _icon.name
3054+ property alias showIcon: _icon.visible
3055+
3056+ RowLayout {
3057+ anchors { left: parent.left; right: parent.right; verticalCenter: parent.verticalCenter; margins: units.gu(2) }
3058+ height: _icon.height
3059+ spacing: units.gu(2)
3060+
3061+ Label {
3062+ id: _title
3063+ anchors.verticalCenter: _icon.verticalCenter
3064+ elide: Text.ElideRight
3065+ Layout.fillWidth: true
3066+ }
3067+
3068+ Icon {
3069+ id: _icon
3070+ height: units.gu(2); width: height
3071+ name: "go-next"
3072+ }
3073+ }
3074+}
3075
3076=== added file 'app/components/WeatherListView.qml'
3077--- app/components/WeatherListView.qml 1970-01-01 00:00:00 +0000
3078+++ app/components/WeatherListView.qml 2015-08-05 00:17:22 +0000
3079@@ -0,0 +1,31 @@
3080+/*
3081+ * Copyright (C) 2013, 2014, 2015
3082+ * Andrew Hayzen <ahayzen@gmail.com>
3083+ * Daniel Holm <d.holmen@gmail.com>
3084+ * Victor Thompson <victor.thompson@gmail.com>
3085+ *
3086+ * This program is free software; you can redistribute it and/or modify
3087+ * it under the terms of the GNU General Public License as published by
3088+ * the Free Software Foundation; version 3.
3089+ *
3090+ * This program is distributed in the hope that it will be useful,
3091+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3092+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3093+ * GNU General Public License for more details.
3094+ *
3095+ * You should have received a copy of the GNU General Public License
3096+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3097+ */
3098+
3099+import QtQuick 2.4
3100+import Ubuntu.Components 1.2
3101+
3102+UbuntuListView {
3103+ Component.onCompleted: {
3104+ // FIXME: workaround for qtubuntu not returning values depending on the grid unit definition
3105+ // for Flickable.maximumFlickVelocity and Flickable.flickDeceleration
3106+ var scaleFactor = units.gridUnit / 8;
3107+ maximumFlickVelocity = maximumFlickVelocity * scaleFactor;
3108+ flickDeceleration = flickDeceleration * scaleFactor;
3109+ }
3110+}
3111
3112=== added directory 'app/data'
3113=== added file 'app/data/CMakeLists.txt'
3114--- app/data/CMakeLists.txt 1970-01-01 00:00:00 +0000
3115+++ app/data/CMakeLists.txt 2015-08-05 00:17:22 +0000
3116@@ -0,0 +1,5 @@
3117+file(GLOB DATA_QML_JS_FILES *.qml *.js)
3118+
3119+add_custom_target(ubuntu-weather-app_data_QMlFiles ALL SOURCES ${DATA_QML_JS_FILES})
3120+
3121+install(FILES ${DATA_QML_JS_FILES} DESTINATION ${UBUNTU-WEATHER_APP_DIR}/data)
3122
3123=== added file 'app/data/CitiesList.js'
3124--- app/data/CitiesList.js 1970-01-01 00:00:00 +0000
3125+++ app/data/CitiesList.js 2015-08-05 00:17:22 +0000
3126@@ -0,0 +1,65 @@
3127+.pragma library
3128+/*
3129+ * Copyright (C) 2013 Canonical Ltd
3130+ *
3131+ * This program is free software: you can redistribute it and/or modify
3132+ * it under the terms of the GNU General Public License version 3 as
3133+ * published by the Free Software Foundation.
3134+ *
3135+ * This program is distributed in the hope that it will be useful,
3136+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3137+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3138+ * GNU General Public License for more details.
3139+ *
3140+ * You should have received a copy of the GNU General Public License
3141+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3142+ *
3143+ * Authored by: Martin Borho <martin@borho.net>
3144+ */
3145+
3146+var preList = [
3147+ {"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"},
3148+ {"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"},
3149+ {"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"},
3150+ {"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"},
3151+ {"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"},
3152+ {"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"},
3153+ {"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"},
3154+ {"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"},
3155+ {"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"},
3156+ {"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"},
3157+ {"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"},
3158+ {"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"},
3159+ {"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"},
3160+ {"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"},
3161+ {"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"},
3162+ {"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"},
3163+ {"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"},
3164+ {"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"},
3165+ {"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"},
3166+ {"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"},
3167+ {"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"},
3168+ {"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"},
3169+ {"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"},
3170+ {"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"},
3171+ {"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"},
3172+ {"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"},
3173+ {"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"},
3174+ {"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"},
3175+ {"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"},
3176+ {"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"},
3177+ {"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"},
3178+ {"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"},
3179+ {"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"},
3180+ {"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"},
3181+ {"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"},
3182+ {"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"},
3183+ {"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"},
3184+ {"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"},
3185+ {"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"},
3186+ {"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"},
3187+ {"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"},
3188+ {"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"},
3189+ {"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"},
3190+ {"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"}
3191+]
3192
3193=== added file 'app/data/Storage.qml'
3194--- app/data/Storage.qml 1970-01-01 00:00:00 +0000
3195+++ app/data/Storage.qml 2015-08-05 00:17:22 +0000
3196@@ -0,0 +1,214 @@
3197+/*
3198+ * Copyright (C) 2013, 2014, 2015 Canonical Ltd
3199+ *
3200+ * This program is free software: you can redistribute it and/or modify
3201+ * it under the terms of the GNU General Public License version 3 as
3202+ * published by the Free Software Foundation.
3203+ *
3204+ * This program is distributed in the hope that it will be useful,
3205+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3206+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3207+ * GNU General Public License for more details.
3208+ *
3209+ * You should have received a copy of the GNU General Public License
3210+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3211+ *
3212+ * Authored by: Martin Borho <martin@borho.net>
3213+ */
3214+import QtQuick.LocalStorage 2.0
3215+import QtQuick 2.4
3216+
3217+Item {
3218+ property var db: null
3219+
3220+ function openDB() {
3221+ if(db !== null) return;
3222+
3223+ db = LocalStorage.openDatabaseSync("com.ubuntu.weather", "", "Default Ubuntu weather app", 100000);
3224+
3225+ if (db.version === "") {
3226+ db.changeVersion("", "0.1",
3227+ function(tx) {
3228+ tx.executeSql('CREATE TABLE IF NOT EXISTS Locations(id INTEGER PRIMARY KEY AUTOINCREMENT, data TEXT, date TEXT)');
3229+ console.log('Database created');
3230+ });
3231+ // reopen database with new version number
3232+ db = LocalStorage.openDatabaseSync("com.ubuntu.weather", "", "Default Ubuntu weather app", 100000);
3233+ }
3234+
3235+ if(db.version === "0.1") {
3236+ db.changeVersion("0.1", "0.2",
3237+ function(tx) {
3238+ tx.executeSql('CREATE TABLE IF NOT EXISTS settings(key TEXT UNIQUE, value TEXT)');
3239+ console.log('Settings table added, Database upgraded to v0.2');
3240+ });
3241+ // reopen database with new version number
3242+ db = LocalStorage.openDatabaseSync("com.ubuntu.weather", "", "Default Ubuntu weather app", 100000);
3243+ }
3244+
3245+ if(db.version === "0.2") {
3246+ db.changeVersion("0.2", "0.3",
3247+ function(tx) {
3248+ tx.executeSql('DELETE FROM Locations WHERE 1');
3249+ console.log('Removed old locations, Database upgraded to v0.3');
3250+ });
3251+ }
3252+
3253+ if (!settings.migrated) {
3254+ try { // attempt to read the old settings
3255+ var oldSettings = {};
3256+
3257+ // Load old settings
3258+ db.readTransaction( function(tx) {
3259+ var rs = tx.executeSql("SELECT * FROM settings")
3260+
3261+ for(var i = 0; i < rs.rows.length; i++) {
3262+ var row = rs.rows.item(i);
3263+ oldSettings[row.key] = row.value;
3264+ }
3265+ });
3266+
3267+ console.debug("Migrating old data:", JSON.stringify(oldSettings))
3268+
3269+ var metric = Qt.locale().measurementSystem === Locale.MetricSystem
3270+
3271+ // Move to new Settings API
3272+ settings.migrated = true
3273+ settings.service = oldSettings["service"] === undefined ? "weatherchannel" : oldSettings["service"]
3274+
3275+ if (oldSettings["precip_units"] !== undefined) {
3276+ settings.precipUnits = oldSettings["precip_units"]
3277+ } else {
3278+ settings.precipUnits = metric ? "mm" : "in"
3279+ }
3280+
3281+ if (oldSettings["units"] !== undefined) {
3282+ settings.tempScale = oldSettings["units"] === "metric" ? "°C" : "°F"
3283+ settings.units = oldSettings["units"]
3284+ } else {
3285+ settings.tempScale = metric ? "°C" : "°F"
3286+ settings.units = metric ? "metric" : "imperial"
3287+ }
3288+
3289+ if (oldSettings["units"] !== undefined) {
3290+ // If old wind speed was in "kmh" use "kph" instead
3291+ settings.windUnits = oldSettings["wind_units"] === "kmh" ? "kph" : oldSettings["wind_units"]
3292+ } else {
3293+ settings.windUnits = metric ? "kph" : "mph"
3294+ }
3295+
3296+ /*
3297+ TODO: uncomment when reboot is ready to replace existing app
3298+ db.transaction( function(tx) {
3299+ tx.executeSql("DROP TABLE IF EXISTS settings")
3300+ });
3301+ */
3302+ } catch (e) { // likely table did not exist
3303+ console.debug("No old data to migrate.")
3304+ settings.migrated = true
3305+ }
3306+ }
3307+ }
3308+
3309+ function insertLocation(data) {
3310+ openDB();
3311+ var res;
3312+
3313+ db.transaction( function(tx){
3314+ var r = tx.executeSql('INSERT INTO Locations(data, date) VALUES(?, ?)', [JSON.stringify(data), new Date().getTime()]);
3315+ res = r.insertId;
3316+ });
3317+ return res;
3318+ }
3319+
3320+ function insertLocationAtStart(data) {
3321+ var res = insertLocation(data);
3322+ reorder(res, 0);
3323+ return 0;
3324+ }
3325+
3326+ function updateLocation(dbId, data) {
3327+ openDB();
3328+ db.transaction( function(tx){
3329+ var r = tx.executeSql('UPDATE Locations SET data = ?, date=? WHERE id = ?', [JSON.stringify(data), new Date().getTime(), dbId])
3330+ });
3331+ }
3332+
3333+ function getLocations(callback) {
3334+ openDB();
3335+ db.readTransaction(
3336+ function(tx){
3337+ var locations = [];
3338+ var rs = tx.executeSql('SELECT * FROM Locations');
3339+ for(var i = 0; i < rs.rows.length; i++) {
3340+ var row = rs.rows.item(i),
3341+ locData = JSON.parse(row.data);
3342+ locData["updated"] = parseInt(row.date, 10);
3343+ locData["db"] = {id: row.id, updated: new Date(parseInt(row.date, 10))};
3344+ locations.push(locData);
3345+ }
3346+ callback(locations);
3347+ }
3348+ );
3349+ }
3350+
3351+ function clearLocation(location_id) {
3352+ openDB();
3353+ db.transaction(function(tx){
3354+ tx.executeSql('DELETE FROM Locations WHERE id = ?', [location_id]);
3355+ });
3356+ }
3357+
3358+ function clearMultiLocation(locations) {
3359+ openDB();
3360+
3361+ db.transaction(function (tx) {
3362+ // Remove all the deleted indexes
3363+ for (var i=0; i < locations.length; i++) {
3364+ tx.executeSql('DELETE FROM Locations WHERE id=?;', [locations[i]])
3365+ }
3366+
3367+ // Rebuild locations in order
3368+ var rs = tx.executeSql('SELECT id FROM Locations ORDER BY id ASC')
3369+
3370+ for (i=0; i < rs.rows.length; i++) {
3371+ tx.executeSql('UPDATE Locations SET id=? WHERE id=?;',
3372+ [i, rs.rows.item(i).id])
3373+ }
3374+ })
3375+ }
3376+
3377+ function clearDB() { // for dev purposes
3378+ openDB();
3379+ db.transaction(function(tx){
3380+ tx.executeSql('DELETE FROM Locations WHERE 1');
3381+ });
3382+ }
3383+
3384+ function reorder(from, to) {
3385+ openDB();
3386+
3387+ db.transaction(function(tx) {
3388+ // Track to move put as -1 for now
3389+ tx.executeSql('UPDATE Locations SET id=? WHERE id=?;',
3390+ [-1, from])
3391+
3392+ // Shuffle locations inbetween from->to
3393+ if (from > to) {
3394+ for (var i = from-1; i >= to; i--) {
3395+ tx.executeSql('UPDATE Locations SET id=? WHERE id=?;',
3396+ [i+1, i])
3397+ }
3398+ } else {
3399+ for (var j = from+1; j <= to; j++) {
3400+ tx.executeSql('UPDATE Locations SET id=? WHERE id=?;',
3401+ [j-1, j])
3402+ }
3403+ }
3404+
3405+ // Switch moving location to its new position
3406+ tx.executeSql('UPDATE Locations SET id=? WHERE id=?;',
3407+ [to, -1])
3408+ })
3409+ }
3410+}
3411
3412=== added file 'app/data/WeatherApi.js'
3413--- app/data/WeatherApi.js 1970-01-01 00:00:00 +0000
3414+++ app/data/WeatherApi.js 2015-08-05 00:17:22 +0000
3415@@ -0,0 +1,760 @@
3416+.pragma library
3417+/*
3418+ * Copyright (C) 2013 Canonical Ltd
3419+ *
3420+ * This program is free software: you can redistribute it and/or modify
3421+ * it under the terms of the GNU General Public License version 3 as
3422+ * published by the Free Software Foundation.
3423+ *
3424+ * This program is distributed in the hope that it will be useful,
3425+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3426+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3427+ * GNU General Public License for more details.
3428+ *
3429+ * You should have received a copy of the GNU General Public License
3430+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3431+ *
3432+ * Authored by: Raúl Yeguas <neokore@gmail.com>
3433+ * Martin Borho <martin@borho.net>
3434+ * Andrew Starr-Bochicchio <a.starr.b@gmail.com>
3435+ */
3436+
3437+/**
3438+* Version of the response data format.
3439+* Increase this number to force a refresh.
3440+*/
3441+var RESPONSE_DATA_VERSION = 20150404;
3442+
3443+/**
3444+* Helper functions
3445+*/
3446+function debug(obj) {
3447+ print(JSON.stringify(obj))
3448+}
3449+//
3450+function calcFahrenheit(celsius) {
3451+ return celsius * 1.8 + 32;
3452+}
3453+//
3454+function calcMph(ms) {
3455+ return ms*2.24;
3456+}
3457+//
3458+function calcInch(mm) {
3459+ return mm/25.4;
3460+}
3461+//
3462+function calcKph(ms) {
3463+ return ms*3.6;
3464+}
3465+//
3466+function convertKphToMph(kph) {
3467+ return kph*0.621;
3468+}
3469+//
3470+function calcWindDir(degrees) {
3471+ var direction = "?";
3472+ if(degrees >=0 && degrees <= 30){
3473+ direction = "N";
3474+ } else if(degrees >30 && degrees <= 60){
3475+ direction = "NE";
3476+ } else if(degrees >60 && degrees <= 120){
3477+ direction = "E";
3478+ } else if(degrees >120 && degrees <= 150){
3479+ direction = "SE";
3480+ } else if(degrees >150 && degrees <= 210){
3481+ direction = "S";
3482+ } else if(degrees >210 && degrees <= 240){
3483+ direction = "SW";
3484+ } else if(degrees >240 && degrees <= 300){
3485+ direction = "W";
3486+ } else if(degrees >300 && degrees <= 330){
3487+ direction = "NW";
3488+ } else if(degrees >330 && degrees <= 360){
3489+ direction = "N";
3490+ }
3491+ return direction;
3492+}
3493+
3494+//
3495+function getLocationTime(tstamp) {
3496+ var locTime = new Date(tstamp);
3497+ return {
3498+ year: locTime.getUTCFullYear(),
3499+ month: locTime.getUTCMonth(),
3500+ date: locTime.getUTCDate(),
3501+ hours: locTime.getUTCHours(),
3502+ minutes: locTime.getUTCMinutes()
3503+ }
3504+}
3505+// Serialize a JavaScript object to URL parameters
3506+// E.g. {param1: value1, param2: value2} to "param1=value&param2=value"
3507+// TODO: it'd be nice to make it work with either passing a single object
3508+// or several at once
3509+function parameterize(obj) {
3510+ var str = [];
3511+ for(var param in obj) {
3512+ str.push(encodeURIComponent(param) + "=" + encodeURIComponent(obj[param]));
3513+ }
3514+ return str.join("&");
3515+}
3516+
3517+var GeoipApi = (function() {
3518+ var _baseUrl = "http://geoip.ubuntu.com/lookup";
3519+ return {
3520+ getLatLong: function(params, apiCaller, onSuccess, onError) {
3521+ var request = { type: "geolookup",url: _baseUrl},
3522+ resultHandler = (function(request, xmlDoc) {
3523+ var coords = {},
3524+ childNodes = xmlDoc.childNodes;
3525+ for(var i=0;i<childNodes.length;i++) {
3526+ if(childNodes[i].nodeName === "Latitude") {
3527+ coords.lat = childNodes[i].firstChild.nodeValue;
3528+ } else if(childNodes[i].nodeName === "Longitude") {
3529+ coords.lon = childNodes[i].firstChild.nodeValue;
3530+ }
3531+ }
3532+ onSuccess(coords);
3533+ }),
3534+ retryHandler = (function(err) {
3535+ console.log("geolookup retry of "+err.request.url);
3536+ apiCaller(request, resultHandler, onError);
3537+ });
3538+ apiCaller(request, resultHandler, retryHandler);
3539+ }
3540+ }
3541+})();
3542+
3543+var GeonamesApi = (function() {
3544+ /**
3545+ provides neccessary methods for requesting and preparing data from Geonames.org
3546+ */
3547+ var _baseUrl = "http://api.geonames.org/";
3548+ var _username = "uweatherdev"
3549+ var _addParams = "&maxRows=25&featureClass=P"
3550+ //
3551+ function _buildSearchResult(request, data) {
3552+ var searchResult = { locations: [], request: request };
3553+ if(data.geonames) {
3554+ data.geonames.forEach(function(r) {
3555+ searchResult.locations.push({
3556+ name: r.name,
3557+ coord: {lat: r.lat, lon: r.lng},
3558+ country: r.countryCode,
3559+ countryName: r.countryName,
3560+ timezone: r.timezone,
3561+ adminName1: r.adminName1,
3562+ adminName2: r.adminName2,
3563+ adminName3: r.adminName3,
3564+ population: r.population,
3565+ services: {
3566+ "geonames": r.geonameId
3567+ }
3568+ });
3569+ })
3570+ }
3571+ return searchResult;
3572+ }
3573+ //
3574+ return {
3575+ //
3576+ search: function(mode, params, apiCaller, onSuccess, onError) {
3577+ var request,
3578+ retryHandler = (function(err) {
3579+ console.log("search retry of "+err.request.url);
3580+ apiCaller(request, searchResponseHandler, onError);
3581+ }),
3582+ searchResponseHandler = function(request, data) {
3583+ onSuccess(_buildSearchResult(request, data));
3584+ };
3585+ if(mode === "point") {
3586+ request = { type: "search",
3587+ url: _baseUrl+ "findNearbyPlaceNameJSON?style=full&username="+encodeURIComponent(_username)
3588+ +"&lat="+encodeURIComponent(params.coords.lat)+"&lng="+encodeURIComponent(params.coords.lon)
3589+ +_addParams}
3590+ } else {
3591+ request = { type: "search",
3592+ url: _baseUrl+ "searchJSON?style=full&username="+encodeURIComponent(_username)
3593+ +"&name_startsWith="+encodeURIComponent(params.name)+_addParams}
3594+ }
3595+ apiCaller(request, searchResponseHandler, retryHandler);
3596+ }
3597+ }
3598+
3599+})();
3600+
3601+var OpenWeatherMapApi = (function() {
3602+ /**
3603+ provides neccessary methods for requesting and preparing data from OpenWeatherMap.org
3604+ */
3605+ var _baseUrl = "http://api.openweathermap.org/data/2.5/";
3606+ //
3607+ var _serviceName = "openweathermap";
3608+ //
3609+ var _icon_map = {
3610+ "01d": "sun",
3611+ "01n": "moon",
3612+ "02d": "cloud_sun",
3613+ "02n": "cloud_moon",
3614+ "03d": "cloud_sun",
3615+ "03n": "cloud_moon",
3616+ "04d": "cloud",
3617+ "04n": "cloud",
3618+ "09d": "rain",
3619+ "09n": "rain",
3620+ "10d": "rain",
3621+ "10n": "rain",
3622+ "11d": "thunder",
3623+ "11n": "thunder",
3624+ "13d": "snow_shower",
3625+ "13n": "snow_shower",
3626+ "50d": "fog",
3627+ "50n": "fog"
3628+ }
3629+ //
3630+ function _buildDataPoint(date, data) {
3631+ var result = {
3632+ timestamp: data.dt,
3633+ date: date,
3634+ metric: {
3635+ temp:data.main.temp,
3636+ windSpeed: calcKph(data.wind.speed),
3637+ rain: data.main.rain || ((data.rain) ? data.rain["3h"] : false ) || 0,
3638+ snow: data.main.snow || ((data.snow) ? data.snow["3h"] : false ) || 0
3639+ },
3640+ imperial: {
3641+ temp: calcFahrenheit(data.main.temp),
3642+ windSpeed: calcMph(data.wind.speed),
3643+ rain: calcInch(data.main.rain || ((data.rain) ? data.rain["3h"] : false ) || 0),
3644+ snow: calcInch(data.main.snow || ((data.snow) ? data.snow["3h"] : false ) ||0)
3645+ },
3646+ humidity: data.main.humidity,
3647+ pressure: data.main.pressure,
3648+ windDeg: data.wind.deg,
3649+ windDir: calcWindDir(data.wind.deg),
3650+ icon: _icon_map[data.weather[0].icon],
3651+ condition: data.weather[0].main
3652+ };
3653+ if(data.id !== undefined) {
3654+ result["service"] = _serviceName;
3655+ result["service_id"] = data.id;
3656+ }
3657+ return result;
3658+ }
3659+ //
3660+ function _buildDayFormat(date, data) {
3661+ var result = {
3662+ date: date,
3663+ timestamp: data.dt,
3664+ metric: {
3665+ tempMin: data.temp.min,
3666+ tempMax: data.temp.max,
3667+ windSpeed: calcKph(data.speed),
3668+ rain: data.rain || 0,
3669+ snow: data.snow || 0
3670+ },
3671+ imperial: {
3672+ tempMin: calcFahrenheit(data.temp.min),
3673+ tempMax: calcFahrenheit(data.temp.max),
3674+ windSpeed: calcMph(data.speed),
3675+ rain: calcInch(data.rain || 0),
3676+ snow: calcInch(data.snow || 0)
3677+ },
3678+ pressure: data.pressure,
3679+ humidity: data.humidity,
3680+ icon: _icon_map[data.weather[0].icon],
3681+ condition: data.weather[0].main,
3682+ windDeg: data.deg,
3683+ windDir: calcWindDir(data.deg),
3684+ hourly: []
3685+ }
3686+ return result;
3687+ }
3688+ //
3689+ function formatResult(data, location) {
3690+ var tmpResult = {},
3691+ result = [],
3692+ day=null,
3693+ offset=(location.timezone && location.timezone.gmtOffset) ? location.timezone.gmtOffset*60*60*1000: 0,
3694+ localNow = getLocationTime(new Date().getTime()+offset),
3695+ todayDate;
3696+ print("["+location.name+"] "+JSON.stringify(localNow))
3697+ // add openweathermap id for faster responses
3698+ if(location.services && !location.services[_serviceName] && data["current"].id) {
3699+ location.services[_serviceName] = data["current"].id
3700+ }
3701+ //
3702+ data["daily"]["list"].forEach(function(dayData) {
3703+ var date = getLocationTime(((dayData.dt*1000)-1000)+offset), // minus 1 sec to handle +/-12 TZ
3704+ day = date.year+"-"+date.month+"-"+date.date;
3705+ if(!todayDate) {
3706+ if(localNow.year+"-"+localNow.month+"-"+localNow.date > day) {
3707+ // skip "yesterday"
3708+ return;
3709+ }
3710+ todayDate = date;
3711+ }
3712+ tmpResult[day] = _buildDayFormat(date, dayData);
3713+ })
3714+ //
3715+ var today = todayDate.year+"-"+todayDate.month+"-"+todayDate.date
3716+ tmpResult[today]["current"] = _buildDataPoint(todayDate, data["current"]);
3717+ if(data["forecast"] !== undefined) {
3718+ data["forecast"]["list"].forEach(function(hourData) {
3719+ var dateData = getLocationTime((hourData.dt*1000)+offset),
3720+ day = dateData.year+"-"+dateData.month+"-"+dateData.date;
3721+ if(tmpResult[day]) {
3722+ tmpResult[day]["hourly"].push(_buildDataPoint(dateData, hourData));
3723+ }
3724+ })
3725+ }
3726+ //
3727+ for(var d in tmpResult) {
3728+ result.push(tmpResult[d]);
3729+ }
3730+ return result;
3731+ }
3732+ //
3733+ function _getUrls(params) {
3734+ var urls = {
3735+ current: "",
3736+ daily: "",
3737+ forecast: ""
3738+ },
3739+ latLongParams = "&lat="+encodeURIComponent(params.location.coord.lat)
3740+ + "&lon="+encodeURIComponent(params.location.coord.lon);
3741+ if(params.location.services && params.location.services[_serviceName]) {
3742+ urls.current = _baseUrl + "weather?units="+params.units+"&id="+params.location.services[_serviceName];
3743+ urls.daily = _baseUrl + "forecast/daily?id="+params.location.services[_serviceName]+"&cnt=10&units="+params.units
3744+ urls.forecast = _baseUrl + "forecast?id="+params.location.services[_serviceName]+"&units="+params.units
3745+
3746+ } else if (params.location.coord) {
3747+ urls.current = _baseUrl + "weather?units="+params.units+latLongParams;
3748+ urls.daily = _baseUrl+"forecast/daily?cnt=10&units="+params.units+latLongParams;
3749+ urls.forecast = _baseUrl+"forecast?units="+params.units+latLongParams;
3750+ }
3751+ return urls;
3752+ }
3753+ //
3754+ return {
3755+ //
3756+ getData: function(params, apiCaller, onSuccess, onError) {
3757+ var urls = _getUrls(params),
3758+ handlerMap = {
3759+ current: { type: "current",url: urls.current},
3760+ daily: { type: "daily",url: urls.daily},
3761+ forecast: { type: "forecast", url: urls.forecast}},
3762+ response = {
3763+ location: params.location,
3764+ db: (params.db) ? params.db : null,
3765+ format: RESPONSE_DATA_VERSION
3766+ },
3767+ respData = {},
3768+ addDataToResponse = (function(request, data) {
3769+ var formattedResult;
3770+ respData[request.type] = data;
3771+ if(respData["current"] !== undefined
3772+ && respData["forecast"] !== undefined
3773+ && respData["daily"] !== undefined) {
3774+ response["data"] = formatResult(respData, params.location)
3775+ onSuccess(response);
3776+ }
3777+ }),
3778+ onErrorHandler = (function(err) {
3779+ onError(err);
3780+ }),
3781+ retryHandler = (function(err) {
3782+ console.log("retry of "+err.request.url);
3783+ var retryFunc = handlerMap[err.request.type];
3784+ apiCaller(retryFunc, addDataToResponse, onErrorHandler);
3785+ });
3786+ //
3787+ apiCaller(handlerMap.current, addDataToResponse, retryHandler);
3788+ apiCaller(handlerMap.forecast, addDataToResponse, retryHandler);
3789+ apiCaller(handlerMap.daily, addDataToResponse, retryHandler);
3790+ }
3791+ }
3792+
3793+})();
3794+
3795+var WeatherChannelApi = (function() {
3796+ /**
3797+ provides neccessary methods for requesting and preparing data from OpenWeatherMap.org
3798+ */
3799+ var _baseUrl = "http://wxdata.weather.com/wxdata/";
3800+ //
3801+ var _serviceName = "weatherchannel";
3802+ //
3803+ // see http://s.imwx.com/v.20131006.223722/img/wxicon/72/([0-9]+).png
3804+ var _iconMap = {
3805+ "0": "thunder", // ??
3806+ "1": "thunder", // ??
3807+ "2": "thunder", // ??
3808+ "3": "thunder", // ??
3809+ "4": "thunder", //T-Storms
3810+ "5": "snow_rain", //Rain / Snow
3811+ "6": "snow_rain", // ??
3812+ "7": "snow_rain", //Wintry Mix
3813+ "8": "scattered", //Freezing Drizzle
3814+ "9": "scattered", //Drizzle
3815+ "10": "rain", // ??
3816+ "11": "rain", //Showers
3817+ "12": "rain", //Rain
3818+ "13": "snow_shower", // ??
3819+ "14": "snow_shower", //Snow shower/Light snow
3820+ "15": "snow_shower", //
3821+ "16": "snow_shower", //Snow
3822+ "17": "thunder", // Hail??
3823+ "18": "snow_rain", // Rain / Snow ??
3824+ "19": "fog", //Fog ??
3825+ "20": "fog", //Fog
3826+ "21": "fog", //Haze
3827+ "22": "fog", // ??
3828+ "23": "fog", // Wind ??
3829+ "24": "overcast", //Partly Cloudy / Wind
3830+ "25": "overcast", // ??
3831+ "26": "overcast",//Cloudy
3832+ "27": "cloud_moon",//Mostly Cloudy
3833+ "28": "cloud_sun", //Mostly Cloudy
3834+ "29": "cloud_moon", //Partly Cloudy
3835+ "30": "cloud_sun", //Partly Cloudy
3836+ "31": "moon", //Clear
3837+ "32": "sun", //Sunny
3838+ "33": "cloud_moon", //Mostly Clear
3839+ "34": "cloud_sun", //Mostly Sunny
3840+ "35": "snow_rain", // ??
3841+ "36": "sun", //Sunny
3842+ "37": "thunder", //Isolated T-Storms
3843+ "38": "thunder", //Scattered T-Storms
3844+ "39": "scattered", //Scattered Showers
3845+ "40": "rain", // ??
3846+ "41": "snow", //Scattered Snow Showers
3847+ "42": "snow_shower", // ??
3848+ "43": "snow_shower", // ??
3849+ "44": "fog", // ??
3850+ "45": "scattered", // ??
3851+ "46": "snow_shower", //Snow Showers Early
3852+ "47": "thunder" //Isolated T-Storms
3853+ };
3854+ //
3855+ function _buildDataPoint(date, dataObj) {
3856+ var data = dataObj["Observation"] || dataObj,
3857+ result = {
3858+ timestamp: data.date || data.dateTime,
3859+ date: date,
3860+ metric: {
3861+ temp: data.temp,
3862+ tempFeels: data.feelsLike,
3863+ windSpeed: data.wSpeed
3864+ },
3865+ imperial: {
3866+ temp: calcFahrenheit(data.temp),
3867+ tempFeels: calcFahrenheit(data.feelsLike),
3868+ windSpeed: convertKphToMph(data.wSpeed)
3869+ },
3870+ precipType: (data.precip_type !== undefined) ? data.precip_type : null,
3871+ propPrecip: (data.pop !== undefined) ? data.pop : null,
3872+ humidity: data.humid,
3873+ pressure: data.pressure,
3874+ windDeg: data.wDir,
3875+ windDir: data.wDirText,
3876+ icon: _iconMap[(data.wxIcon||data.icon)],
3877+ condition: data.text || data.wDesc,
3878+ uv: data.uv
3879+ };
3880+ if(_iconMap[data.wxIcon||data.icon] === undefined) {
3881+ print("ICON MISSING POINT: "+(data.wxIcon||data.icon)+" "+result.condition)
3882+ }
3883+ return result;
3884+ }
3885+ //
3886+ function _buildDayFormat(date, data, now) {
3887+ var partData = (now > data.validDate || data.day === undefined) ? data.night : data.day,
3888+ result = {
3889+ date: date,
3890+ timestamp: data.validDate,
3891+ metric: {
3892+ tempMin: data.minTemp,
3893+ tempMax: data.maxTemp,
3894+ windSpeed: partData.wSpeed
3895+ },
3896+ imperial: {
3897+ tempMin: calcFahrenheit(data.minTemp),
3898+ tempMax: calcFahrenheit(data.maxTemp !== undefined ? data.maxTemp : data.minTemp),
3899+ windSpeed: convertKphToMph(partData.wSpeed)
3900+ },
3901+ precipType: partData.precip_type,
3902+ propPrecip: partData.pop,
3903+ pressure: null,
3904+ humidity: partData.humid,
3905+ icon: _iconMap[partData.icon],
3906+ condition: partData.phrase,
3907+ windDeg: partData.wDir,
3908+ windDir: partData.wDirText,
3909+ uv: partData.uv,
3910+ hourly: []
3911+ }
3912+ if(_iconMap[partData.icon] === undefined) {
3913+ print("ICON MISSING DAY: "+partData.icon+" "+result.condition)
3914+ }
3915+ return result;
3916+ }
3917+ //
3918+ function formatResult(combinedData, location) {
3919+ var tmpResult = {}, result = [],
3920+ day=null, todayDate,
3921+ offset=(location.timezone && location.timezone.gmtOffset) ? location.timezone.gmtOffset*60*60*1000: 0,
3922+ now = new Date().getTime(),
3923+ nowMs = parseInt(now/1000),
3924+ localNow = getLocationTime(now+offset),
3925+ data = {
3926+ "location": combinedData[0]["Location"],
3927+ "daily": combinedData[0]["DailyForecasts"],
3928+ "forecast": combinedData[0]["HourlyForecasts"],
3929+ "current": combinedData[0]["StandardObservation"],
3930+ "sunRiseSet": combinedData[0]["SunRiseSet"],
3931+ };
3932+ print("["+location.name+"] "+JSON.stringify(localNow));
3933+ // add openweathermap id for faster responses
3934+ if(location.services && !location.services[_serviceName] && data["location"].key) {
3935+ location.services[_serviceName] = data["location"].key
3936+ }
3937+ // only 5 days of forecast for TWC
3938+ for(var x=0;x<5;x++) {
3939+ var dayData = data["daily"][x],
3940+ date = getLocationTime(((dayData.validDate*1000)-1000)+offset); // minus 1 sec to handle +/-12 TZ
3941+ var sunRiseSet = data["sunRiseSet"][x];
3942+ day = date.year+"-"+date.month+"-"+date.date;
3943+ if(!todayDate) {
3944+ if(localNow.year+"-"+localNow.month+"-"+localNow.date > day) {
3945+ // skip "yesterday"
3946+ continue;
3947+ }
3948+ todayDate = date;
3949+ }
3950+ tmpResult[day] = _buildDayFormat(date, dayData, nowMs);
3951+ var sunrise = new Date(sunRiseSet.rise*1000);
3952+ var sunset = new Date(sunRiseSet.set*1000);
3953+ tmpResult[day].sunrise = sunrise.toLocaleTimeString();
3954+ tmpResult[day].sunset = sunset.toLocaleTimeString();
3955+ }
3956+ //
3957+ if(data["forecast"] !== undefined) {
3958+ data["forecast"].forEach(function(hourData) {
3959+ var dateData = getLocationTime((hourData.dateTime*1000)+offset),
3960+ day = dateData.year+"-"+dateData.month+"-"+dateData.date;
3961+ if(tmpResult[day]) {
3962+ tmpResult[day]["hourly"].push(_buildDataPoint(dateData, hourData));
3963+ }
3964+ })
3965+ }
3966+ //
3967+ if(data["current"]) {
3968+ var today = todayDate.year+"-"+todayDate.month+"-"+todayDate.date;
3969+ tmpResult[today]["current"] = _buildDataPoint(todayDate, data["current"]);
3970+ // if the icon is missing, use the first from the hourly forecast
3971+ if(!tmpResult[today]["current"].icon && tmpResult[today]["hourly"] && tmpResult[today]["hourly"].length > 0) {
3972+ tmpResult[today]["current"].icon = tmpResult[today]["hourly"][0].icon;
3973+ }
3974+ // if condition text is missing, use the condition from the first hourly forecast
3975+ if((tmpResult[today]["current"].condition === "-" || tmpResult[today]["current"].condition === undefined)
3976+ && (tmpResult[today]["hourly"] && tmpResult[today]["hourly"].length > 0)) {
3977+ tmpResult[today]["current"].condition = tmpResult[today]["hourly"][0].condition;
3978+ }
3979+ }
3980+ //
3981+ for(var d in tmpResult) {
3982+ result.push(tmpResult[d]);
3983+ }
3984+ return result;
3985+ }
3986+ //
3987+ function _getUrl(params) {
3988+ var url, serviceId,
3989+ baseParams = {
3990+ key: params.api_key,
3991+ units: (params.units === "metric") ? "m" : "e",
3992+ locale: Qt.locale().name,
3993+ hours: "48",
3994+ },
3995+ commands = {
3996+ "mobileaggregation": "mobile/mobagg/",
3997+ };
3998+ if(params.location.services && params.location.services[_serviceName]) {
3999+ serviceId = encodeURIComponent(params.location.services[_serviceName]);
4000+ url = _baseUrl+commands["mobileaggregation"]+serviceId+".js?"+parameterize(baseParams);
4001+ } else if (params.location.coord) {
4002+ var coord = {lat: params.location.coord.lat, lng: params.location.coord.lon};
4003+ url = _baseUrl+commands["mobileaggregation"]+"get.js?"+parameterize(baseParams)+"&"+
4004+ parameterize(coord);
4005+ }
4006+ return url;
4007+ }
4008+ //
4009+ return {
4010+ getData: function(params, apiCaller, onSuccess, onError) {
4011+ var url = _getUrl(params),
4012+ handlerMap = {
4013+ all: { type: "all", url: url}
4014+ },
4015+ response = {
4016+ location: params.location,
4017+ db: (params.db) ? params.db : null,
4018+ format: RESPONSE_DATA_VERSION
4019+ },
4020+ addDataToResponse = (function(request, data) {
4021+ var formattedResult;
4022+ response["data"] = formatResult(data, params.location);
4023+ onSuccess(response);
4024+ }),
4025+ onErrorHandler = (function(err) {
4026+ onError(err);
4027+ });
4028+ apiCaller(handlerMap.all, addDataToResponse, onErrorHandler);
4029+ }
4030+ }
4031+})();
4032+
4033+var WeatherApi = (function(_services) {
4034+ /**
4035+ proxy for requesting weather apis, the passed _services are providing the respective api endpoints
4036+ and formatters to build a uniform response object
4037+ */
4038+ function _getService(name) {
4039+ if(_services[name] !== undefined) {
4040+ return _services[name];
4041+ }
4042+ return _services["weatherchannel"];
4043+ }
4044+ //
4045+ function _sendRequest(request, onSuccess, onError) {
4046+ var xmlHttp = new XMLHttpRequest();
4047+ if (xmlHttp) {
4048+ console.log("Sent request URL: " + request.url);
4049+ xmlHttp.open('GET', request.url, true);
4050+ xmlHttp.onreadystatechange = function () {
4051+ try {
4052+ if (xmlHttp.readyState == 4) {
4053+ if(xmlHttp.status === 200) {
4054+ if(xmlHttp.responseXML) {
4055+ onSuccess(request, xmlHttp.responseXML.documentElement);
4056+ } else {
4057+ var json = JSON.parse(xmlHttp.responseText);
4058+ onSuccess(request,json);
4059+ }
4060+ } else {
4061+ onError({
4062+ msg: "wrong response http code, got "+xmlHttp.status,
4063+ request: request
4064+ });
4065+ }
4066+ }
4067+ } catch (e) {
4068+ print("Exception: "+e)
4069+ onError({msg: "wrong response data format", request: request});
4070+ }
4071+ };
4072+ xmlHttp.send(null);
4073+ }
4074+ }
4075+ //
4076+ return {
4077+ //
4078+ geoLookup: function(params, onSuccess, onError) {
4079+ var service = _getService('geoip'),
4080+ geoNameService = _getService('geonames'),
4081+ lookupHandler = function(data) {
4082+ print("Geolookup: "+JSON.stringify(data))
4083+ geoNameService.search("point", {coords:data}, _sendRequest, onSuccess, onError);
4084+ };
4085+ service.getLatLong(params, _sendRequest, lookupHandler, onError)
4086+ },
4087+ //
4088+ search: function(mode, params, onSuccess, onError) {
4089+ var service = _getService('geonames');
4090+ service.search(mode, params, _sendRequest, onSuccess, onError);
4091+ },
4092+ //
4093+ getLocationData: function(params, onSuccess, onError) {
4094+ var service = _getService(params.service);
4095+ service.getData(params, _sendRequest, onSuccess, onError);
4096+ },
4097+ }
4098+})({
4099+ "openweathermap": OpenWeatherMapApi,
4100+ "weatherchannel": WeatherChannelApi,
4101+ "geonames": GeonamesApi,
4102+ "geoip": GeoipApi
4103+});
4104+
4105+var sendRequest = function(message, responseCallback) {
4106+ // handles the response data
4107+ var finished = function(result) {
4108+ // print result to get data for test json files
4109+ // print(JSON.stringify(result));
4110+ //WorkerScript.sendMessage({
4111+ responseCallback({
4112+ action: message.action,
4113+ result: result
4114+ })
4115+ }
4116+ // handles errors
4117+ var onError = function(err) {
4118+ console.log(JSON.stringify(err, null, true));
4119+ //WorkerScript.sendMessage({ 'error': err})
4120+ responseCallback({ 'error': err})
4121+ }
4122+ // keep order of locations, sort results
4123+ var sortDataResults = function(locA, locB) {
4124+ return locA.db.id - locB.db.id;
4125+ }
4126+ // perform the api calls
4127+ if(message.action === "searchByName") {
4128+ WeatherApi.search("name", message.params, finished, onError);
4129+ } else if(message.action === "searchByPoint") {
4130+ WeatherApi.search("point", message.params, finished, onError);
4131+ } else if(message.action === "getGeoIp") {
4132+ WeatherApi.geoLookup(message.params, finished, onError);
4133+ } else if(message.action === "updateData") {
4134+ var locLength = message.params.locations.length,
4135+ locUpdated = 0,
4136+ result = [],
4137+ now = new Date().getTime();
4138+ if(locLength > 0) {
4139+ message.params.locations.forEach(function(loc) {
4140+ var updatedHnd = function (newData, cached) {
4141+ locUpdated += 1;
4142+ if(cached === true) {
4143+ newData["save"] = false;
4144+ } else {
4145+ newData["save"] = true;
4146+ newData["updated"] = new Date().getTime();
4147+ }
4148+ result.push(newData);
4149+ if(locUpdated === locLength) {
4150+ result.sort(sortDataResults);
4151+ finished(result);
4152+ }
4153+ },
4154+ params = {
4155+ location:loc.location,
4156+ db: loc.db,
4157+ units: 'metric',
4158+ service: message.params.service,
4159+ api_key: message.params.api_key,
4160+ interval: message.params.interval
4161+ },
4162+ secsFromLastFetch = (now-loc.updated)/1000;
4163+ if( message.params.force===true || loc.format !== RESPONSE_DATA_VERSION || secsFromLastFetch > params.interval){
4164+ // data older than 30min, location is new or data format is deprecated
4165+ WeatherApi.getLocationData(params, updatedHnd, onError);
4166+ } else {
4167+ console.log("["+loc.location.name+"] returning cached data, time from last fetch: "+secsFromLastFetch)
4168+ updatedHnd(loc, true);
4169+ }
4170+ })
4171+ } else {
4172+ finished(result);
4173+ }
4174+ }
4175+}
4176
4177=== added file 'app/data/key.js'
4178--- app/data/key.js 1970-01-01 00:00:00 +0000
4179+++ app/data/key.js 2015-08-05 00:17:22 +0000
4180@@ -0,0 +1,1 @@
4181+var twcKey = "";
4182
4183=== added file 'app/data/suncalc.js'
4184--- app/data/suncalc.js 1970-01-01 00:00:00 +0000
4185+++ app/data/suncalc.js 2015-08-05 00:17:22 +0000
4186@@ -0,0 +1,300 @@
4187+/*
4188+ (c) 2011-2015, Vladimir Agafonkin
4189+ SunCalc is a JavaScript library for calculating sun/moon position and light phases.
4190+ https://github.com/mourner/suncalc
4191+*/
4192+
4193+// shortcuts for easier to read formulas
4194+
4195+var PI = Math.PI,
4196+ sin = Math.sin,
4197+ cos = Math.cos,
4198+ tan = Math.tan,
4199+ asin = Math.asin,
4200+ atan = Math.atan2,
4201+ acos = Math.acos,
4202+ rad = PI / 180;
4203+
4204+// sun calculations are based on http://aa.quae.nl/en/reken/zonpositie.html formulas
4205+
4206+
4207+// date/time constants and conversions
4208+
4209+var dayMs = 1000 * 60 * 60 * 24,
4210+ J1970 = 2440588,
4211+ J2000 = 2451545;
4212+
4213+function toJulian(date) { return date.valueOf() / dayMs - 0.5 + J1970; }
4214+function fromJulian(j) { return new Date((j + 0.5 - J1970) * dayMs); }
4215+function toDays(date) { return toJulian(date) - J2000; }
4216+
4217+
4218+// general calculations for position
4219+
4220+var e = rad * 23.4397; // obliquity of the Earth
4221+
4222+function rightAscension(l, b) { return atan(sin(l) * cos(e) - tan(b) * sin(e), cos(l)); }
4223+function declination(l, b) { return asin(sin(b) * cos(e) + cos(b) * sin(e) * sin(l)); }
4224+
4225+function azimuth(H, phi, dec) { return atan(sin(H), cos(H) * sin(phi) - tan(dec) * cos(phi)); }
4226+function altitude(H, phi, dec) { return asin(sin(phi) * sin(dec) + cos(phi) * cos(dec) * cos(H)); }
4227+
4228+function siderealTime(d, lw) { return rad * (280.16 + 360.9856235 * d) - lw; }
4229+
4230+
4231+// general sun calculations
4232+
4233+function solarMeanAnomaly(d) { return rad * (357.5291 + 0.98560028 * d); }
4234+
4235+function eclipticLongitude(M) {
4236+
4237+ var C = rad * (1.9148 * sin(M) + 0.02 * sin(2 * M) + 0.0003 * sin(3 * M)), // equation of center
4238+ P = rad * 102.9372; // perihelion of the Earth
4239+
4240+ return M + C + P + PI;
4241+}
4242+
4243+function sunCoords(d) {
4244+
4245+ var M = solarMeanAnomaly(d),
4246+ L = eclipticLongitude(M);
4247+
4248+ return {
4249+ dec: declination(L, 0),
4250+ ra: rightAscension(L, 0)
4251+ };
4252+}
4253+
4254+
4255+var SunCalc = {};
4256+
4257+
4258+// calculates sun position for a given date and latitude/longitude
4259+
4260+SunCalc.getPosition = function (date, lat, lng) {
4261+
4262+ var lw = rad * -lng,
4263+ phi = rad * lat,
4264+ d = toDays(date),
4265+
4266+ c = sunCoords(d),
4267+ H = siderealTime(d, lw) - c.ra;
4268+
4269+ return {
4270+ azimuth: azimuth(H, phi, c.dec),
4271+ altitude: altitude(H, phi, c.dec)
4272+ };
4273+};
4274+
4275+
4276+// sun times configuration (angle, morning name, evening name)
4277+
4278+var times = SunCalc.times = [
4279+ [-0.833, 'sunrise', 'sunset' ],
4280+ [ -0.3, 'sunriseEnd', 'sunsetStart' ],
4281+ [ -6, 'dawn', 'dusk' ],
4282+ [ -12, 'nauticalDawn', 'nauticalDusk'],
4283+ [ -18, 'nightEnd', 'night' ],
4284+ [ 6, 'goldenHourEnd', 'goldenHour' ]
4285+];
4286+
4287+// adds a custom time to the times config
4288+
4289+SunCalc.addTime = function (angle, riseName, setName) {
4290+ times.push([angle, riseName, setName]);
4291+};
4292+
4293+
4294+// calculations for sun times
4295+
4296+var J0 = 0.0009;
4297+
4298+function julianCycle(d, lw) { return Math.round(d - J0 - lw / (2 * PI)); }
4299+
4300+function approxTransit(Ht, lw, n) { return J0 + (Ht + lw) / (2 * PI) + n; }
4301+function solarTransitJ(ds, M, L) { return J2000 + ds + 0.0053 * sin(M) - 0.0069 * sin(2 * L); }
4302+
4303+function hourAngle(h, phi, d) { return acos((sin(h) - sin(phi) * sin(d)) / (cos(phi) * cos(d))); }
4304+
4305+// returns set time for the given sun altitude
4306+function getSetJ(h, lw, phi, dec, n, M, L) {
4307+
4308+ var w = hourAngle(h, phi, dec),
4309+ a = approxTransit(w, lw, n);
4310+ return solarTransitJ(a, M, L);
4311+}
4312+
4313+
4314+// calculates sun times for a given date and latitude/longitude
4315+
4316+SunCalc.getTimes = function (date, lat, lng) {
4317+
4318+ var lw = rad * -lng,
4319+ phi = rad * lat,
4320+
4321+ d = toDays(date),
4322+ n = julianCycle(d, lw),
4323+ ds = approxTransit(0, lw, n),
4324+
4325+ M = solarMeanAnomaly(ds),
4326+ L = eclipticLongitude(M),
4327+ dec = declination(L, 0),
4328+
4329+ Jnoon = solarTransitJ(ds, M, L),
4330+
4331+ i, len, time, Jset, Jrise;
4332+
4333+
4334+ var result = {
4335+ solarNoon: fromJulian(Jnoon),
4336+ nadir: fromJulian(Jnoon - 0.5)
4337+ };
4338+
4339+ for (i = 0, len = times.length; i < len; i += 1) {
4340+ time = times[i];
4341+
4342+ Jset = getSetJ(time[0] * rad, lw, phi, dec, n, M, L);
4343+ Jrise = Jnoon - (Jset - Jnoon);
4344+
4345+ result[time[1]] = fromJulian(Jrise);
4346+ result[time[2]] = fromJulian(Jset);
4347+ }
4348+
4349+ return result;
4350+};
4351+
4352+
4353+// moon calculations, based on http://aa.quae.nl/en/reken/hemelpositie.html formulas
4354+
4355+function moonCoords(d) { // geocentric ecliptic coordinates of the moon
4356+
4357+ var L = rad * (218.316 + 13.176396 * d), // ecliptic longitude
4358+ M = rad * (134.963 + 13.064993 * d), // mean anomaly
4359+ F = rad * (93.272 + 13.229350 * d), // mean distance
4360+
4361+ l = L + rad * 6.289 * sin(M), // longitude
4362+ b = rad * 5.128 * sin(F), // latitude
4363+ dt = 385001 - 20905 * cos(M); // distance to the moon in km
4364+
4365+ return {
4366+ ra: rightAscension(l, b),
4367+ dec: declination(l, b),
4368+ dist: dt
4369+ };
4370+}
4371+
4372+SunCalc.getMoonPosition = function (date, lat, lng) {
4373+
4374+ var lw = rad * -lng,
4375+ phi = rad * lat,
4376+ d = toDays(date),
4377+
4378+ c = moonCoords(d),
4379+ H = siderealTime(d, lw) - c.ra,
4380+ h = altitude(H, phi, c.dec);
4381+
4382+ // altitude correction for refraction
4383+ h = h + rad * 0.017 / tan(h + rad * 10.26 / (h + rad * 5.10));
4384+
4385+ return {
4386+ azimuth: azimuth(H, phi, c.dec),
4387+ altitude: h,
4388+ distance: c.dist
4389+ };
4390+};
4391+
4392+
4393+// calculations for illumination parameters of the moon,
4394+// based on http://idlastro.gsfc.nasa.gov/ftp/pro/astro/mphase.pro formulas and
4395+// Chapter 48 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998.
4396+
4397+SunCalc.getMoonIllumination = function (date) {
4398+
4399+ var d = toDays(date),
4400+ s = sunCoords(d),
4401+ m = moonCoords(d),
4402+
4403+ sdist = 149598000, // distance from Earth to Sun in km
4404+
4405+ phi = acos(sin(s.dec) * sin(m.dec) + cos(s.dec) * cos(m.dec) * cos(s.ra - m.ra)),
4406+ inc = atan(sdist * sin(phi), m.dist - sdist * cos(phi)),
4407+ angle = atan(cos(s.dec) * sin(s.ra - m.ra), sin(s.dec) * cos(m.dec) -
4408+ cos(s.dec) * sin(m.dec) * cos(s.ra - m.ra));
4409+
4410+ return {
4411+ fraction: (1 + cos(inc)) / 2,
4412+ phase: 0.5 + 0.5 * inc * (angle < 0 ? -1 : 1) / Math.PI,
4413+ angle: angle
4414+ };
4415+};
4416+
4417+
4418+function hoursLater(date, h) {
4419+ return new Date(date.valueOf() + h * dayMs / 24);
4420+}
4421+
4422+// calculations for moon rise/set times are based on http://www.stargazing.net/kepler/moonrise.html article
4423+
4424+SunCalc.getMoonTimes = function (date, lat, lng) {
4425+ var t = new Date(date);
4426+ t.setHours(0);
4427+ t.setMinutes(0);
4428+ t.setSeconds(0);
4429+ t.setMilliseconds(0);
4430+
4431+ var hc = 0.133 * rad,
4432+ h0 = SunCalc.getMoonPosition(t, lat, lng).altitude - hc,
4433+ h1, h2, rise, set, a, b, xe, ye, d, roots, x1, x2, dx;
4434+
4435+ // go in 2-hour chunks, each time seeing if a 3-point quadratic curve crosses zero (which means rise or set)
4436+ for (var i = 1; i <= 24; i += 2) {
4437+ h1 = SunCalc.getMoonPosition(hoursLater(t, i), lat, lng).altitude - hc;
4438+ h2 = SunCalc.getMoonPosition(hoursLater(t, i + 1), lat, lng).altitude - hc;
4439+
4440+ a = (h0 + h2) / 2 - h1;
4441+ b = (h2 - h0) / 2;
4442+ xe = -b / (2 * a);
4443+ ye = (a * xe + b) * xe + h1;
4444+ d = b * b - 4 * a * h1;
4445+ roots = 0;
4446+
4447+ if (d >= 0) {
4448+ dx = Math.sqrt(d) / (Math.abs(a) * 2);
4449+ x1 = xe - dx;
4450+ x2 = xe + dx;
4451+ if (Math.abs(x1) <= 1) roots++;
4452+ if (Math.abs(x2) <= 1) roots++;
4453+ if (x1 < -1) x1 = x2;
4454+ }
4455+
4456+ if (roots === 1) {
4457+ if (h0 < 0) rise = i + x1;
4458+ else set = i + x1;
4459+
4460+ } else if (roots === 2) {
4461+ rise = i + (ye < 0 ? x2 : x1);
4462+ set = i + (ye < 0 ? x1 : x2);
4463+ }
4464+
4465+ if (rise && set) break;
4466+
4467+ h0 = h2;
4468+ }
4469+
4470+ var result = {};
4471+
4472+ if (rise) result.rise = hoursLater(t, rise);
4473+ if (set) result.set = hoursLater(t, set);
4474+
4475+ if (!rise && !set) result[ye > 0 ? 'alwaysUp' : 'alwaysDown'] = true;
4476+
4477+ return result;
4478+};
4479+
4480+
4481+// export as AMD module / Node module / browser variable
4482+//if (typeof define === 'function' && define.amd) define(SunCalc);
4483+//else if (typeof module !== 'undefined') module.exports = SunCalc;
4484+//else window.SunCalc = SunCalc;
4485+
4486+//}());
4487
4488=== added directory 'app/graphics'
4489=== added file 'app/graphics/Big-Rain.png'
4490Binary files app/graphics/Big-Rain.png 1970-01-01 00:00:00 +0000 and app/graphics/Big-Rain.png 2015-08-05 00:17:22 +0000 differ
4491=== added file 'app/graphics/CMakeLists.txt'
4492--- app/graphics/CMakeLists.txt 1970-01-01 00:00:00 +0000
4493+++ app/graphics/CMakeLists.txt 2015-08-05 00:17:22 +0000
4494@@ -0,0 +1,5 @@
4495+file(GLOB IMAGE_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.png *.svg)
4496+
4497+add_custom_target(ubuntu-weather-app_graphics_IMAGEFiles ALL SOURCES ${IMAGE_FILES})
4498+
4499+install(FILES ${IMAGE_FILES} DESTINATION ${UBUNTU-WEATHER_APP_DIR}/graphics)
4500
4501=== added file 'app/graphics/Cloudy-Circles.png'
4502Binary files app/graphics/Cloudy-Circles.png 1970-01-01 00:00:00 +0000 and app/graphics/Cloudy-Circles.png 2015-08-05 00:17:22 +0000 differ
4503=== added file 'app/graphics/Cloudy-Night.png'
4504Binary files app/graphics/Cloudy-Night.png 1970-01-01 00:00:00 +0000 and app/graphics/Cloudy-Night.png 2015-08-05 00:17:22 +0000 differ
4505=== added file 'app/graphics/Cloudy-Snow.png'
4506Binary files app/graphics/Cloudy-Snow.png 1970-01-01 00:00:00 +0000 and app/graphics/Cloudy-Snow.png 2015-08-05 00:17:22 +0000 differ
4507=== added file 'app/graphics/Cloudy.png'
4508Binary files app/graphics/Cloudy.png 1970-01-01 00:00:00 +0000 and app/graphics/Cloudy.png 2015-08-05 00:17:22 +0000 differ
4509=== added file 'app/graphics/Fog.png'
4510Binary files app/graphics/Fog.png 1970-01-01 00:00:00 +0000 and app/graphics/Fog.png 2015-08-05 00:17:22 +0000 differ
4511=== added file 'app/graphics/Raindrop.png'
4512Binary files app/graphics/Raindrop.png 1970-01-01 00:00:00 +0000 and app/graphics/Raindrop.png 2015-08-05 00:17:22 +0000 differ
4513=== added file 'app/graphics/Showers.png'
4514Binary files app/graphics/Showers.png 1970-01-01 00:00:00 +0000 and app/graphics/Showers.png 2015-08-05 00:17:22 +0000 differ
4515=== added file 'app/graphics/Starry-Night.png'
4516Binary files app/graphics/Starry-Night.png 1970-01-01 00:00:00 +0000 and app/graphics/Starry-Night.png 2015-08-05 00:17:22 +0000 differ
4517=== added file 'app/graphics/Stormy.png'
4518Binary files app/graphics/Stormy.png 1970-01-01 00:00:00 +0000 and app/graphics/Stormy.png 2015-08-05 00:17:22 +0000 differ
4519=== added file 'app/graphics/Sunny.png'
4520Binary files app/graphics/Sunny.png 1970-01-01 00:00:00 +0000 and app/graphics/Sunny.png 2015-08-05 00:17:22 +0000 differ
4521=== added file 'app/graphics/Windy-n-Snow.png'
4522Binary files app/graphics/Windy-n-Snow.png 1970-01-01 00:00:00 +0000 and app/graphics/Windy-n-Snow.png 2015-08-05 00:17:22 +0000 differ
4523=== added file 'app/graphics/extended-information_chance-of-rain.svg'
4524--- app/graphics/extended-information_chance-of-rain.svg 1970-01-01 00:00:00 +0000
4525+++ app/graphics/extended-information_chance-of-rain.svg 2015-08-05 00:17:22 +0000
4526@@ -0,0 +1,153 @@
4527+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
4528+<!-- Created with Inkscape (http://www.inkscape.org/) -->
4529+
4530+<svg
4531+ xmlns:dc="http://purl.org/dc/elements/1.1/"
4532+ xmlns:cc="http://creativecommons.org/ns#"
4533+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
4534+ xmlns:svg="http://www.w3.org/2000/svg"
4535+ xmlns="http://www.w3.org/2000/svg"
4536+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
4537+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
4538+ width="90"
4539+ height="90"
4540+ id="svg6138"
4541+ version="1.1"
4542+ inkscape:version="0.91pre2 r"
4543+ viewBox="0 0 90 90.000001"
4544+ sodipodi:docname="weather-chance-of-rain02.svg">
4545+ <defs
4546+ id="defs6140" />
4547+ <sodipodi:namedview
4548+ id="base"
4549+ pagecolor="#ffffff"
4550+ bordercolor="#666666"
4551+ borderopacity="1.0"
4552+ inkscape:pageopacity="0.0"
4553+ inkscape:pageshadow="2"
4554+ inkscape:zoom="5.0931702"
4555+ inkscape:cx="112.1011"
4556+ inkscape:cy="19.378885"
4557+ inkscape:document-units="px"
4558+ inkscape:current-layer="g6442"
4559+ showgrid="true"
4560+ fit-margin-top="0"
4561+ fit-margin-left="0"
4562+ fit-margin-right="0"
4563+ fit-margin-bottom="0"
4564+ inkscape:snap-global="true"
4565+ inkscape:snap-bbox="true"
4566+ inkscape:bbox-paths="true"
4567+ inkscape:snap-bbox-midpoints="true"
4568+ inkscape:snap-bbox-edge-midpoints="true"
4569+ inkscape:bbox-nodes="true"
4570+ inkscape:object-paths="true"
4571+ inkscape:snap-intersection-paths="true"
4572+ inkscape:object-nodes="true"
4573+ inkscape:snap-smooth-nodes="true"
4574+ inkscape:snap-midpoints="true"
4575+ inkscape:snap-others="true"
4576+ inkscape:snap-object-midpoints="true"
4577+ inkscape:snap-center="true"
4578+ showguides="false"
4579+ inkscape:guide-bbox="true">
4580+ <inkscape:grid
4581+ type="xygrid"
4582+ id="grid6700"
4583+ empspacing="6" />
4584+ <sodipodi:guide
4585+ orientation="0,1"
4586+ position="90,87"
4587+ id="guide4064" />
4588+ <sodipodi:guide
4589+ orientation="0,1"
4590+ position="89,84"
4591+ id="guide4066" />
4592+ <sodipodi:guide
4593+ orientation="1,0"
4594+ position="3,69"
4595+ id="guide4068" />
4596+ <sodipodi:guide
4597+ orientation="1,0"
4598+ position="6,60"
4599+ id="guide4070" />
4600+ <sodipodi:guide
4601+ orientation="1,0"
4602+ position="87,58"
4603+ id="guide4072" />
4604+ <sodipodi:guide
4605+ orientation="1,0"
4606+ position="84,47"
4607+ id="guide4074" />
4608+ <sodipodi:guide
4609+ orientation="0,1"
4610+ position="77,3"
4611+ id="guide4076" />
4612+ <sodipodi:guide
4613+ orientation="0,1"
4614+ position="81,6"
4615+ id="guide4078" />
4616+ <sodipodi:guide
4617+ orientation="1,0"
4618+ position="81,25"
4619+ id="guide4080" />
4620+ <sodipodi:guide
4621+ orientation="0,1"
4622+ position="78,9"
4623+ id="guide4082" />
4624+ <sodipodi:guide
4625+ orientation="0,1"
4626+ position="79,81"
4627+ id="guide4084" />
4628+ <sodipodi:guide
4629+ orientation="1,0"
4630+ position="9,39"
4631+ id="guide4086" />
4632+ </sodipodi:namedview>
4633+ <metadata
4634+ id="metadata6143">
4635+ <rdf:RDF>
4636+ <cc:Work
4637+ rdf:about="">
4638+ <dc:format>image/svg+xml</dc:format>
4639+ <dc:type
4640+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
4641+ <dc:title></dc:title>
4642+ </cc:Work>
4643+ </rdf:RDF>
4644+ </metadata>
4645+ <g
4646+ inkscape:label="Layer 1"
4647+ inkscape:groupmode="layer"
4648+ id="layer1"
4649+ transform="translate(-283.57144,-358.79068)">
4650+ <g
4651+ transform="translate(169.57144,223.42822)"
4652+ id="g5937"
4653+ inkscape:export-filename="envelope02.png"
4654+ inkscape:export-xdpi="90"
4655+ inkscape:export-ydpi="90" />
4656+ <g
4657+ id="g6442"
4658+ transform="translate(-753.45981,336.4283)">
4659+ <rect
4660+ style="fill:none;stroke:none"
4661+ id="rect1687"
4662+ width="90"
4663+ height="90"
4664+ x="1037.0312"
4665+ y="22.362379" />
4666+ <path
4667+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:125%;font-family:Ubuntu;-inkscape-font-specification:Ubuntu;text-align:end;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;fill:#808080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
4668+ d="M 44.929688 12.066406 C 34.037288 12.066406 24.849688 14.943458 17.367188 20.697266 C 9.7896875 26.523909 6 33.624202 6 42 C 7.7011 40.603306 9.6530686 39.474739 11.855469 38.615234 C 14.075869 37.773637 16.4573 37.353516 19 37.353516 C 21.5427 37.353516 23.914587 37.773637 26.117188 38.615234 C 28.337486 39.474739 30.2989 40.603306 32 42 C 33.7011 40.603306 35.653069 39.474739 37.855469 38.615234 C 40.075769 37.773637 42.4573 37.353516 45 37.353516 C 47.5427 37.353516 49.914586 37.773637 52.117188 38.615234 C 54.337487 39.474739 56.2989 40.603306 58 42 L 58.654297 42 C 60.235137 40.784631 62.009869 39.776665 64 39 C 66.2203 38.158403 68.601831 37.738281 71.144531 37.738281 C 73.687231 37.738281 76.059119 38.158403 78.261719 39 C 80.268226 39.776743 82.052173 40.784465 83.634766 42 L 84 42 C 84 33.624202 80.210313 26.523909 72.632812 20.697266 C 65.055613 14.943458 55.822287 12.066406 44.929688 12.066406 z "
4669+ transform="translate(1037.0313,22.36238)"
4670+ id="path4181" />
4671+ <path
4672+ style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:125%;font-family:Ubuntu;-inkscape-font-specification:Ubuntu;text-indent:0;text-align:end;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:end;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:6.00000048;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
4673+ d="m 1079.0312,28.363281 0,66.335938 0.01,0.09766 c 0.015,0.226687 0.022,0.780446 0.022,1.509766 0,1.918939 -0.4328,2.763898 -1.0039,3.289062 l -0.01,0.0059 -0.01,0.0078 c -0.6357,0.592743 -1.5903,1.007813 -3.4297,1.007813 -1.8313,0 -2.8017,-0.41364 -3.461,-1.015626 -0.5541,-0.519673 -0.9882,-1.37472 -0.9882,-3.294921 0,-0.72932 0.01,-1.281931 0.021,-1.501953 l -5.9863,-0.410157 c -0.041,0.598238 -0.035,1.18279 -0.035,1.91211 0,2.983063 0.8627,5.791367 2.9003,7.691407 l 0.01,0.008 0.01,0.006 c 1.9716,1.8129 4.6659,2.60547 7.5332,2.60547 2.8642,0 5.5555,-0.79304 7.5136,-2.61524 2.0532,-1.89396 2.9356,-4.70919 2.9356,-7.695307 0,-0.72932 0.01,-1.312724 -0.033,-1.904297 l 0.01,0.197265 0,-67.236328 -6,1 z"
4674+ id="path4225"
4675+ inkscape:connector-curvature="0"
4676+ sodipodi:nodetypes="cccscccscsccscccscsccccc" />
4677+ </g>
4678+ </g>
4679+</svg>
4680
4681=== added file 'app/graphics/extended-information_humidity.svg'
4682--- app/graphics/extended-information_humidity.svg 1970-01-01 00:00:00 +0000
4683+++ app/graphics/extended-information_humidity.svg 2015-08-05 00:17:22 +0000
4684@@ -0,0 +1,147 @@
4685+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
4686+<!-- Created with Inkscape (http://www.inkscape.org/) -->
4687+
4688+<svg
4689+ xmlns:dc="http://purl.org/dc/elements/1.1/"
4690+ xmlns:cc="http://creativecommons.org/ns#"
4691+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
4692+ xmlns:svg="http://www.w3.org/2000/svg"
4693+ xmlns="http://www.w3.org/2000/svg"
4694+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
4695+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
4696+ width="90"
4697+ height="90"
4698+ id="svg6138"
4699+ version="1.1"
4700+ inkscape:version="0.91pre2 r"
4701+ viewBox="0 0 90 90.000001"
4702+ sodipodi:docname="weather-chance-of-rain.svg">
4703+ <defs
4704+ id="defs6140" />
4705+ <sodipodi:namedview
4706+ id="base"
4707+ pagecolor="#ffffff"
4708+ bordercolor="#666666"
4709+ borderopacity="1.0"
4710+ inkscape:pageopacity="0.0"
4711+ inkscape:pageshadow="2"
4712+ inkscape:zoom="4.0745362"
4713+ inkscape:cx="90.550182"
4714+ inkscape:cy="25.205316"
4715+ inkscape:document-units="px"
4716+ inkscape:current-layer="g6442"
4717+ showgrid="true"
4718+ fit-margin-top="0"
4719+ fit-margin-left="0"
4720+ fit-margin-right="0"
4721+ fit-margin-bottom="0"
4722+ inkscape:snap-global="false"
4723+ inkscape:snap-bbox="true"
4724+ inkscape:bbox-paths="true"
4725+ inkscape:snap-bbox-midpoints="true"
4726+ inkscape:snap-bbox-edge-midpoints="true"
4727+ inkscape:bbox-nodes="true"
4728+ inkscape:object-paths="true"
4729+ inkscape:snap-intersection-paths="true"
4730+ inkscape:object-nodes="true"
4731+ inkscape:snap-smooth-nodes="true"
4732+ inkscape:snap-midpoints="true"
4733+ inkscape:snap-others="true"
4734+ inkscape:snap-object-midpoints="true"
4735+ inkscape:snap-center="true"
4736+ showguides="false"
4737+ inkscape:guide-bbox="true">
4738+ <inkscape:grid
4739+ type="xygrid"
4740+ id="grid6700"
4741+ empspacing="6" />
4742+ <sodipodi:guide
4743+ orientation="0,1"
4744+ position="90,87"
4745+ id="guide4064" />
4746+ <sodipodi:guide
4747+ orientation="0,1"
4748+ position="89,84"
4749+ id="guide4066" />
4750+ <sodipodi:guide
4751+ orientation="1,0"
4752+ position="3,69"
4753+ id="guide4068" />
4754+ <sodipodi:guide
4755+ orientation="1,0"
4756+ position="6,60"
4757+ id="guide4070" />
4758+ <sodipodi:guide
4759+ orientation="1,0"
4760+ position="87,58"
4761+ id="guide4072" />
4762+ <sodipodi:guide
4763+ orientation="1,0"
4764+ position="84,47"
4765+ id="guide4074" />
4766+ <sodipodi:guide
4767+ orientation="0,1"
4768+ position="77,3"
4769+ id="guide4076" />
4770+ <sodipodi:guide
4771+ orientation="0,1"
4772+ position="81,6"
4773+ id="guide4078" />
4774+ <sodipodi:guide
4775+ orientation="1,0"
4776+ position="81,25"
4777+ id="guide4080" />
4778+ <sodipodi:guide
4779+ orientation="0,1"
4780+ position="78,9"
4781+ id="guide4082" />
4782+ <sodipodi:guide
4783+ orientation="0,1"
4784+ position="79,81"
4785+ id="guide4084" />
4786+ <sodipodi:guide
4787+ orientation="1,0"
4788+ position="9,39"
4789+ id="guide4086" />
4790+ </sodipodi:namedview>
4791+ <metadata
4792+ id="metadata6143">
4793+ <rdf:RDF>
4794+ <cc:Work
4795+ rdf:about="">
4796+ <dc:format>image/svg+xml</dc:format>
4797+ <dc:type
4798+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
4799+ <dc:title></dc:title>
4800+ </cc:Work>
4801+ </rdf:RDF>
4802+ </metadata>
4803+ <g
4804+ inkscape:label="Layer 1"
4805+ inkscape:groupmode="layer"
4806+ id="layer1"
4807+ transform="translate(-283.57144,-358.79068)">
4808+ <g
4809+ transform="translate(169.57144,223.42822)"
4810+ id="g5937"
4811+ inkscape:export-filename="envelope02.png"
4812+ inkscape:export-xdpi="90"
4813+ inkscape:export-ydpi="90" />
4814+ <g
4815+ id="g6442"
4816+ transform="translate(-753.45981,336.4283)">
4817+ <path
4818+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;enable-background:accumulate"
4819+ d="M 45.046875 3 C 32.915975 27.594868 24.552722 55.021065 23.982422 56.908203 C 23.901522 57.129789 23.841578 57.361086 23.767578 57.585938 C 23.036878 59.796557 22.623047 62.152027 22.623047 64.607422 C 22.623047 76.973464 32.652638 86.998047 45.023438 86.998047 C 57.372742 86.972595 67.376953 76.959723 67.376953 64.609375 C 67.376953 62.155598 66.975594 59.797236 66.246094 57.587891 C 66.246094 57.587891 58.008075 29.900931 45.046875 3.0390625 L 45.046875 3.03125 L 45.046875 3.0195312 L 45.046875 3.0058594 L 45.046875 3 z M 44.996094 17.728516 C 54.458094 39.650002 60.503906 59.296875 60.503906 59.296875 A 5.9907304 5.9907304 0 0 0 60.558594 59.466797 C 61.095294 61.092212 61.386719 62.80886 61.386719 64.609375 C 61.386719 73.725917 54.126659 80.989079 45.005859 81.005859 C 35.879459 80.996009 28.613281 73.728301 28.613281 64.607422 C 28.613281 62.825026 28.910878 61.113375 29.455078 59.466797 A 5.9907304 5.9907304 0 0 0 29.457031 59.458984 C 29.615031 58.978962 29.665675 58.806709 29.609375 58.960938 A 5.9907304 5.9907304 0 0 0 29.716797 58.638672 C 30.156997 57.182234 36.199294 37.969649 44.996094 17.728516 z "
4820+ transform="translate(1037.0313,22.36238)"
4821+ id="path4253" />
4822+ <rect
4823+ style="fill:none;stroke:none"
4824+ id="rect1687"
4825+ width="90"
4826+ height="90"
4827+ x="1037.0312"
4828+ y="22.362379" />
4829+ </g>
4830+ </g>
4831+</svg>
4832
4833=== added file 'app/graphics/extended-information_sunrise.svg'
4834--- app/graphics/extended-information_sunrise.svg 1970-01-01 00:00:00 +0000
4835+++ app/graphics/extended-information_sunrise.svg 2015-08-05 00:17:22 +0000
4836@@ -0,0 +1,88 @@
4837+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
4838+<!-- Created with Inkscape (http://www.inkscape.org/) -->
4839+
4840+<svg
4841+ xmlns:dc="http://purl.org/dc/elements/1.1/"
4842+ xmlns:cc="http://creativecommons.org/ns#"
4843+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
4844+ xmlns:svg="http://www.w3.org/2000/svg"
4845+ xmlns="http://www.w3.org/2000/svg"
4846+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
4847+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
4848+ width="90"
4849+ height="90"
4850+ id="svg3642"
4851+ version="1.1"
4852+ inkscape:version="0.91 r13725"
4853+ viewBox="0 0 90 90"
4854+ sodipodi:docname="sunrise.svg">
4855+ <defs
4856+ id="defs3644" />
4857+ <sodipodi:namedview
4858+ id="base"
4859+ pagecolor="#ffffff"
4860+ bordercolor="#666666"
4861+ borderopacity="1.0"
4862+ inkscape:pageopacity="0.0"
4863+ inkscape:pageshadow="2"
4864+ inkscape:zoom="5.5"
4865+ inkscape:cx="1.4545455"
4866+ inkscape:cy="32"
4867+ inkscape:current-layer="layer1"
4868+ showgrid="true"
4869+ inkscape:document-units="px"
4870+ inkscape:grid-bbox="true"
4871+ inkscape:window-width="1855"
4872+ inkscape:window-height="1056"
4873+ inkscape:window-x="65"
4874+ inkscape:window-y="24"
4875+ inkscape:window-maximized="1" />
4876+ <metadata
4877+ id="metadata3647">
4878+ <rdf:RDF>
4879+ <cc:Work
4880+ rdf:about="">
4881+ <dc:format>image/svg+xml</dc:format>
4882+ <dc:type
4883+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
4884+ <dc:title></dc:title>
4885+ </cc:Work>
4886+ </rdf:RDF>
4887+ </metadata>
4888+ <g
4889+ id="layer1"
4890+ inkscape:label="Layer 1"
4891+ inkscape:groupmode="layer"
4892+ transform="translate(0,26)">
4893+ <path
4894+ style="fill:#808080;fill-opacity:1"
4895+ d="m 26.78212,5.9887068 -5.382513,-9.3529353 2.093211,-1.1925985 c 1.151271,-0.65592 2.107413,-1.174671 2.124765,-1.152765 0.313155,0.395325 10.541061,18.372014 10.541061,18.527102 0,0.121167 -0.536958,0.524286 -1.193238,0.895824 -0.656289,0.371547 -1.554939,0.889911 -1.99701,1.151919 L 32.164633,15.341641 26.78212,5.9887068 Z"
4896+ id="path3437"
4897+ inkscape:connector-curvature="0" />
4898+ <path
4899+ style="fill:#808080;fill-opacity:1"
4900+ d="m 42.749893,0.3532757 0,-10.6823697 2.386485,0 2.386485,0 0,10.6823697 0,10.6823603 -2.386485,0 -2.386485,0 0,-10.6823603 z"
4901+ id="path3439"
4902+ inkscape:connector-curvature="0" />
4903+ <path
4904+ style="fill:#808080;fill-opacity:1"
4905+ d="M 56.046025,14.195662 C 55.045972,13.59223 54.130753,13.062283 54.012196,13.018003 53.848144,12.956713 61.28128,-0.2748253 64.150641,-5.02921 l 0.436437,-0.723159 1.979793,1.141956 c 1.088883,0.6280825 2.017233,1.1784505 2.062989,1.2230456 0.08784,0.08559 -10.018844,17.7534264 -10.490561,18.3388854 -0.237942,0.295308 -0.520002,0.193464 -2.093274,-0.755856 z"
4906+ id="path3441"
4907+ inkscape:connector-curvature="0" />
4908+ <path
4909+ style="fill:#808080;fill-opacity:1"
4910+ d="M 63.87093,22.143905 C 63.219591,21.005711 62.784261,20.007647 62.893962,19.904048 63.177039,19.63673 81.221725,9.1995467 81.276697,9.2713397 c 0.282537,0.369027 2.389788,4.1043953 2.339028,4.1462363 -0.03753,0.03096 -3.392335,1.976715 -7.455034,4.323898 -4.062708,2.347191 -8.225586,4.759875 -9.250839,5.361516 l -1.864089,1.093905 -1.174833,-2.05299 z"
4911+ id="path3445"
4912+ inkscape:connector-curvature="0" />
4913+ <path
4914+ style="fill:#808080;fill-opacity:1"
4915+ d="m 1.8387093,32.887313 0,-2.53251 13.6075957,0 13.607586,0 0.620559,-1.761453 c 2.147886,-6.096807 5.892831,-9.823986 11.166354,-11.11338 2.201751,-0.538335 6.756111,-0.534681 8.727615,0.0072 5.170455,1.420614 9.12609,5.644305 11.040102,11.788227 l 0.407133,1.306881 13.481909,0 13.481911,0 0,2.272842 0,2.272842 -41.673339,0 c -22.920327,0 -42.3019986,0.0657 -43.0703737,0.146025 l -1.397052,0.146016 0,-2.532501 z M 55.616266,29.616128 c -0.413046,-2.094669 -2.175516,-4.507155 -4.292244,-5.875263 -4.577967,-2.958876 -9.971064,-2.49669 -13.914819,1.192491 -1.555938,1.455507 -2.841543,3.616272 -2.841543,4.775895 l 0,0.645552 10.597131,0 10.597131,0 -0.145656,-0.738675 z"
4916+ id="path3447"
4917+ inkscape:connector-curvature="0" />
4918+ <path
4919+ style="fill:#808080;fill-opacity:1"
4920+ d="m 15.922288,18.850454 c -5.058325,-2.928024 -9.2259277,-5.40856 -9.2613247,-5.512285 -0.03537,-0.103734 0.459243,-1.078245 1.099215,-2.16558 l 1.163583,-1.9769753 0.719055,0.424107 C 10.038303,9.8529737 14.146156,12.22684 18.7714,14.89498 c 4.625235,2.668132 8.483391,4.91356 8.573679,4.989826 0.09029,0.07623 -0.37359,1.072584 -1.030833,2.214009 l -1.194993,2.075328 -9.196965,-5.323689 z"
4921+ id="path3449"
4922+ inkscape:connector-curvature="0" />
4923+ </g>
4924+</svg>
4925
4926=== added file 'app/graphics/extended-information_sunset.svg'
4927--- app/graphics/extended-information_sunset.svg 1970-01-01 00:00:00 +0000
4928+++ app/graphics/extended-information_sunset.svg 2015-08-05 00:17:22 +0000
4929@@ -0,0 +1,88 @@
4930+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
4931+<!-- Created with Inkscape (http://www.inkscape.org/) -->
4932+
4933+<svg
4934+ xmlns:dc="http://purl.org/dc/elements/1.1/"
4935+ xmlns:cc="http://creativecommons.org/ns#"
4936+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
4937+ xmlns:svg="http://www.w3.org/2000/svg"
4938+ xmlns="http://www.w3.org/2000/svg"
4939+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
4940+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
4941+ width="90"
4942+ height="90"
4943+ id="svg3642"
4944+ version="1.1"
4945+ inkscape:version="0.91 r13725"
4946+ viewBox="0 0 90 90"
4947+ sodipodi:docname="sunset.svg">
4948+ <defs
4949+ id="defs3644" />
4950+ <sodipodi:namedview
4951+ id="base"
4952+ pagecolor="#ffffff"
4953+ bordercolor="#666666"
4954+ borderopacity="1.0"
4955+ inkscape:pageopacity="0.0"
4956+ inkscape:pageshadow="2"
4957+ inkscape:zoom="5.5"
4958+ inkscape:cx="1.4545455"
4959+ inkscape:cy="32"
4960+ inkscape:current-layer="layer1"
4961+ showgrid="true"
4962+ inkscape:document-units="px"
4963+ inkscape:grid-bbox="true"
4964+ inkscape:window-width="1855"
4965+ inkscape:window-height="1056"
4966+ inkscape:window-x="65"
4967+ inkscape:window-y="24"
4968+ inkscape:window-maximized="1" />
4969+ <metadata
4970+ id="metadata3647">
4971+ <rdf:RDF>
4972+ <cc:Work
4973+ rdf:about="">
4974+ <dc:format>image/svg+xml</dc:format>
4975+ <dc:type
4976+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
4977+ <dc:title></dc:title>
4978+ </cc:Work>
4979+ </rdf:RDF>
4980+ </metadata>
4981+ <g
4982+ id="layer1"
4983+ inkscape:label="Layer 1"
4984+ inkscape:groupmode="layer"
4985+ transform="translate(0,26)">
4986+ <path
4987+ style="fill:#808080;fill-opacity:1"
4988+ d="m 63.036063,25.102202 5.382513,9.352936 -2.093211,1.192598 c -1.151271,0.65592 -2.107413,1.174671 -2.124765,1.152765 -0.313155,-0.395325 -10.541061,-18.372014 -10.541061,-18.527102 0,-0.121167 0.536958,-0.524286 1.193238,-0.895824 0.656289,-0.371547 1.554939,-0.889911 1.99701,-1.151919 l 0.803763,-0.476388 5.382513,9.352934 z"
4989+ id="path3437"
4990+ inkscape:connector-curvature="0" />
4991+ <path
4992+ style="fill:#808080;fill-opacity:1"
4993+ d="m 47.06829,30.737633 0,10.68237 -2.386485,0 -2.386485,0 0,-10.68237 0,-10.68236 2.386485,0 2.386485,0 0,10.68236 z"
4994+ id="path3439"
4995+ inkscape:connector-curvature="0" />
4996+ <path
4997+ style="fill:#808080;fill-opacity:1"
4998+ d="m 33.772158,16.895247 c 1.000053,0.603432 1.915272,1.133379 2.033829,1.177659 0.164052,0.06129 -7.269084,13.292828 -10.138445,18.047213 l -0.436437,0.723159 -1.979793,-1.141956 c -1.088883,-0.628083 -2.017233,-1.178451 -2.062989,-1.223046 -0.08784,-0.08559 10.018844,-17.753426 10.490561,-18.338885 0.237942,-0.295308 0.520002,-0.193464 2.093274,0.755856 z"
4999+ id="path3441"
5000+ inkscape:connector-curvature="0" />
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches