summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--AUTHORS3
-rw-r--r--COPYING19
-rw-r--r--ChangeLog0
-rw-r--r--Makefile.am37
-rw-r--r--NEWS2
-rw-r--r--NEWS.xml6
-rw-r--r--NEWS.xsl24
-rw-r--r--README0
-rwxr-xr-xautogen.sh7
-rw-r--r--chrome.manifest5
-rw-r--r--configure.ac28
-rw-r--r--content/Makefile.am22
-rw-r--r--content/about.xul45
-rw-r--r--content/autoconf.dtd.in3
-rw-r--r--content/background.jpegbin0 -> 880 bytes
-rw-r--r--content/browser.xul19
-rw-r--r--content/copying.xul14
-rw-r--r--content/fireflix-panel.xul252
-rw-r--r--content/fireflix.css90
-rw-r--r--content/fireflix.js878
-rw-r--r--content/flickr.js403
-rw-r--r--content/generated-content.js17
-rw-r--r--content/generated-content.xul20
-rw-r--r--content/icons/16x16/fireflix.pngbin0 -> 926 bytes
-rw-r--r--content/icons/32x32/fireflix.pngbin0 -> 2017 bytes
-rw-r--r--content/md5.js154
-rw-r--r--content/photoset-props.js81
-rw-r--r--content/photoset-props.xul38
-rw-r--r--install.rdf.in28
-rw-r--r--locale/Makefile.am10
-rw-r--r--locale/en-US/fireflix.dtd79
-rw-r--r--locale/en-US/fireflix.properties11
-rw-r--r--update.rdf.in26
33 files changed, 2321 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..a9fb0c7
--- a/dev/null
+++ b/AUTHORS
@@ -0,0 +1,3 @@
1Klever dissected:
2 Michael 'hacker' Krelin <hacker@klever.net>
3 Leonid Ivanov <kamel@klever.net>
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..9a21651
--- a/dev/null
+++ b/COPYING
@@ -0,0 +1,19 @@
1Copyright (c) 2006 Klever Group (http://www.klever.net/)
2
3Permission is hereby granted, free of charge, to any person obtaining a copy of
4this software and associated documentation files (the "Software"), to deal in
5the Software without restriction, including without limitation the rights to
6use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7of the Software, and to permit persons to whom the Software is furnished to do
8so, subject to the following conditions:
9
10The above copyright notice and this permission notice shall be included in all
11copies or substantial portions of the Software.
12
13THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19SOFTWARE.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..e69de29
--- a/dev/null
+++ b/ChangeLog
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..913b46a
--- a/dev/null
+++ b/Makefile.am
@@ -0,0 +1,37 @@
1SUBDIRS = content locale
2
3xpi_DATA = \
4 install.rdf \
5 AUTHORS COPYING NEWS
6
7EXTRA_DIST = NEWS NEWS.xml NEWS.xsl chrome.manifest
8
9XPI=${PACKAGE}-${VERSION}.xpi
10
11xpi: ${XPI}
12
13${XPI}: install
14 cd ${xpichromedir} \
15 && ${ZIP} -r -m ${PACKAGE}.jar */
16 cd ${xpidir} \
17 && ${ZIP} -r @abs_builddir@/$@ .
18
19install-data-local: ${xpidir}/chrome.manifest
20
21${xpidir}/chrome.manifest: chrome.manifest Makefile
22 sed \
23 -e 's,^content[[:space:]]\+\([^[:space:]]\+\)[[:space:]]\+\([^[:space:]]\+\)$$,content \1 jar:chrome/${PACKAGE}.jar!/\2,' \
24 -e 's,^locale[[:space:]]\+\([^[:space:]]\+\)[[:space:]]\+\([^[:space:]]\+\)[[:space:]]\+\([^[:space:]]\+\)$$,locale \1 \2 jar:chrome/${PACKAGE}.jar!/\3,' \
25 $< >$@
26
27clean-local:
28 rm -rf ${xpidir} ${XPI}
29
30all-local: NEWS
31
32NEWS: NEWS.xsl NEWS.xml
33 ${XSLTPROC} -o $@ NEWS.xsl NEWS.xml
34
35mozextptr: ${MOZ_EXT_ID}
36${MOZ_EXT_ID}:
37 echo @abs_srcdir@ >$@
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..d669517
--- a/dev/null
+++ b/NEWS
@@ -0,0 +1,2 @@
10.0 (September 26th, 2006)
2 - Initial release
diff --git a/NEWS.xml b/NEWS.xml
new file mode 100644
index 0000000..ed82c8a
--- a/dev/null
+++ b/NEWS.xml
@@ -0,0 +1,6 @@
1<?xml version="1.0" encoding="us-ascii"?>
2<news>
3 <version version="0.0" date="September 26th, 2006">
4 <ni>Initial release</ni>
5 </version>
6</news>
diff --git a/NEWS.xsl b/NEWS.xsl
new file mode 100644
index 0000000..7c71307
--- a/dev/null
+++ b/NEWS.xsl
@@ -0,0 +1,24 @@
1<?xml version="1.0" encoding="us-ascii"?>
2<xsl:stylesheet version="1.0"
3 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
4 >
5 <xsl:output
6 method="text"
7 encoding="us-ascii"
8 media-type="text/plain" />
9
10 <xsl:template match="news">
11 <xsl:apply-templates/>
12 </xsl:template>
13 <xsl:template match="version">
14 <xsl:value-of select="concat(@version,' (',@date,')&#xA;')"/>
15 <xsl:apply-templates/>
16 </xsl:template>
17 <xsl:template match="ni">
18 <xsl:text> - </xsl:text>
19 <xsl:apply-templates mode="text"/>
20 <xsl:text>&#xA;</xsl:text>
21 </xsl:template>
22 <xsl:template match="*|text()"/>
23
24</xsl:stylesheet>
diff --git a/README b/README
new file mode 100644
index 0000000..e69de29
--- a/dev/null
+++ b/README
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..1a4b02a
--- a/dev/null
+++ b/autogen.sh
@@ -0,0 +1,7 @@
1#!/bin/sh
2WANT_AUTOMAKE=1.8
3export WANT_AUTOMAKE
4aclocal \
5&& automake -a \
6&& autoconf \
7&& ./configure "$@"
diff --git a/chrome.manifest b/chrome.manifest
new file mode 100644
index 0000000..3792a2f
--- a/dev/null
+++ b/chrome.manifest
@@ -0,0 +1,5 @@
1content fireflix content/
2
3overlay chrome://browser/content/browser.xul chrome://fireflix/content/browser.xul
4
5locale fireflix en-US locale/en-US/
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..b76ae0f
--- a/dev/null
+++ b/configure.ac
@@ -0,0 +1,28 @@
1AC_INIT([fireflix], [0.0], [fireflix-bugs@klever.net])
2AC_CONFIG_SRCDIR([install.rdf.in])
3AM_INIT_AUTOMAKE([dist-bzip2])
4AC_SUBST([MOZ_EXT_ID],[{4269f719-86de-4668-b8ad-04752c23a69e}])
5
6AC_PATH_PROG([ZIP],[zip],[false])
7if test "${ZIP}" = "false" ; then
8 AC_MSG_ERROR([zip is required to produce packaged extension])
9fi
10AC_PATH_PROG([XSLTPROC],[xsltproc],[true])
11
12AC_SUBST([xpidir],[\${top_builddir}/xpi])
13AC_SUBST([xpichromedir],[\${xpidir}/chrome])
14AC_SUBST([xpichromecontentdir],[\${xpichromedir}/content])
15AC_SUBST([xpichromelocaledir],[\${xpichromedir}/locale])
16
17COPYING="`sed -e 's/\\"/\\&quot;/g' -e 's,$,<br/>,g' COPYING|tr '\n' ' '`"
18AC_SUBST([COPYING])
19
20AC_CONFIG_FILES([
21 Makefile
22 install.rdf
23 update.rdf
24 content/Makefile
25 content/autoconf.dtd
26 locale/Makefile
27])
28AC_OUTPUT
diff --git a/content/Makefile.am b/content/Makefile.am
new file mode 100644
index 0000000..8548400
--- a/dev/null
+++ b/content/Makefile.am
@@ -0,0 +1,22 @@
1xpichromecontent_DATA = \
2 autoconf.dtd \
3 browser.xul about.xul fireflix-panel.xul photoset-props.xul \
4 generated-content.xul \
5 photoset-props.js fireflix.js flickr.js md5.js \
6 generated-content.js \
7 fireflix.css \
8 background.jpeg
9
10sized_icons = \
11 $(addsuffix .png, \
12 fireflix \
13 )
14
15nobase_xpichromecontent_DATA = \
16 $(addprefix icons/, \
17 $(addprefix 16x16/,${sized_icons}) \
18 $(addprefix 32x32/,${sized_icons}) \
19 )
20
21EXTRA_DIST = \
22 ${xpichromecontent_DATA} ${nobase_xpichromecontent_DATA}
diff --git a/content/about.xul b/content/about.xul
new file mode 100644
index 0000000..1cb13d4
--- a/dev/null
+++ b/content/about.xul
@@ -0,0 +1,45 @@
1<?xml version="1.0"?>
2<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
3<?xml-stylesheet href="fireflix.css" type="text/css"?>
4<!DOCTYPE window SYSTEM "chrome://fireflix/locale/fireflix.dtd">
5<window
6 xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
7 id="aboutFireflix" title="&aboutFireflix;"
8 orient="vertical"
9 width="400" height="200"
10 >
11
12 <hbox class="about wholething" flex="1">
13 <hbox flex="1" class="insides">
14 <vbox>
15 <spacer flex="1"/>
16 <image src="icons/32x32/fireflix.png" width="32" height="32" />
17 <spacer flex="1"/>
18 </vbox>
19 <groupbox flex="1" class="text" orient="vertical">
20 <spacer flex="2"/>
21 <hbox>
22 <spacer flex="1"/>
23 <label value="Fireflix &autoconf.version;" class="title"/>
24 <spacer flex="1"/>
25 </hbox>
26 <spacer flex="1"/>
27 <hbox>
28 <label value="Copyright © 2006 "/>
29 <label value="Klever Group" class="link"
30 onclick="window.openDialog('chrome://browser/content/browser.xul','_blank','chrome,all,dialog=no','http://www.klever.net/',null,null)" />
31 </hbox>
32 <spacer flex="2"/>
33 </groupbox>
34 <vbox>
35 <spacer flex="10" />
36 <button label="&about.license.label;" tooltiptext="&about.license.tip;"
37 oncommand="window.openDialog('chrome://fireflix/content/copying.xul','copying')" />
38 <spacer flex="2" />
39 <button label="OK" oncommand="window.close()"/>
40 <spacer flex="1" />
41 </vbox>
42 </hbox>
43 </hbox>
44
45</window>
diff --git a/content/autoconf.dtd.in b/content/autoconf.dtd.in
new file mode 100644
index 0000000..e4ad217
--- a/dev/null
+++ b/content/autoconf.dtd.in
@@ -0,0 +1,3 @@
1<!ENTITY autoconf.version "@VERSION@">
2<!ENTITY autoconf.package "@PACKAGE@">
3<!ENTITY autoconf.copying "@COPYING@">
diff --git a/content/background.jpeg b/content/background.jpeg
new file mode 100644
index 0000000..43684f7
--- a/dev/null
+++ b/content/background.jpeg
Binary files differ
diff --git a/content/browser.xul b/content/browser.xul
new file mode 100644
index 0000000..aaff443
--- a/dev/null
+++ b/content/browser.xul
@@ -0,0 +1,19 @@
1<?xml version="1.0"?>
2<!DOCTYPE overlay SYSTEM "chrome://fireflix/locale/fireflix.dtd">
3<overlay id="fireflixOverlay"
4 xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" >
5
6 <menupopup id="viewSidebarMenu">
7 <menuitem observes="viewFireflixSidebar" />
8 </menupopup>
9
10 <broadcasterset id="mainBroadcasterSet">
11 <broadcaster id="viewFireflixSidebar"
12 autoCheck="false" label="&browser.sidebar.label;"
13 type="checkbox" group="sidebar"
14 sidebarurl="chrome://fireflix/content/fireflix-panel.xul"
15 sidebartitle="&browser.sidebar.title;"
16 oncommand="toggleSidebar('viewFireflixSidebar')" />
17 </broadcasterset>
18</overlay>
19
diff --git a/content/copying.xul b/content/copying.xul
new file mode 100644
index 0000000..179dca5
--- a/dev/null
+++ b/content/copying.xul
@@ -0,0 +1,14 @@
1<?xml version="1.0"?>
2<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
3<?xml-stylesheet href="fireflix.css" type="text/css"?>
4<!DOCTYPE dialog SYSTEM "chrome://fireflix/locale/fireflix.dtd">
5<dialog
6 xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
7 id="copying" title="&copying.title;" orient="vertical"
8 buttons="accept">
9
10 <div xmlns="http://www.w3.org/1999/xhtml">
11 &autoconf.copying;
12 </div>
13
14</dialog>
diff --git a/content/fireflix-panel.xul b/content/fireflix-panel.xul
new file mode 100644
index 0000000..9953761
--- a/dev/null
+++ b/content/fireflix-panel.xul
@@ -0,0 +1,252 @@
1<?xml version="1.0"?>
2<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
3<?xml-stylesheet href="fireflix.css" type="text/css"?>
4<!DOCTYPE page SYSTEM "chrome://fireflix/locale/fireflix.dtd">
5<page
6 xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
7 id="fireflixwindow" title="Fireflix"
8 onload="fireflix.init()"
9 orient="vertical"
10 ondragover="nsDragAndDrop.dragOver(event,fireflix.uploadObserver)"
11 ondragdrop="nsDragAndDrop.drop(event,fireflix.uploadObserver)"
12 >
13
14 <script src="chrome://global/content/nsDragAndDrop.js"/>
15 <script src="chrome://global/content/nsTransferable.js"/>
16 <script type="application/x-javascript" src="md5.js" />
17 <script type="application/x-javascript" src="flickr.js" />
18 <script type="application/x-javascript" src="fireflix.js" />
19
20 <stringbundleset>
21 <stringbundle id="loc_strings" src="chrome://fireflix/locale/fireflix.properties" />
22 </stringbundleset>
23
24 <commandset id="cmdset_search">
25 <command id="cmd_search" label="&panel.search.cmd_search.label;"
26 oncommand="fireflix.foundphotos.search_photos()"/>
27 <command id="cmd_search_open" label="&panel.search.cmd_search_open.label;"
28 oncommand="fireflix.foundphotos.on_cmd_open(event)" />
29 </commandset>
30
31 <commandset id="cmdset_sets">
32 <command id="cmd_refresh_sets" label="&panel.sets.cmd_refresh_sets;"
33 oncommand="fireflix.on_refresh_sets()" />
34 <command id="cmd_set_props" label="&panel.sets.cmd_properties;"
35 oncommand="fireflix.on_set_props()" disabled="true" />
36 </commandset>
37
38 <popupset>
39 <popup id="sets_menu">
40 <menuitem command="cmd_set_props"/>
41 <menuitem command="cmd_refresh_sets"/>
42 <menuseparator/>
43 <menu label="&panel.sets.generate_html;" id="sets_html_menu"/>
44 </popup>
45 </popupset>
46
47 <commandset id="cmdset_uploads">
48 <command id="cmd_uploads_clear" label="&panel.uploads.clear.label;"
49 oncommand="fireflix.uploads.on_clear()" />
50 <command id="cmd_uploads_upload" label="&panel.uploads.upload.label;"
51 oncommand="fireflix.uploads.on_upload()" />
52 <command id="cmd_uploads_remove" label="&panel.uploads.remove.label;"
53 oncommand="fireflix.uploads.on_remove()" />
54 <command id="cmd_uploads_add" label="&panel.uploads.add.label;"
55 oncommand="fireflix.uploads.on_add()" />
56 </commandset>
57
58 <popupset>
59 <popup id="uploads_menu">
60 <menuitem command="cmd_uploads_add"/>
61 <menuitem command="cmd_uploads_clear"/>
62 <menuitem command="cmd_uploads_remove"/>
63 <menuseparator/>
64 <menuitem command="cmd_uploads_upload"/>
65 <menuseparator/>
66 <menu label="&panel.uploads.generate_html;" id="uploads_html_menu"/>
67 </popup>
68 </popupset>
69
70 <vbox class="wholething" flex="1">
71
72 <groupbox>
73 <caption label="&panel.auth_info;"/>
74 <hbox>
75 <label id="auth_info" value="&panel.no_auth_info;" flex="1" disabled="true"/>
76 <button id="b_auth" label="&panel.auth_button;" oncommand="fireflix.on_auth()"/>
77 <button id="b_auth_done" label="&panel.auth_complete_button;" hidden="true"
78 oncommand="fireflix.on_auth_done()"/>
79 <button label="&panel.flickr_button.label;"
80 tooltiptext="&panel.flickr_button.tip;"
81 oncommand="fireflix.openTab('http://www.flickr.com/')" />
82 </hbox>
83 </groupbox>
84
85 <tabbox flex="1" id="fireflix_tabs">
86
87 <tabs>
88 <tab label="&panel.tabs.search;"/>
89 <tab label="&panel.tabs.sets;"/>
90 <tab label="&panel.tabs.tags;" hidden="true"/> <!-- TODO: -->
91 <tab id="tab_upload" label="&panel.tabs.upload;"/>
92 </tabs>
93
94 <tabpanels flex="1">
95
96 <tabpanel id="tabpanel_search" flex="1">
97 <vbox flex="1">
98 <groupbox class="search_params" orient="vertical" onkeypress="if(event.keyCode==event.DOM_VK_RETURN) fireflix.foundphotos.search_photos()">
99 <hbox>
100 <label control="search_for" value="&panel.search.search_for.label;"
101 accesskey="s"/>
102 <textbox id="search_for" flex="1"/>
103 </hbox>
104 <hbox>
105 <checkbox id="search_tags" label="&panel.search.mode.tagsonly.label;"
106 tooltiptext="&panel.search.mode.tagsonly.tip;" checked="false"
107 accesskey="t" />
108 <checkbox id="search_mine" label="&panel.search.mode.mine.label;" checked="true" accesskey="m"/>
109 <spacer flex="1"/>
110 <button command="cmd_search"/>
111 </hbox>
112 </groupbox>
113 <tree id="searchresults" rows="4" flex="1"
114 onselect="fireflix.foundphotos.on_select()"
115 ondblclick="fireflix.foundphotos.on_cmd_open(event)"
116 onkeypress="if(event.keyCode==event.DOM_VK_RETURN)
117 fireflix.foundphotos.on_cmd_open(event)">
118 <treecols>
119 <treecol id="sr_title" label="&panel.search.col.title.label;" flex="2" crop="end" align="start" />
120 </treecols>
121 <treechildren/>
122 </tree>
123 <groupbox id="searchresult_props" orient="horizontal" hidden="true">
124 <vbox width="100" pack="center">
125 <hbox pack="center">
126 <image id="search_photo"/>
127 </hbox>
128 </vbox>
129 <vbox flex="1">
130 <label id="searchresult_title"/>
131 <textbox flex="1" multiline="true" class="plain" readonly="true" id="searchresult_description"/>
132 <hbox pack="end">
133 <button command="cmd_search_open"/>
134 </hbox>
135 </vbox>
136 </groupbox>
137 </vbox>
138 </tabpanel>
139
140 <tabpanel id="tabpanel_sets" flex="1"
141 onkeypress="if(event.keyCode==event.DOM_VK_RETURN)
142 document.getElementById('setphotos').focus()">
143 <vbox flex="1">
144 <tree id="setslist" rows="4" onselect="fireflix.photosets.on_select()"
145 flex="1" context="sets_menu"
146 >
147 <treecols>
148 <treecol id="sl_name" label="&panel.sets.name.label;" flex="4" crop="end" align="start" tooltiptext="&panel.sets.name.tip;"/>
149 <splitter class="tree-splitter" />
150 <treecol id="sl_photos" label="&panel.sets.photos.label;" flex="1" align="end" tooltiptext="&panel.sets.photos.tip;" />
151 </treecols>
152 <treechildren/>
153 </tree>
154 <hbox>
155 <button command="cmd_refresh_sets" />
156 <button command="cmd_set_props" />
157 </hbox>
158 <tree id="setphotos" rows="4" onselect="fireflix.photoset.on_select()"
159 flex="1">
160 <treecols>
161 <treecol id="sp_title" label="&panel.setphotos.title.label;" flex="1" crop="end" align="start" tooltiptext="&panel.setphotos.title.tip;" />
162 <splitter class="tree-splitter" />
163 <treecol id="sp_taken" label="&panel.setphotos.taken.label;" crop="end" align="start" tooltiptext="&panel.setphotos.taken.tip;" hidden="true" />
164 <treecol id="sp_upload" label="&panel.setphotos.upload.label;" crop="end" align="start" tooltiptext="&panel.setphotos.upload.tip;" hidden="true" />
165 </treecols>
166 <treechildren/>
167 </tree>
168 <groupbox id="set_photo_props" orient="horizontal">
169 <vbox width="100" pack="center">
170 <hbox pack="center">
171 <image id="set_photo" hidden="true"/>
172 </hbox>
173 </vbox>
174 <spacer flex="1"/>
175 </groupbox>
176 </vbox>
177 </tabpanel>
178
179 <tabpanel id="tabpanel_tags">
180 <listbox id="tagslist" rows="8" flex="1">
181 <listhead>
182 <listheader label="&panel.tagslist.tag.label;"/>
183 </listhead>
184 <listcols>
185 <listcol flex="1"/>
186 </listcols>
187 </listbox>
188 </tabpanel>
189
190 <tabpanel id="tabpanel_upload">
191 <vbox flex="1">
192 <tree id="uploadlist" rows="8" flex="1"
193 onselect="fireflix.uploads.selectionChanged()"
194 context="uploads_menu">
195 <treecols>
196 <treecol id="up_file" label="&panel.uploadlist.file.label;" flex="4" crop="start" align="start"/>
197 <splitter class="tree-splitter" />
198 <treecol id="up_title" label="&panel.uploadlist.title.label;" flex="5" crop="end" align="start" />
199 <splitter class="tree-splitter" />
200 <treecol id="up_status" label="&panel.uploadlist.status.label;" flex="1" crop="end" align="start" />
201 </treecols>
202 <treechildren/>
203 </tree>
204 <progressmeter id="upload_progress" mode="undetermined" hidden="true" />
205 <groupbox id="upload_file_props" orient="horizontal" hidden="true">
206 <image id="upload_file_preview" width="100" height="100" />
207 <grid flex="1">
208 <columns>
209 <column/>
210 <column flex="1"/>
211 </columns>
212 <rows>
213 <row>
214 <label control="upload_filename"
215 value="&panel.upload_props.filename.label;" />
216 <textbox id="upload_filename"
217 oninput="fireflix.uploads.propsToSel('filename')"/>
218 </row>
219 <row>
220 <label control="upload_title" value="&panel.upload_props.title.label;" />
221 <textbox id="upload_title"
222 oninput="fireflix.uploads.propsToSel('title')"/>
223 </row>
224 <row>
225 <label control="uplod_tags" value="&panel.upload_props.tags.label;" />
226 <textbox id="upload_tags"
227 oninput="fireflix.uploads.propsToSel('tags')"/>
228 </row>
229 <!-- TODO: description, public, friend, family -->
230 </rows>
231 </grid>
232 </groupbox>
233 <hbox>
234 <button command="cmd_uploads_add" />
235 <spacer flex="1"/>
236 <button command="cmd_uploads_remove" />
237 <spacer flex="1"/>
238 <button command="cmd_uploads_clear" />
239 </hbox>
240 <hbox pack="center">
241 <button command="cmd_uploads_upload" flex="1"/>
242 </hbox>
243 </vbox>
244 </tabpanel>
245
246 </tabpanels>
247
248 </tabbox>
249
250 </vbox>
251
252</page>
diff --git a/content/fireflix.css b/content/fireflix.css
new file mode 100644
index 0000000..188f48e
--- a/dev/null
+++ b/content/fireflix.css
@@ -0,0 +1,90 @@
1.generated.wholething,
2.about.wholething,
3tabbox, tabpanels, tabpanel {
4 background: url("background.jpeg");
5}
6tabpanels {
7 padding: 0px;
8}
9
10tree {
11 margin-top: 2px;
12 background: rgb(12,167,0);
13 color: rgb(255,255,0);
14 font-size: 90%;
15}
16tree treechildren { /* for windows */
17 background: rgb(12,167,0);
18}
19
20tree#uploadlist treechildren::-moz-tree-cell-text(pending) {
21}
22tree#uploadlist treechildren::-moz-tree-cell-text(completed) {
23 color: white;
24}
25tree#uploadlist treechildren::-moz-tree-row(failed) {
26 background: yellow;
27}
28tree#uploadlist treechildren::-moz-tree-cell-text(failed) {
29 color: red;
30}
31tree#uploadlist treechildren::-moz-tree-cell-text(uploading) {
32 font-weight: bold;
33}
34
35groupbox#searchresult_props,
36groupbox.search_params,
37groupbox#upload_file_props,
38groupbox#set_props,
39groupbox#set_photo_props {
40 background: white;
41}
42
43groupbox#upload_file_props label {
44 text-align: right;
45}
46
47image#set_photo, image#set_primary {
48 border: black 1px solid;
49}
50
51.about .insides {
52 margin: 1ex;
53}
54.about .text {
55 border: yellow solid 1px;
56 background: green;
57}
58.about .title {
59 font-size: 300%;
60 font-weight: bold;
61 color: yellow;
62}
63.about .link {
64 text-decoration: underline;
65 color: white;
66 cursor: pointer;
67}
68
69menuitem.menuhead {
70 background: gray;
71 color: black;
72 font-weight: bold;
73}
74
75label#searchresult_description {
76 font-weight: bold;
77}
78textbox#searchresult_description {
79 padding: 1px 3px !important;
80 background: white;
81}
82
83#copying div {
84 margin: 1ex 1em;
85 font-family: courier, monospace;
86 font-size: 9pt;
87 padding: 2px;
88 border: dotted 1px gray;
89 background: white;
90}
diff --git a/content/fireflix.js b/content/fireflix.js
new file mode 100644
index 0000000..9518480
--- a/dev/null
+++ b/content/fireflix.js
@@ -0,0 +1,878 @@
1function splitascii(s) {
2 var rv='';
3 for(var i=0;i<s.length;++i) {
4 var w = s.charCodeAt(i);
5 rv += String.fromCharCode(
6 w&0xff, (w>>8)&0xff );
7 }
8 return rv;
9}
10
11
12var fireflix = {
13 flickr: new Flickr(),
14 init: function() {
15 this.loc_strings = document.getElementById('loc_strings');
16 this.build_menus();
17 this.cmd_set_props = document.getElementById('cmd_set_props');
18 this.foundphotos.init(this);
19 this.photosets.init(this);
20 this.photoset.init(this);
21 this.uploads.init(this);
22 this.uploadObserver.init(this);
23 this.flickr.api_key = '9c43cd66947a57e6f29db1a9da3f72e3';
24 this.flickr.api_shs = '9c33c9e2f0f0cfd5';
25 this.flickr.prefs_root = 'net.klever.kin.fireflix';
26 this.flickr.load_token();
27 document.getElementById('setslist').view = this.photosets;
28 document.getElementById('setphotos').view = this.photoset;
29 document.getElementById('uploadlist').view = this.uploads;
30 this.flickr.no_auth_info_label = document.getElementById('auth_info').value;
31 if(this.flickr.token) {
32 this.refresh_stuff();
33 document.getElementById('auth_info').value =
34 this.flickr.user.fullname+' ['+this.flickr.user.username+']';
35 document.getElementById('auth_info').disabled = false;
36 document.getElementById('b_auth').hidden = true;
37 }
38 },
39 on_auth: function() {
40 var _this = this;
41 this.flickr.authorize_0(
42 function() {
43 document.getElementById('b_auth').hidden = true;
44 document.getElementById('b_auth_done').hidden = false;
45 }, function(x,s,c,m) {
46 _this.flickr_failure(x,s,c,m);
47 }
48 );
49 },
50 on_auth_done: function() {
51 document.getElementById('b_auth_done').hidden = true;
52 var _this = this;
53 this.flickr.authorize_1(
54 function() {
55 _this.flickr.save_token();
56 _this.refresh_stuff();
57 document.getElementById('auth_info').value =
58 _this.flickr.user.fullname+' ['+_this.flickr.user.username+']';
59 document.getElementById('auth_info').disabled = false;
60 }, function(x,s,c,m) {
61 document.getElementById('b_auth').hidden = false;
62 _this.flickr_failure(x,s,c,m);
63 }
64 );
65 },
66
67 refresh_sets: function() { this.photosets.refresh_sets(); },
68 refresh_stuff: function() {
69 this.refresh_sets();
70 this.refresh_user_tags();
71 },
72
73 /* photoset treeview */
74 photoset: {
75 photos: new Array(),
76 fireflix: null,
77 init: function(f) {
78 this.fireflix = f;
79 },
80 rowCount: 0,
81 getCellText: function(r,c) {
82 var p = this.photos[r];
83 if(c.id=='sp_title') return p.title;
84 if(c.id=='sp_taken') return p.datetaken;
85 if(c.id=='sp_upload') return p.dateupload; /* TODO: unixtime conversion */
86 return c.id;
87 },
88 setTree: function(t) { this.tree = t },
89 isContainer: function(r) { return false; },
90 isSeparator: function(r) { return false; },
91 isSorted: function(r) { return false; },
92 getLevel: function(r) { return 0; },
93 getImageSrc: function(r,c) { return null },
94 getRowProperties: function(r,p) {},
95 getCellProperties: function(cid,cel,p) {},
96 getColumnProperties: function(cid,cel,p) { },
97 cycleHeader: function(cid,e) { },
98 getParentIndex: function(r) { return -1; },
99 drop: function(r,o) { },
100 canDropBeforeAfter: function(r,b) { return false },
101
102 importXPR: function(xp) {
103 this.tree.beginUpdateBatch();
104 this.photos = new Array();
105 var n; while(n=xp.iterateNext()) {
106 this.photos.push(new Photo(n));
107 }
108 this.rowCount = this.photos.length;
109 this.tree.endUpdateBatch();
110 },
111 load_photos: function(psid) {
112 var _this = this;
113 this.fireflix.flickr.api_call(
114 {
115 method: 'flickr.photosets.getPhotos',
116 auth_token: 'default',
117 photoset_id: psid,
118 extras: 'license,date_upload,date_taken,owner_name,icon_server,original_format,last_update'
119 }, function(xr) {
120 var x = xr.responseXML;
121 var xp = x.evaluate(
122 '/rsp/photoset/photo', x, null,
123 XPathResult.ORDERED_NODE_ITERATOR_TYPE, null );
124 _this.importXPR(xp);
125 }, function(x,s,c,m) {
126 _this.fireflix.flickr_failure(x,s,c,m);
127 }
128 );
129 },
130 on_select: function() {
131 if(this.selection.count==1) {
132 var p = this.photos[this.selection.currentIndex];
133 document.getElementById('set_photo').src =
134 this.fireflix.flickr.get_photo_url(p.server,p.id,p.secret,'t');
135 document.getElementById('set_photo').hidden = false;
136 }else{
137 document.getElementById('set_photo').hidden = true;
138 }
139 }
140 },
141
142 /* photosets treeview */
143 photosets: {
144 sets: new Array(),
145 fireflix: null,
146 init: function(f) {
147 this.fireflix = f;
148 },
149 rowCount: 0,
150 getCellText: function(r,c) {
151 var s = this.sets[r];
152 if(c.id=='sl_name') return s.title;
153 if(c.id=='sl_photos') return s.photos;
154 return c.id;
155 },
156 setTree: function(t) { this.tree = t },
157 isContainer: function(r) { return false; },
158 isSeparator: function(r) { return false; },
159 isSorted: function() { return false; },
160 getLevel: function(r) { return 0; },
161 getImageSrc: function(r,c) { return null },
162 getRowProperties: function(r,p) {},
163 getCellProperties: function(cid,cel,p) { },
164 getColumnProperties: function(cid,cel,p) { },
165 cycleHeader: function(cid,e) { },
166 getParentIndex: function(r) { return -1; },
167 drop: function(r,o) { },
168 canDropBeforeAfter: function(r,b) { return false },
169
170 importXPR: function(xp) {
171 this.tree.beginUpdateBatch();
172 this.sets = new Array();
173 var n; while(n=xp.iterateNext()) {
174 this.sets.push(new Photoset(n));
175 }
176 this.rowCount = this.sets.length;
177 this.tree.endUpdateBatch();
178 },
179 refresh_sets: function() {
180 var _this = this;
181 this.fireflix.flickr.api_call(
182 {
183 method: 'flickr.photosets.getList',
184 auth_token: 'default'
185 }, function(xr) {
186 var x = xr.responseXML;
187 var xp = x.evaluate(
188 '/rsp/photosets/photoset', x, null,
189 XPathResult.ORDERED_NODE_ITERATOR_TYPE, null );
190 _this.importXPR(xp);
191 }, function(x,s,c,m) {
192 _this.fireflix.flickr_failure(x,s,c,m);
193 }
194 );
195 },
196 on_select: function() {
197 if(this.selection.count==1) {
198 this.fireflix.cmd_set_props.setAttribute('disabled','false');
199 var s = this.sets[this.selection.currentIndex];
200 this.fireflix.photoset.load_photos(s.id);
201 }else{
202 this.fireflix.cmd_set_props.setAttribute('disabled','true');
203 }
204 }
205 },
206
207 refresh_user_tags: function() {
208 var lb = document.getElementById('tagslist');
209 var _this = this;
210 this.flickr.api_call(
211 {
212 method: 'flickr.tags.getListUser',
213 auth_token: 'default',
214 }, function(xr) {
215 var x = xr.responseXML;
216 var xp = x.evaluate(
217 '/rsp/who/tags/tag', x, null,
218 XPathResult.ORDERED_NODE_ITERATOR_TYPE, null );
219 // TODO: clear list
220 var n; while(n=xp.iterateNext()) {
221 lb.appendItem(n.firstChild.nodeValue);
222 }
223 }, function(x,s,c,m) {
224 _this.flickr_failure(x,s,c,m);
225 }
226 );
227 },
228
229 uploadObserver: {
230 fireflix: null,
231 init: function(f) {
232 this.fireflix = f;
233 },
234 getSupportedFlavours: function() {
235 var rv = new FlavourSet();
236 rv.appendFlavour('application/x-moz-file','nsIFile');
237 rv.appendFlavour('application/x-moz-url');
238 rv.appendFlavour('text/uri-list');
239 rv.appendFlavour('text/unicode');
240 return rv;
241 },
242 canHandleMultipleItems: true,
243 onDragOver: function(ev,fl,sess) {
244 return true;
245 },
246 onDrop: function(ev,dd,s) {
247 var ldf = null;
248 for(var i in dd.dataList) {
249 var di = dd.dataList[i];
250 var dif = di.first;
251 if(
252 ldf==null
253 || ldf.flavour.contentType!=dif.flavour.contentType
254 || ldf.contentLength!=dif.contentLength
255 || ldf.data!=dif.data )
256 this.drop_item(ev,di,s);
257 ldf = dif;
258 }
259 },
260 drop_item: function(ev,di,s) {
261 var d = di.first;
262 switch(d.flavour.contentType) {
263 case 'text/unicode':
264 this.drop_urilist(ev,d.data,s);
265 break;
266 case 'application/x-moz-file':
267 this.fireflix.uploads.add(d.data.path);
268 document.getElementById('fireflix_tabs').selectedTab
269 = document.getElementById('tab_upload');
270 break;
271 case 'text/uri-list':
272 // is it ascii or could it be utf8?
273 this.drop_urilist(ev,splitascii(d.data),s);
274 break;
275 default: alert(d.flavour.contentType+':'+d.data); break;
276 };
277 },
278 drop_urilist: function(ev,ul,s) {
279 // TODO: check for being a file?
280 var us = decodeURIComponent(ul).split(/[\r\n]/);
281 for(var ui in us)
282 if(/\S/.test(us[ui]))
283 this.fireflix.uploads.add(us[ui]);
284 document.getElementById('fireflix_tabs').selectedTab
285 = document.getElementById('tab_upload');
286 }
287 },
288
289 uploads: {
290 fireflix: null,
291 init: function(f) {
292 this.fireflix=f;
293 this.upload_filename = document.getElementById('upload_filename');
294 this.upload_title = document.getElementById('upload_title');
295 this.upload_file_preview = document.getElementById('upload_file_preview');
296 this.upload_file_props = document.getElementById('upload_file_props');
297 this.upload_progress = document.getElementById('upload_progress');
298 this.upload_tags = document.getElementById('upload_tags');
299 },
300 files: new Array(),
301 rowCount: 0,
302 getCellText: function(r,c) {
303 var f = this.files[r];
304 if(c.id=='up_file') return f.file;
305 if(c.id=='up_title') return f.title;
306 if(c.id=='up_status') return f.state;
307 return c.id;
308 },
309 setTree: function(t) { this.tree = t },
310 isContainer: function(r) { return false; },
311 isSeparator: function(r) { return false; },
312 isSorted: function(r) { return false; },
313 getLevel: function(r) { return 0; },
314 getImageSrc: function(r,c) { return null },
315 getRowProperties: function(r,p) {
316 try {
317 if(!Components) return;
318 }catch(e) { return }
319 var f = this.files[r];
320 var as = Components.classes['@mozilla.org/atom-service;1'].
321 getService(Components.interfaces.nsIAtomService);
322 p.AppendElement(as.getAtom(f.state));
323 },
324 getCellProperties: function(r,c,p) { this.getRowProperties(r,p); },
325 getColumnProperties: function(c,p) { },
326 cycleHeader: function(cid,e) { },
327 getParentIndex: function(r) { return -1; },
328 drop: function(r,o) { },
329 canDropBeforeAfter: function(r,b) { return false },
330
331 add: function(f) {
332 if(f.indexOf('file:/')==0) {
333 f = f.substr(5);
334 while(f.substr(0,2)=='//') { // XXX: not very performant, is it? ;-)
335 f = f.substr(1);
336 }
337 }
338 var t = f;
339 var ls = t.lastIndexOf('/');
340 if(ls>0) t = t.substr(ls+1);
341 ls = t.lastIndexOf('\\');
342 if(ls>0) t = t.substr(ls+1);
343 var ld = t.lastIndexOf('.');
344 if(ld>0) t = t.substr(0,ld);
345 this.files.push( {
346 file: f,
347 title: t,
348 tags: '',
349 state: 'pending'
350 } );
351 this.rowCount = this.files.length;
352 this.tree.rowCountChanged(this.rowCount-1,1);
353 },
354
355 upload_worker: function() {
356 for(var f in this.files) {
357 if(this.files[f].state=='pending') {
358 var ff = this.files[f];
359 dump('upload '+ff.file+'\n');
360 this.on_file_upload(ff);
361 ff.state='uploading';
362 this.tree.invalidate();
363 var _this = this;
364 this.fireflix.flickr.upload_file(
365 ff.file, { title: ff.title, tags: ff.tags },
366 function(x,p) {
367 ff.photoid = p;
368 _this.batch_ids.push(p);
369 ff.state='completed';
370 _this.tree.invalidate();
371 window.setTimeout(_this.upload_to,0,_this);
372 }, function(x,s,c,m) {
373 ff.state='failed';
374 ff.flickr_errcode = c;
375 ff.flickr_errmsg = m;
376 _this.tree.invalidate();
377 window.setTimeout(_this.upload_to,0,_this);
378 }
379 );
380 return;
381 }
382 }
383 dump('uploading done\n');
384 this.on_finish_upload();
385 },
386 upload_to: function(_this) { _this.upload_worker(); },
387 on_file_upload: function(f) {
388 document.getElementById('cmd_uploads_upload').setAttribute('disabled','true');
389 for(var fi in this.files) {
390 if(this.files[fi].file==f.file) {
391 this.tree.ensureRowIsVisible(fi);
392 this.selection.rangedSelect(fi,fi,false);
393 this.selection.currentIndex = fi;
394 this.selToProps();
395 break;
396 }
397 }
398 },
399 on_finish_upload: function() {
400 if(this.batch_ids.length) {
401 var psn = prompt(this.fireflix.loc_strings.getString('postUploadPhotoset'));
402 if(psn!=null) {
403 var pids = this.batch_ids.join(',');
404 var ppid = this.batch_ids[0];
405 var _this = this;
406 this.fireflix.flickr.api_call(
407 {
408 method: 'flickr.photosets.create',
409 auth_token: 'default',
410 title: psn,
411 primary_photo_id: ppid
412 }, function(x) {
413 var npid =
414 x.responseXML.getElementsByTagName('photoset').item(0).getAttribute('id');
415 _this.fireflix.flickr.api_call(
416 {
417 method: 'flickr.photosets.editPhotos',
418 auth_token: 'default',
419 photoset_id: npid,
420 primary_photo_id: ppid,
421 photo_ids: pids
422 }, function(x) {
423 _this.fireflix.refresh_sets();
424 }, function(x,s,c,m) {
425 _this.fireflix.flickr_failure(x,s,c,m);
426 }
427 );
428 }, function(x,s,c,m) {
429 _this.fireflix.flickr_failure(x,s,c,m);
430 }
431 );
432 }
433 }
434 this.selection.clearSelection();
435 document.getElementById('cmd_uploads_upload').setAttribute('disabled','false');
436 this.upload_progress.setAttribute('hidden','true');
437 },
438
439 clear_list: function() {
440 this.tree.beginUpdateBatch();
441 this.rowCount = 0;
442 this.files = new Array();
443 this.tree.endUpdateBatch();
444 this.selToProps();
445 },
446 selectionChanged: function() {
447 this.selToProps();
448 },
449 disableProps: function() {
450 this.upload_filename.value='';
451 this.upload_filename.disabled = true;
452 this.upload_title.value='';
453 this.upload_title.disabled = true;
454 this.upload_file_preview.src = null;
455 this.upload_file_props.hidden = true;
456 this.upload_tags.value='';
457 this.upload_tags.disabled = true;
458 },
459 selToProps: function() {
460 if(!this.selection.count) {
461 this.disableProps();
462 }else if(this.selection.count==1) {
463 var f=this.files[this.selection.currentIndex];
464 if(f==null || f.state!='pending') {
465 this.disableProps();
466 }else{
467 this.upload_filename.value = f.file;
468 this.upload_filename.disabled = false;
469 this.upload_title.value = f.title;
470 this.upload_title.disabled = false;
471 this.upload_file_preview.src = 'file:///'+f.file;
472 this.upload_file_props.hidden = false;
473 this.upload_tags.value = f.tags;
474 this.upload_tags.disabled = false;
475 }
476 }else{
477 var ftitle = null; var onetitle = true;
478 var ftags = null; var onetag = true;
479 var fs = 0;
480 for(var ff in this.files) {
481 if(this.selection.isSelected(ff) && this.files[ff].state=='pending' ) {
482 ++fs;
483 if(ftitle==null) {
484 ftitle = this.files[ff].title;
485 }else if(ftitle!=this.files[ff].title) {
486 onetitle = false;
487 }
488 if(ftags==null) {
489 ftags = this.files[ff].tags;
490 }else if(ftags!=this.files[ff].tags) {
491 onetag = false;
492 }
493 }
494 }
495 if(fs) {
496 this.upload_filename.value='';
497 this.upload_filename.disabled = true;
498 if(onetitle)
499 this.upload_title.value = ftitle;
500 this.upload_title.disabled = false;
501 if(onetag)
502 this.upload_tags.value = ftags;
503 this.upload_tags.disabled = false;
504 this.upload_file_preview.src = null;
505 this.upload_file_props.hidden = false;
506 }else
507 this.disableProps();
508 }
509 },
510 propsToSel: function(prop) {
511 if(this.selection.count<=0) return;
512 for(var ff in this.files) {
513 if(this.selection.isSelected(ff) && this.files[ff].state=='pending') {
514 if(prop=='filename')
515 this.files[ff].file = this.upload_filename.value;
516 if(prop=='title')
517 this.files[ff].title = this.upload_title.value;
518 if(prop=='tags')
519 this.files[ff].tags = this.upload_tags.value;
520 this.tree.invalidateRow(ff);
521 }
522 }
523 },
524
525 on_upload: function() {
526 this.selToProps();
527 this.batch_ids = new Array();
528 this.upload_progress.value=0;
529 this.upload_progress.setAttribute('hidden','false');
530 this.upload_worker();
531 },
532 on_clear: function() {
533 this.clear_list();
534 },
535 on_remove: function() {
536 if(this.selection.count) {
537 this.tree.beginUpdateBatch();
538 for(var i=this.files.length-1;i>=0;--i) {
539 if(this.selection.isSelected(i)) {
540 this.files.splice(i,1);
541 this.rowCount--;
542 }
543 }
544 this.tree.endUpdateBatch();
545 this.selection.clearSelection();
546 }
547 },
548 on_add: function() {
549 var ifp = Components.interfaces.nsIFilePicker;
550 var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(ifp);
551 fp.init(window, "Select a File", ifp.modeOpenMultiple);
552 fp.appendFilters(ifp.filterImages);
553 var rv = fp.show();
554 if(rv==ifp.returnOK) {
555 var ff = fp.files;
556 while(ff.hasMoreElements()) {
557 var f = ff.getNext();
558 f.QueryInterface(Components.interfaces.nsIFile);
559 this.add(f.path);
560 }
561 }
562 }
563 },
564
565 on_set_props: function() {
566 var pset = this.photosets.sets[this.photosets.selection.currentIndex];
567 window.openDialog(
568 "chrome://fireflix/content/photoset-props.xul",
569 null, "dependent,modal,dialog,chrome", this,
570 pset );
571 if(pset.dirty) {
572 var _this = this;
573 this.flickr.api_call(
574 {
575 method: 'flickr.photosets.editMeta',
576 auth_token: 'default',
577 photoset_id: pset.id,
578 title: pset.title,
579 description: pset.description
580 }, function(xr) {
581 pset.dirty = false;
582 _this.flickr.api_call(
583 {
584 method: 'flickr.photosets.getPhotos',
585 auth_token: 'default',
586 photoset_id: pset.id
587 }, function(xr) {
588 var x = xr.responseXML;
589 var xp = x.evaluate(
590 '/rsp/photoset/photo', x, null,
591 XPathResult.ORDERED_NODE_ITERATOR_TYPE, null );
592 var phids = new Array();
593 var priph = null;
594 var n; while(n=xp.iterateNext()) {
595 var pid = n.getAttribute('id');
596 phids.push( pid );
597 if(pid==pset.primary && n.getAttribute('isprimary')!='1')
598 priph = pid;
599 }
600 if(priph) {
601 _this.flickr.api_call(
602 {
603 method: 'flickr.photosets.editPhotos',
604 auth_token: 'default',
605 photoset_id: pset.id,
606 primary_photo_id: priph,
607 photo_ids: phids.join(',')
608 }, function() { }, function(x,s,c,m) { /* flickr.photosets.editPhotos */
609 _this.flickr_failure(x,s,c,m);
610 }
611 );
612 }
613 }, function(x,s,c,m) { /* flickr.photosets.getPhotos */
614 _this.flickr_failure(x,s,c,m);
615 }
616 );
617 }, function(x,s,c,m) { /* flickr.photosets.editMeta */
618 _this.flickr_failure(x,s,c,m);
619 }
620 );
621 }
622 },
623 on_refresh_sets: function() {
624 this.refresh_sets();
625 },
626 on_cmd_sets_html: function(csfx,ev) {
627 var uti = csfx.charAt(0); var utl = csfx.charAt(1);
628 var rv = this.build_html(this.photoset.photos,uti,utl);
629 this.popup_content(rv);
630 },
631
632 on_cmd_uploads_html: function(csfx,ev) {
633 var uti = csfx.charAt(0); var utl = csfx.charAt(1);
634 var pids = new Array();
635 for(var f in this.uploads.files) {
636 if(this.uploads.selection.isSelected(f))
637 if(this.uploads.files[f].photoid)
638 pids.push(this.uploads.files[f].photoid);
639 }
640 var pp = this.uploads.rowCount*2; if(pp>500) pp = 500;
641 var _this = this;
642 this.flickr.api_call(
643 {
644 method: 'flickr.photos.search',
645 auth_token: 'default',
646 extras: 'original_format',
647 user_id: 'me',
648 per_page: pp
649 },
650 function(xr) {
651 var x = xr.responseXML;
652 var rv = '';
653 for(var pn in pids) {
654 var p = pids[pn];
655 var pp = new Photo(xp_node('/rsp/photos/photo[@id='+p+']',x));
656 rv += _this.photo_html(pp,uti,utl)+'\n';
657 }
658 _this.popup_content(rv);
659 }, function(x,s,c,m) {
660 _this.flickr_failure(x,s,c,m);
661 }
662 );
663 },
664
665 /*
666 *
667 */
668 foundphotos: {
669 fireflix: null,
670 init: function(f) {
671 this.fireflix = f;
672 this.search_for = document.getElementById('search_for');
673 this.search_tags= document.getElementById('search_tags');
674 this.search_mine = document.getElementById('search_mine');
675 document.getElementById('searchresults').view = this;
676 this.searchresult_props = document.getElementById('searchresult_props');
677 this.search_photo = document.getElementById('search_photo');
678 this.searchresult_title = document.getElementById('searchresult_title');
679 this.searchresult_description = document.getElementById('searchresult_description');
680 },
681 photos: new Array(),
682 rowCount: 0,
683 getCellText: function(r,c) {
684 var p = this.photos[r];
685 if(c.id=='sr_title') return p.title;
686 return c.id;
687 },
688 setTree: function(t) { this.tree = t },
689 isContainer: function(r) { return false },
690 isSeparator: function(r) { return false },
691 isSorted: function(r) { return false },
692 getLevel: function(r) { return 0 },
693 getImageSrc: function(r,c) { return null },
694 getRowProperties: function(r,p) { },
695 getCellProperties: function(cid,cel,p) { },
696 getColumnProperties: function(cid,cel,p) { },
697 cycleHeader: function(cid,e) { },
698 getParentIndex: function(r) { return -1 },
699 drop: function(r,o) { },
700 canDropBeforeAfter: function(r,b) { return false },
701
702 importXPR: function(xp) {
703 this.selection.clearSelection();
704 this.selection.currentIndex = -1;
705 this.searchresult_props.hidden = true;
706 this.tree.beginUpdateBatch();
707 this.photos = new Array();
708 var n; while(n=xp.iterateNext()) {
709 this.photos.push(new Photo(n));
710 }
711 this.rowCount = this.photos.length;
712 this.tree.endUpdateBatch();
713 },
714 search_photos: function() {
715 var pars = {
716 method: 'flickr.photos.search',
717 auth_token: 'default',
718 extras: 'license,date_upload,date_taken,owner_name,icon_server,original_format,last_update,geo'
719 };
720 if(this.search_mine.checked)
721 pars.user_id='me';
722 if(this.search_tags.checked) {
723 pars.tags=this.search_for.value.split(/ +/).join(',');
724 }else{
725 pars.text=this.search_for.value;
726 }
727 var _this = this;
728 this.fireflix.flickr.api_call( pars,
729 function(xr) {
730 var x = xr.responseXML;
731 var xp = x.evaluate(
732 '/rsp/photos/photo', x, null,
733 XPathResult.ORDERED_NODE_ITERATOR_TYPE, null );
734 _this.importXPR(xp);
735 _this.on_select();
736 }, function(x,s,c,m) {
737 _this.fireflix.flickr_failure(x,s,c,m);
738 }
739 );
740 },
741 on_select: function() {
742 if(this.selection.currentIndex<0) {
743 this.searchresult_props.hidden = true;
744 }else{
745 var p = this.photos[this.selection.currentIndex];
746 if(!p) {
747 this.searchresult_props.hidden = true;
748 }else{
749 this.search_photo.src = this.fireflix.flickr.make_photo_url(p,'t');
750 this.searchresult_title.value = p.title;
751 this.searchresult_description.value = null;
752 if(p.description==null && p.description==undefined) {
753 var pid = p.id;
754 var ci = this.selection.currentIndex;
755 var _this = this;
756 this.fireflix.flickr.api_call(
757 {
758 method: 'flickr.photos.getInfo',
759 auth_token: 'default',
760 photo_id: p.id,
761 secret: p.secret
762 }, function(xr) {
763 var pp = _this.photos[ci];
764 if(ci==_this.selection.currentIndex && pp.id==pid) {
765 var n = xp_node('/rsp/photo',xr.responseXML);
766 pp.fromNode_(n);
767 _this.searchresult_description.value=pp.description?pp.description:null;
768 }
769 }, function(x,s,c,m) {
770 _this.fireflix.flickr_failure(x,s,c,m);
771 }
772 );
773 this.searchresult_props.hidden = false;
774 }else{
775 this.searchresult_description.value=p.description?p.description:null;
776 }
777 }
778 }
779 },
780 on_cmd_open: function(ev) {
781 if(this.selection.currentIndex<0)
782 return;
783 var p = this.photos[this.selection.currentIndex];
784 if(!p.id)
785 return;
786 this.fireflix.openTab(this.fireflix.flickr.make_photo_url(p,'p'));
787 }
788 },
789
790 photo_html: function(p,i,l) {
791 // TODO: add alt/title when possible
792 var rv =
793 '<a href="'+this.flickr.make_photo_url(p,l)+'">' +
794 '<img src="'+this.flickr.make_photo_url(p,i)+'" />'+
795 '</a>';
796 return rv;
797 },
798 build_html: function(photos,uti,utl) {
799 var rv = '';
800 for(var i in photos) {
801 var p = photos[i];
802 rv += this.photo_html(p,utl,uti)+'\n';
803 }
804 return rv;
805 },
806
807 popup_content: function(s) {
808 window.openDialog(
809 "chrome://fireflix/content/generated-content.xul",
810 null, "dialog,chrome", this, s );
811 },
812 copy_to_clipboard: function(s) {
813 var ch = Components.classes["@mozilla.org/widget/clipboardhelper;1"]
814 .getService(Components.interfaces.nsIClipboardHelper);
815 ch.copyString(s);
816 },
817 openTab: function(l) {
818 var wm = Components.classes['@mozilla.org/appshell/window-mediator;1'].getService(
819 Components.interfaces.nsIWindowMediator );
820 var bw = wm.getMostRecentWindow('navigator:browser');
821 var b = bw.getBrowser();
822 var t = b.addTab(l);
823 b.selectedTab = t;
824 },
825
826 build_menus: function() {
827 this.append_html_menu(
828 document.getElementById('sets_html_menu'),
829 'stm_','m_bop','cmdset_sets','cmd_sets_html'
830 );
831 this.append_html_menu(
832 document.getElementById('uploads_html_menu'),
833 'stm_','m_bop','cmdset_uploads','cmd_uploads_html'
834 );
835 return;
836 },
837 append_html_menu: function(m,imgt,lnkt,csid,cpfx) {
838 var mp = m.appendChild(document.createElement('menupopup'));
839 var t;
840 t=mp.appendChild(document.createElement('menuitem'));
841 t.setAttribute('label',this.loc_strings.getString('menutitle_Images'));
842 t.setAttribute('class','menuhead');t.setAttribute('disabled','true');
843 mp.appendChild(document.createElement('menuseparator'));
844 var cs = document.getElementById(csid);
845 for(var iti=0;iti<imgt.length;++iti) {
846 t = mp.appendChild(document.createElement('menu'));
847 t.setAttribute('label',this.loc_strings.getString('urltype_'+imgt.charAt(iti)));
848 var smp = t.appendChild(document.createElement('menupopup'));
849 t=smp.appendChild(document.createElement('menuitem'));
850 t.setAttribute('label',this.loc_strings.getString('menutitle_Links'));
851 t.setAttribute('class','menuhead');t.setAttribute('disabled','true');
852 smp.appendChild(document.createElement('menuseparator'));
853 for(var lti=0;lti<lnkt.length;++lti) {
854 var csfx = imgt.charAt(iti)+lnkt.charAt(lti);
855 t=smp.appendChild(document.createElement('menuitem'));
856 t.setAttribute('label',this.loc_strings.getString('urltype_'+lnkt.charAt(lti)));
857 t.setAttribute('command',cpfx+'_'+csfx);
858 t=cs.appendChild(document.createElement('command'));
859 t.setAttribute('id',cpfx+'_'+csfx);
860 t.setAttribute('oncommand','fireflix.on_'+cpfx+"('"+csfx+"',event)");
861 }
862 }
863 return mp;
864 },
865
866 flickr_failure: function(x,s,c,m) {
867 if(c==98) { // Invalid auth token
868 this.flickr.reset_token();
869 document.getElementById('auth_info').value = this.no_auth_info_label;
870 document.getElementById('auth_info').disabled = true;
871 document.getElementById('b_auth').hidden = false;
872 return;
873 }
874 // TODO: is that beauty
875 alert('flickr api call failed\n'+c+' '+m);
876 }
877
878};
diff --git a/content/flickr.js b/content/flickr.js
new file mode 100644
index 0000000..3554796
--- a/dev/null
+++ b/content/flickr.js
@@ -0,0 +1,403 @@
1/*
2 * Photoset
3 */
4
5function Photoset(s) {
6 if(s instanceof Photoset) {
7 for(var p in s) this[p]=s[p];
8 }else
9 this.fromNode(s);
10}
11Photoset.prototype = {
12 id: null,
13 primary: null,
14 secret: null,
15 server: null,
16 photos: null,
17 title: null,
18 description: null,
19 fromNode: function(n) {
20 this.id = n.getAttribute('id');
21 this.primary = n.getAttribute('primary');
22 this.secret = n.getAttribute('secret');
23 this.server = n.getAttribute('server');
24 this.photos = n.getAttribute('photos');
25 this.title = n.getElementsByTagName('title').item(0).firstChild.nodeValue;
26 this.description = n.getElementsByTagName('description').item(0).firstChild;
27 if(this.description) this.description = this.description.nodeValue;
28 }
29};
30
31/*
32 * Photo
33 */
34function Photo(s) {
35 if(s instanceof Photo) {
36 for(var p in s) this[p]=s[p];
37 }else
38 this.fromNode(s);
39}
40Photo.prototype = {
41 id: null, secret: null,
42 server: null,
43 title: null,
44 isprimary: null,
45 license: null,
46 dateupload: null, datetaken: null, datetakengranularity: null,
47 ownername: null,
48 iconserver: null,
49 originalformat: null,
50 lastupdate: null,
51 fromNode: function(n) {
52 this.id = n.getAttribute('id'); this.secret = n.getAttribute('secret');
53 this.server = n.getAttribute('server');
54 this.title = n.getAttribute('title');
55 this.isprimary = n.getAttribute('isprimary');
56 this.license = n.getAttribute('license');
57 this.dateupload = n.getAttribute('dateupload');
58 this.datetaken = n.getAttribute('datetaken'); this.datetakengranularity = n.getAttribute('datetakengranularity');
59 this.ownername = n.getAttribute('ownername');
60 this.iconserver = n.getAttribute('iconserver');
61 this.originalformat = n.getAttribute('originalformat');
62 this.lastupdate = n.getAttribute('lastupdate');
63 },
64 fromNode_: function(n) {
65 var t;
66 // TODO: @rotation @isfavorite
67 this.owner = {};
68 t = n.getElementsByTagName('owner').item(0);
69 if(t) {
70 this.owner.nsid=t.getAttribute('nsid');
71 this.owner.username=t.getAttribute('username');
72 this.owner.realname=t.getAttribute('realname');
73 this.owner.location=t.getAttribute.location;
74 }
75 t = n.getElementsByTagName('description').item(0);
76 if(t && t.firstChild) {
77 this.description = t.firstChild.nodeValue;
78 }
79 // TODO: visibility/@ispublic visibility/@isfriend visibility/@isfamily
80 // TODO: dates/@posted dates/@taken dates/@takengranularity dates/@lastupdate
81 // TODO: permissions/@permcomment permsiions/@permaddmeta
82 // TODO: editability/@canaddcomment editability/@canaddmeta
83 // TODO: comments
84 // TODO: notes/note/@id notes/note/@author notes/note/@authorname
85 // TODO: notes/note/@x notes/note/@y notes/note/@w notes/note/@h
86 // TODO: notes/note
87 // TODO: tags/tag/@id tags/tag/@author tags/tag/@raw tags/tag
88 // TODO: urls/url/@type urls/url
89 }
90};
91
92function toutf8(ucode) {
93 var rv = '';
94 for(var i=0;i<ucode.length;++i) {
95 var cc = ucode.charCodeAt(i);
96 if(cc<=0x7F)
97 rv += ucode.charAt(i);
98 else if(cc<=0x7ff)
99 rv += String.fromCharCode(
100 0xc0|((cc>> 6)&0x1f),
101 0x80|( cc &0x3f) );
102 else if(cc<=0xffff)
103 rv += String.fromCharCode(
104 0xe0|((cc>>12)&0x0f),
105 0x80|((cc>> 6)&0x3f),
106 0x80|( cc &0x3f) );
107 else if(cc<=0x1fffff)
108 rv += String.fromCharCode(
109 0xf0|((cc>>18)&0x07),
110 0x80|((cc>>12)&0x3f),
111 0x80|((cc>> 6)&0x3f),
112 0x80|( cc &0x3f) );
113 else if(cc<=0x03ffffff)
114 rv += String.fromCharCode(
115 0xf8|((cc>>24)&0x03),
116 0x80|((cc>>18)&0x3f),
117 0x80|((cc>>12)&0x3f),
118 0x80|((cc>> 6)&0x3f),
119 0x80|( cc &0x3f) );
120 else if(cc<=0x7fffffff)
121 rv += String.fromCharCode(
122 0xfc|((cc>>30)&0x01),
123 0x80|((cc>>24)&0x3f),
124 0x80|((cc>>18)&0x3f),
125 0x80|((cc>>12)&0x3f),
126 0x80|((cc>> 6)&0x3f),
127 0x80|( cc &0x3f) );
128 }
129 return rv;
130}
131function xp_str(xp,x) {
132 var rv = x.evaluate(
133 xp, x, null, XPathResult.STRING_TYPE, null );
134 return rv.stringValue;
135}
136function xp_node(xp,x) {
137 var rv = x.evaluate(
138 xp, x, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null );
139 return rv.singleNodeValue;
140}
141
142function Flickr() { }
143Flickr.prototype = {
144
145 rest_url: 'http://www.flickr.com/services/rest/',
146 auth_url: 'http://flickr.com/services/auth/',
147 photo_url: 'http://static.flickr.com/',
148 photos_url: 'http://www.flickr.com/photos/',
149 upload_url: 'http://www.flickr.com/services/upload/',
150
151 api_sig: function(paramstr) {
152 return MD5(toutf8(this.api_shs+paramstr));
153 },
154 api_call_url: function(params,url) {
155 params.api_key = this.api_key;
156 var pp = new Array();
157 for(var p in params) {
158 pp.push(p);
159 }
160 var pstr = '';
161 var rv = (url?url:this.rest_url)+'?';
162 for(var p in pp.sort()) {
163 var pn = pp[p];
164 pstr += pn+params[pn];
165 rv += pn+'='+params[pn]+'&';
166 }
167 rv += 'api_sig='+this.api_sig(pstr);
168 return rv;
169 },
170 api_call: function(params, on_success, on_failure) {
171 if(params.auth_token == 'default')
172 params.auth_token = this.token;
173 var x = new XMLHttpRequest();
174 x.open("GET",this.api_call_url(params));
175 x.onreadystatechange=function() {
176 if(x.readyState!=4) return false;
177 if(x.status==200) {
178 var stat = x.responseXML.firstChild.getAttribute('stat');
179 if(stat=='ok') {
180 if(on_success) on_success(x);
181 }else{
182 var e = x.responseXML.getElementsByTagName('err').item(0);
183 var ecode = e.getAttribute('code');
184 var emsg = e.getAttribute('msg');
185 dump(params.method+' failed: '+ecode+' '+emsg+'\n');
186 if(on_failure) on_failure(x,stat,ecode,emsg);
187 }
188 }else{
189 if(on_failure) on_failure(x);
190 }
191 return true;
192 }
193 x.send(null);
194 return true;
195 },
196
197 frob: null,
198 authorize_0: function(on_s, on_f) {
199 var _this = this;
200 this.api_call(
201 { method: 'flickr.auth.getFrob' },
202 function(x) {
203 _this.frob = xp_str('/rsp/frob',x.responseXML);
204 var u = _this.api_call_url(
205 { frob: _this.frob, perms: 'delete' }, _this.auth_url );
206 var wm = Components.classes['@mozilla.org/appshell/window-mediator;1'].getService(
207 Components.interfaces.nsIWindowMediator );
208 var bw = wm.getMostRecentWindow('navigator:browser');
209 var b = bw.getBrowser();
210 var t = b.addTab(u);
211 b.selectedTab = t;
212 if(on_s) on_s();
213 }, function(x,s,c,m) {
214 if(on_f) on_f(x,s,c,m);
215 }
216 );
217 },
218 token: null,
219 perms: null,
220 user: null,
221 authorize_1: function(on_s, on_f) {
222 var _this = this;
223 this.api_call(
224 { method: 'flickr.auth.getToken', frob: this.frob },
225 function(x) {
226 _this.token = xp_str('/rsp/auth/token',x.responseXML);
227 _this.perms = xp_str('/rsp/auth/perms',x.responseXML);
228 var u = xp_node('/rsp/auth/user',x.responseXML);
229 _this.user = {
230 nsid: u.getAttribute('nsid'),
231 username: u.getAttribute('username'),
232 fullname: u.getAttribute('fullname')
233 };
234 if(on_s) on_s(x);
235 }, function(x,s,c,m) {
236 if(on_f) on_f(x,s,c,m);
237 }
238 );
239 },
240
241 prefs: Components.classes['@mozilla.org/preferences-service;1'].getService(
242 Components.interfaces.nsIPrefBranch
243 ),
244 prefs_root: 'net.klever.kin.flickr',
245 save_token: function() {
246 // TODO: don't clear when there's nothing to clear or catch exceptions
247 if(this.token)
248 this.prefs.setCharPref(this.prefs_root+'.auth_token',this.token);
249 else
250 this.prefs.clearUserPref(this.prefs_root+'.auth_token');
251 if(this.perms)
252 this.prefs.setCharPref(this.prefs_root+'.auth_perms',this.perms);
253 else
254 this.prefs.clearUserPref(this.prefs_root+'.auth_perms');
255 if(this.user && this.user.nsid!=null && this.user.nsid!=undefined)
256 this.prefs.setCharPref(this.prefs_root+'.auth_user.nsid',this.user.nsid);
257 else
258 this.prefs.clearUserPref(this.prefs_root+'.auth_user.nsid');
259 if(this.user && this.user.username!=null && this.user.username!=undefined)
260 this.prefs.setCharPref(this.prefs_root+'.auth_user.username',this.user.username);
261 else
262 this.prefs.clearUserPref(this.prefs_root+'.auth_user.username');
263 if(this.user && this.user.fullname!=null && this.user.fullname!=undefined)
264 this.prefs.setCharPref(this.prefs_root+'.auth_user.fullname',this.user.fullname);
265 else
266 this.prefs.clearUserPref(this.prefs_root+'.auth_user.fullname');
267 },
268 _reset_token: function() {
269 this.token = null; this.perms = null; this.user = null;
270 return false;
271 },
272 load_token: function() {
273 try {
274 if(this.prefs.getPrefType(this.prefs_root+'.auth_token')!=this.prefs.PREF_STRING)
275 return this._reset_token();
276 this.token = this.prefs.getCharPref(this.prefs_root+'.auth_token');
277 if(this.prefs.getPrefType(this.prefs_root+'.auth_perms')!=this.prefs.PREF_STRING)
278 return this._reset_token();
279 this.perms = this.prefs.getCharPref(this.prefs_root+'.auth_perms');
280 if(this.prefs.getPrefType(this.prefs_root+'.auth_user.nsid')!=this.prefs.PREF_STRING)
281 return this._reset_token();
282 this.user = new Object();
283 this.user.nsid = this.prefs.getCharPref(this.prefs_root+'.auth_user.nsid');
284 if(this.prefs.getPrefType(this.prefs_root+'.auth_user.username')!=this.prefs.PREF_STRING)
285 return this._reset_token();
286 this.user.username = this.prefs.getCharPref(this.prefs_root+'.auth_user.username');
287 if(this.prefs.getPrefType(this.prefs_root+'.auth_user.fullname')!=this.prefs.PREF_STRING)
288 return this._reset_token();
289 this.user.fullname = this.prefs.getCharPref(this.prefs_root+'.auth_user.fullname');
290 }catch(e) { return this._reset_token(); }
291 return true;
292 },
293 reset_token: function() {
294 this._reset_token();
295 this.save_token();
296 },
297
298 get_photo_url: function(ser,id,sec,sfx,ext) {
299 var rv = this.photo_url + ser + '/' + id + '_' + sec;
300 if(sfx && sfx!='_') rv += '_'+sfx;
301 rv += ext?'.'+ext:'.jpg';
302 return rv;
303 },
304 get_image_url: function(o,sfx) {
305 return this.get_photo_url(
306 o.server,
307 (o instanceof Photoset)? o.primary : o.id,
308 o.secret,
309 sfx,
310 (sfx=='o')?o.originalformat:null
311 );
312 },
313 get_photo_page_url: function(p) {
314 if(p instanceof Photo) // TODO: half wrong, what if no owner?
315 return this.photos_url + (p.owner.nsid?p.owner.nsid:this.user.nsid) + '/' + p.id;
316 else // TODO: take owner into account?
317 return this.photos_url + this.user.nsid + '/' + p;
318 },
319 make_photo_url: function(p,sfx) {
320 if(sfx=='p')
321 return this.get_photo_page_url(p);
322 else
323 return this.get_image_url(p,sfx);
324 },
325
326 upload_file: function(f,fa,on_success,on_failure) {
327 try {
328 var fi = Components.classes["@mozilla.org/file/local;1"]
329 .createInstance(Components.interfaces.nsILocalFile);
330 fi.initWithPath( f );
331 var st = Components.classes["@mozilla.org/network/file-input-stream;1"]
332 .createInstance(Components.interfaces.nsIFileInputStream);
333 st.init(fi,0x01,00004,null);
334 var bis = Components.classes["@mozilla.org/binaryinputstream;1"]
335 .createInstance(Components.interfaces.nsIBinaryInputStream);
336 bis.setInputStream(st);
337
338 // allocate and initialize temp storage string
339 var pbs = Components.classes["@mozilla.org/storagestream;1"]
340 .createInstance(Components.interfaces.nsIStorageStream);
341 pbs.init(1024,10000000,null);
342 // create output stream
343 var pbos = pbs.getOutputStream(0);
344 // and a binaryoutputstream interface
345 var pbbos = Components.classes["@mozilla.org/binaryoutputstream;1"]
346 .createInstance(Components.interfaces.nsIBinaryOutputStream);
347 pbbos.setOutputStream(pbos);
348
349 /* create POST body */
350 var boundarytoken = 'kadaroloongazaduviaxamma';
351 var boundary = '--'+boundarytoken;
352 var b = '';
353
354 var parms = { api_key: this.api_key, auth_token: this.token };
355 for(var p in fa) parms[p] = fa[p];
356 var pns = new Array();
357 for(var p in parms) pns.push(p);
358 var pstr = '';
359 for(var p in pns.sort()) {
360 var pn = pns[p];
361 pstr += pn+parms[pn];
362 b += boundary+'\nContent-Disposition: form-data; name="'+pn+'"\n\n'+toutf8(parms[pn])+'\n';
363 }
364 b += boundary+'\nContent-Disposition: form-data; name="api_sig"\n\n'+this.api_sig(pstr)+'\n';
365 b += boundary+'\nContent-Disposition: form-data; name="photo"; filename="'+f+'"\nContent-Type: image/jpeg\nContent-Transfer-Encoding: binary\n\n';
366 pbbos.writeBytes(b,b.length);
367 var bisbytes = bis.available();
368 pbbos.writeBytes(bis.readBytes(bisbytes),bisbytes);
369 pbbos.writeBytes('\n'+boundary+'--',3+boundary.length); bis.close(); st.close();
370
371 pbbos.close(); pbos.close();
372
373 var x = new XMLHttpRequest();
374 x.open("POST",this.upload_url);
375 x.setRequestHeader('Content-Type', 'multipart/form-data; boundary="'+boundarytoken+'"');
376 x.setRequestHeader('Connection','close');
377 x.setRequestHeader('Content-Length',b.length);
378 x.onreadystatechange=function() {
379 if(x.readyState!=4) return false;
380 if(x.status==200) {
381 var stat = x.responseXML.firstChild.getAttribute('stat');
382 if(stat=='ok') {
383 var pid = xp_str('/rsp/photoid',x.responseXML);
384 if(on_success) on_success(x,pid);
385 }else{
386 var e = x.responseXML.getElementsByTagName('err').item(0);
387 var ecode = e.getAttribute('code');
388 var emsg = e.getAttribute('msg');
389 dump('upload failed: '+ecode+' '+emsg+'\n');
390 if(on_failure) on_failure(x,stat,ecode,emsg);
391 }
392 }else{
393 if(on_failure) on_failure(x);
394 }
395 return true;
396 };
397 x.send(pbs.newInputStream(0));
398 }catch(e) {
399 if(on_failure) on_failure(e,null,-1,e.message);
400 }
401 }
402
403};
diff --git a/content/generated-content.js b/content/generated-content.js
new file mode 100644
index 0000000..0ad08bb
--- a/dev/null
+++ b/content/generated-content.js
@@ -0,0 +1,17 @@
1var generated = {
2 fireflix: null,
3 data: null,
4
5 init: function() {
6 this.fireflix = window.arguments[0];
7 this.data = window.arguments[1];
8 this.databox = document.getElementById('data');
9 this.databox.value = this.data;
10 this.databox.select();
11 },
12 copy: function() {
13 var ch = Components.classes["@mozilla.org/widget/clipboardhelper;1"]
14 .getService(Components.interfaces.nsIClipboardHelper);
15 ch.copyString(this.data);
16 }
17};
diff --git a/content/generated-content.xul b/content/generated-content.xul
new file mode 100644
index 0000000..2a91efa
--- a/dev/null
+++ b/content/generated-content.xul
@@ -0,0 +1,20 @@
1<?xml version="1.0" encoding="utf-8"?>
2<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
3<?xsml-stylesheet href="fireflix.css" type="text/css"?>
4<!DOCTYPE dialog SYSTEM "chrome://fireflix/locale/fireflix.dtd">
5<dialog
6 xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
7 id="generated_content"
8 buttons="accept"
9 defaultbutton="accept"
10 title="&generated.title;"
11 onload="generated.init()"
12 >
13
14 <script src="generated-content.js" type="application/x-javascript"/>
15
16 <vbox class="generated wholething" flex="1">
17 <textbox flex="1" minheight="300" minwidth="300" id="data" multiline="true" readonly="true" />
18 <button id="copy" label="&generated.copy;" oncommand="generated.copy()" />
19 </vbox>
20</dialog>
diff --git a/content/icons/16x16/fireflix.png b/content/icons/16x16/fireflix.png
new file mode 100644
index 0000000..377ab28
--- a/dev/null
+++ b/content/icons/16x16/fireflix.png
Binary files differ
diff --git a/content/icons/32x32/fireflix.png b/content/icons/32x32/fireflix.png
new file mode 100644
index 0000000..374ebbd
--- a/dev/null
+++ b/content/icons/32x32/fireflix.png
Binary files differ
diff --git a/content/md5.js b/content/md5.js
new file mode 100644
index 0000000..f22db2b
--- a/dev/null
+++ b/content/md5.js
@@ -0,0 +1,154 @@
1/* MD5 Message-Digest Algorithm - JavaScript
2' MODIFICATION HISTORY:
3' 1.0 16-Feb-2001 - Phil Fresle (sales@frez.co.uk) - Initial Version (VB/ASP code)
4' 1.0 21-Feb-2001 - Enrico Mosanghini (erik504@yahoo.com) - JavaScript porting
5*/
6function MD5(sMessage) {
7 function RotateLeft(lValue, iShiftBits) { return (lValue<<iShiftBits) | (lValue>>>(32-iShiftBits)); }
8 function AddUnsigned(lX,lY) {
9 var lX4,lY4,lX8,lY8,lResult;
10 lX8 = (lX & 0x80000000);
11 lY8 = (lY & 0x80000000);
12 lX4 = (lX & 0x40000000);
13 lY4 = (lY & 0x40000000);
14 lResult = (lX & 0x3FFFFFFF)+(lY & 0x3FFFFFFF);
15 if (lX4 & lY4) return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
16 if (lX4 | lY4) {
17 if (lResult & 0x40000000) return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
18 else return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
19 } else return (lResult ^ lX8 ^ lY8);
20 }
21 function F(x,y,z) { return (x & y) | ((~x) & z); }
22 function G(x,y,z) { return (x & z) | (y & (~z)); }
23 function H(x,y,z) { return (x ^ y ^ z); }
24 function I(x,y,z) { return (y ^ (x | (~z))); }
25 function FF(a,b,c,d,x,s,ac) {
26 a = AddUnsigned(a, AddUnsigned(AddUnsigned(F(b, c, d), x), ac));
27 return AddUnsigned(RotateLeft(a, s), b);
28 }
29 function GG(a,b,c,d,x,s,ac) {
30 a = AddUnsigned(a, AddUnsigned(AddUnsigned(G(b, c, d), x), ac));
31 return AddUnsigned(RotateLeft(a, s), b);
32 }
33 function HH(a,b,c,d,x,s,ac) {
34 a = AddUnsigned(a, AddUnsigned(AddUnsigned(H(b, c, d), x), ac));
35 return AddUnsigned(RotateLeft(a, s), b);
36 }
37 function II(a,b,c,d,x,s,ac) {
38 a = AddUnsigned(a, AddUnsigned(AddUnsigned(I(b, c, d), x), ac));
39 return AddUnsigned(RotateLeft(a, s), b);
40 }
41 function ConvertToWordArray(sMessage) {
42 var lWordCount;
43 var lMessageLength = sMessage.length;
44 var lNumberOfWords_temp1=lMessageLength + 8;
45 var lNumberOfWords_temp2=(lNumberOfWords_temp1-(lNumberOfWords_temp1 % 64))/64;
46 var lNumberOfWords = (lNumberOfWords_temp2+1)*16;
47 var lWordArray=Array(lNumberOfWords-1);
48 var lBytePosition = 0;
49 var lByteCount = 0;
50 while ( lByteCount < lMessageLength ) {
51 lWordCount = (lByteCount-(lByteCount % 4))/4;
52 lBytePosition = (lByteCount % 4)*8;
53 lWordArray[lWordCount] = (lWordArray[lWordCount] | (sMessage.charCodeAt(lByteCount)<<lBytePosition));
54 lByteCount++;
55 }
56 lWordCount = (lByteCount-(lByteCount % 4))/4;
57 lBytePosition = (lByteCount % 4)*8;
58 lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80<<lBytePosition);
59 lWordArray[lNumberOfWords-2] = lMessageLength<<3;
60 lWordArray[lNumberOfWords-1] = lMessageLength>>>29;
61 return lWordArray;
62 }
63 function WordToHex(lValue) {
64 var WordToHexValue="",WordToHexValue_temp="",lByte,lCount;
65 for (lCount = 0;lCount<=3;lCount++) {
66 lByte = (lValue>>>(lCount*8)) & 255;
67 WordToHexValue_temp = "0" + lByte.toString(16);
68 WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length-2,2);
69 }
70 return WordToHexValue;
71 }
72 var x=Array();
73 var k,AA,BB,CC,DD,a,b,c,d
74 var S11=7, S12=12, S13=17, S14=22;
75 var S21=5, S22=9 , S23=14, S24=20;
76 var S31=4, S32=11, S33=16, S34=23;
77 var S41=6, S42=10, S43=15, S44=21;
78 // Steps 1 and 2. Append padding bits and length and convert to words
79 x = ConvertToWordArray(sMessage);
80 // Step 3. Initialise
81 a = 0x67452301; b = 0xEFCDAB89; c = 0x98BADCFE; d = 0x10325476;
82 // Step 4. Process the message in 16-word blocks
83 for (k=0;k<x.length;k+=16) {
84 AA=a; BB=b; CC=c; DD=d;
85 a=FF(a,b,c,d,x[k+0], S11,0xD76AA478);
86 d=FF(d,a,b,c,x[k+1], S12,0xE8C7B756);
87 c=FF(c,d,a,b,x[k+2], S13,0x242070DB);
88 b=FF(b,c,d,a,x[k+3], S14,0xC1BDCEEE);
89 a=FF(a,b,c,d,x[k+4], S11,0xF57C0FAF);
90 d=FF(d,a,b,c,x[k+5], S12,0x4787C62A);
91 c=FF(c,d,a,b,x[k+6], S13,0xA8304613);
92 b=FF(b,c,d,a,x[k+7], S14,0xFD469501);
93 a=FF(a,b,c,d,x[k+8], S11,0x698098D8);
94 d=FF(d,a,b,c,x[k+9], S12,0x8B44F7AF);
95 c=FF(c,d,a,b,x[k+10],S13,0xFFFF5BB1);
96 b=FF(b,c,d,a,x[k+11],S14,0x895CD7BE);
97 a=FF(a,b,c,d,x[k+12],S11,0x6B901122);
98 d=FF(d,a,b,c,x[k+13],S12,0xFD987193);
99 c=FF(c,d,a,b,x[k+14],S13,0xA679438E);
100 b=FF(b,c,d,a,x[k+15],S14,0x49B40821);
101 a=GG(a,b,c,d,x[k+1], S21,0xF61E2562);
102 d=GG(d,a,b,c,x[k+6], S22,0xC040B340);
103 c=GG(c,d,a,b,x[k+11],S23,0x265E5A51);
104 b=GG(b,c,d,a,x[k+0], S24,0xE9B6C7AA);
105 a=GG(a,b,c,d,x[k+5], S21,0xD62F105D);
106 d=GG(d,a,b,c,x[k+10],S22,0x2441453);
107 c=GG(c,d,a,b,x[k+15],S23,0xD8A1E681);
108 b=GG(b,c,d,a,x[k+4], S24,0xE7D3FBC8);
109 a=GG(a,b,c,d,x[k+9], S21,0x21E1CDE6);
110 d=GG(d,a,b,c,x[k+14],S22,0xC33707D6);
111 c=GG(c,d,a,b,x[k+3], S23,0xF4D50D87);
112 b=GG(b,c,d,a,x[k+8], S24,0x455A14ED);
113 a=GG(a,b,c,d,x[k+13],S21,0xA9E3E905);
114 d=GG(d,a,b,c,x[k+2], S22,0xFCEFA3F8);
115 c=GG(c,d,a,b,x[k+7], S23,0x676F02D9);
116 b=GG(b,c,d,a,x[k+12],S24,0x8D2A4C8A);
117 a=HH(a,b,c,d,x[k+5], S31,0xFFFA3942);
118 d=HH(d,a,b,c,x[k+8], S32,0x8771F681);
119 c=HH(c,d,a,b,x[k+11],S33,0x6D9D6122);
120 b=HH(b,c,d,a,x[k+14],S34,0xFDE5380C);
121 a=HH(a,b,c,d,x[k+1], S31,0xA4BEEA44);
122 d=HH(d,a,b,c,x[k+4], S32,0x4BDECFA9);
123 c=HH(c,d,a,b,x[k+7], S33,0xF6BB4B60);
124 b=HH(b,c,d,a,x[k+10],S34,0xBEBFBC70);
125 a=HH(a,b,c,d,x[k+13],S31,0x289B7EC6);
126 d=HH(d,a,b,c,x[k+0], S32,0xEAA127FA);
127 c=HH(c,d,a,b,x[k+3], S33,0xD4EF3085);
128 b=HH(b,c,d,a,x[k+6], S34,0x4881D05);
129 a=HH(a,b,c,d,x[k+9], S31,0xD9D4D039);
130 d=HH(d,a,b,c,x[k+12],S32,0xE6DB99E5);
131 c=HH(c,d,a,b,x[k+15],S33,0x1FA27CF8);
132 b=HH(b,c,d,a,x[k+2], S34,0xC4AC5665);
133 a=II(a,b,c,d,x[k+0], S41,0xF4292244);
134 d=II(d,a,b,c,x[k+7], S42,0x432AFF97);
135 c=II(c,d,a,b,x[k+14],S43,0xAB9423A7);
136 b=II(b,c,d,a,x[k+5], S44,0xFC93A039);
137 a=II(a,b,c,d,x[k+12],S41,0x655B59C3);
138 d=II(d,a,b,c,x[k+3], S42,0x8F0CCC92);
139 c=II(c,d,a,b,x[k+10],S43,0xFFEFF47D);
140 b=II(b,c,d,a,x[k+1], S44,0x85845DD1);
141 a=II(a,b,c,d,x[k+8], S41,0x6FA87E4F);
142 d=II(d,a,b,c,x[k+15],S42,0xFE2CE6E0);
143 c=II(c,d,a,b,x[k+6], S43,0xA3014314);
144 b=II(b,c,d,a,x[k+13],S44,0x4E0811A1);
145 a=II(a,b,c,d,x[k+4], S41,0xF7537E82);
146 d=II(d,a,b,c,x[k+11],S42,0xBD3AF235);
147 c=II(c,d,a,b,x[k+2], S43,0x2AD7D2BB);
148 b=II(b,c,d,a,x[k+9], S44,0xEB86D391);
149 a=AddUnsigned(a,AA); b=AddUnsigned(b,BB); c=AddUnsigned(c,CC); d=AddUnsigned(d,DD);
150 }
151 // Step 5. Output the 128 bit digest
152 var temp= WordToHex(a)+WordToHex(b)+WordToHex(c)+WordToHex(d);
153 return temp.toLowerCase();
154}
diff --git a/content/photoset-props.js b/content/photoset-props.js
new file mode 100644
index 0000000..43dc1b9
--- a/dev/null
+++ b/content/photoset-props.js
@@ -0,0 +1,81 @@
1
2var psetprops = {
3 fireflix: null,
4 photoset: null,
5 pripic: null,
6
7 settitle: null, setdesc: null,
8 primarypic: null,
9 photos: new Array(),
10 init: function() {
11 this.fireflix = window.arguments[0];
12 this.photoset = window.arguments[1];
13 this.settitle = document.getElementById('set_title');
14 this.settitle.value = this.photoset.title;
15 this.setdesc = document.getElementById('set_desc');
16 this.setdesc.value = this.photoset.description;
17 this.primarypic = document.getElementById('primary_picture');
18 this.primarypic.src =
19 this.fireflix.flickr.get_image_url( this.photoset, 't' );
20 this.primarypic.hidden = false;
21 this.picslist = document.getElementById('primary_picture_list');
22
23 var _this = this;
24 this.fireflix.flickr.api_call(
25 {
26 method: 'flickr.photosets.getPhotos',
27 auth_token: 'default',
28 photoset_id: this.photoset.id
29 }, function(xr) {
30 var x = xr.responseXML;
31 var xp = x.evaluate(
32 '/rsp/photoset/photo', x, null,
33 XPathResult.ORDERED_NODE_ITERATOR_TYPE, null );
34 _this.picslist.removeAllItems(); _this.photos= new Array();
35 var n; while(n=xp.iterateNext()) {
36 _this.photos.push(
37 {
38 id: n.getAttribute('id'),
39 secret: n.getAttribute('secret'),
40 server: n.getAttribute('server')
41 }
42 );
43 var ni = _this.picslist.appendItem(
44 n.getAttribute('title'), _this.photos.length-1
45 );
46 ni.setAttribute('command','cmd_select_picture');
47 if(n.getAttribute('isprimary')==1) {
48 _this.picslist.selectedItem = ni;
49 _this.pripic = _this.photos[_this.photos.length-1];
50 }
51 }
52 _this.picslist.hidden = false;
53 }, function() { }
54 );
55 },
56 on_select_picture: function(ev) {
57 var epic = ev.explicitOriginalTarget;
58 this.picslist.selectedItem = epic;
59 var pic = this.photos[this.picslist.selectedItem.value];
60 this.pripic = pic;
61 this.primarypic.src =
62 this.fireflix.flickr.get_photo_url(
63 pic.server,
64 pic.id,
65 pic.secret,
66 't'
67 );
68 },
69 on_accept: function() {
70 this.photoset.title =
71 document.getElementById('set_title').value;
72 this.photoset.description =
73 document.getElementById('set_desc').value;
74 this.photoset.server = this.pripic.server;
75 this.photoset.primary = this.pripic.id;
76 this.photoset.secret = this.pripic.secret;
77 this.photoset.dirty = true;
78 return;
79 }
80};
81
diff --git a/content/photoset-props.xul b/content/photoset-props.xul
new file mode 100644
index 0000000..e8f6d13
--- a/dev/null
+++ b/content/photoset-props.xul
@@ -0,0 +1,38 @@
1<?xml version="1.0" encoding="utf-8"?>
2<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
3<?xsml-stylesheet href="fireflix.css" type="text/css"?>
4<!DOCTYPE dialog SYSTEM "chrome://fireflix/locale/fireflix.dtd">
5<dialog
6 xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
7 id="photoset_props"
8 buttons="accept,cancel"
9 defaultbutton="accept"
10 title="&photosetprops.title;"
11 onload="psetprops.init()"
12 ondialogaccept="psetprops.on_accept()"
13 >
14
15 <script src="photoset-props.js" type="application/x-javascript"/>
16
17 <commandset>
18 <command id="cmd_select_picture"
19 oncommand="psetprops.on_select_picture(event)"/>
20 </commandset>
21
22 <hbox class="wholething">
23 <vbox>
24 <menulist id="primary_picture_list" hidden="true" sizetopopup="always"/>
25 <hbox pack="center">
26 <box width="100" pack="center">
27 <image id="primary_picture" hidden="true"/>
28 </box>
29 </hbox>
30 </vbox>
31 <vbox flex="1" minwidth="300">
32 <label control="set_title" value="&photosetprops.set_title.label;"/>
33 <textbox id="set_title" />
34 <label control="set_desc" value="&photosetprops.set_desc.label;"/>
35 <textbox id="set_desc" multiline="true" rows="5" />
36 </vbox>
37 </hbox>
38</dialog>
diff --git a/install.rdf.in b/install.rdf.in
new file mode 100644
index 0000000..73c9d37
--- a/dev/null
+++ b/install.rdf.in
@@ -0,0 +1,28 @@
1<?xml version="1.0"?>
2<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
3 xmlns:em="http://www.mozilla.org/2004/em-rdf#">
4
5 <Description about="urn:mozilla:install-manifest">
6
7 <em:id>@MOZ_EXT_ID@</em:id>
8 <em:name>Fireflix</em:name>
9 <em:version>@VERSION@</em:version>
10 <em:description>Flickr management tool</em:description>
11 <em:creator>Klever Group; http://www.klever.net/</em:creator>
12 <em:homepageURL>http://kin.klever.net/</em:homepageURL>
13 <em:iconURL>chrome://fireflix/content/icons/32x32/fireflix.png</em:iconURL>
14 <em:updateURL>http://kin.klever.net/fireflix/update?v=@VERSION@</em:updateURL>
15 <em:aboutURL>chrome://fireflix/content/about.xul</em:aboutURL>
16
17 <!-- Firefox -->
18 <em:targetApplication>
19 <Description>
20 <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
21 <em:minVersion>1.5</em:minVersion>
22 <em:maxVersion>1.5.0.7</em:maxVersion>
23 </Description>
24 </em:targetApplication>
25
26 </Description>
27
28</RDF>
diff --git a/locale/Makefile.am b/locale/Makefile.am
new file mode 100644
index 0000000..0e62920
--- a/dev/null
+++ b/locale/Makefile.am
@@ -0,0 +1,10 @@
1LOCALES=en-US
2nobase_xpichromelocale_DATA = \
3 $(addsuffix /fireflix.dtd, \
4 ${LOCALES} \
5 ) \
6 $(addsuffix /fireflix.properties, \
7 ${LOCALES} \
8 )
9
10EXTRA_DIST = ${nobase_xpichromelocale_DATA}
diff --git a/locale/en-US/fireflix.dtd b/locale/en-US/fireflix.dtd
new file mode 100644
index 0000000..2b6e72a
--- a/dev/null
+++ b/locale/en-US/fireflix.dtd
@@ -0,0 +1,79 @@
1<!ENTITY % autoconf SYSTEM "chrome://fireflix/content/autoconf.dtd">
2%autoconf;
3
4<!-- About Box -->
5
6<!ENTITY aboutFireflix "About Fireflix" >
7<!ENTITY about.ok.label "OK">
8<!ENTITY about.license.label "License">
9<!ENTITY about.license.tip "Show copying policy">
10
11<!-- COPYING -->
12
13<!ENTITY copying.title "Filreflix: copying policy">
14
15<!-- Sidebar -->
16
17<!ENTITY panel.auth_info "Authorization info">
18<!ENTITY panel.no_auth_info "No auth info available">
19<!ENTITY panel.auth_button "Authorize">
20<!ENTITY panel.auth_complete_button "Authorization complete">
21<!ENTITY panel.flickr_button.label "Flickr">
22<!ENTITY panel.flickr_button.tip "Open Flickr in new tab">
23
24<!ENTITY panel.tabs.search "Search" >
25<!ENTITY panel.tabs.sets "Sets" >
26<!ENTITY panel.tabs.tags "Tags" >
27<!ENTITY panel.tabs.upload "Upload" >
28
29<!ENTITY panel.search.cmd_search.label "Search" >
30<!ENTITY panel.search.search_for.label "Search for:" >
31<!ENTITY panel.search.mode.tagsonly.label "tags">
32<!ENTITY panel.search.mode.tagsonly.tip "Search tags only">
33<!ENTITY panel.search.mode.mine.label "mine">
34<!ENTITY panel.search.col.title.label "Title">
35<!ENTITY panel.search.cmd_search_open.label "Open">
36
37<!ENTITY panel.sets.name.label "Set">
38<!ENTITY panel.sets.name.tip "Photoset name">
39<!ENTITY panel.sets.photos.label "Photos">
40<!ENTITY panel.sets.photos.tip "Number of photos in set">
41
42<!ENTITY panel.sets.cmd_refresh_sets "Refresh">
43<!ENTITY panel.sets.cmd_properties "Properties">
44
45<!ENTITY panel.sets.generate_html "Generate HTML">
46
47<!ENTITY panel.setphotos.title.label "Title">
48<!ENTITY panel.setphotos.title.tip "Picture title">
49<!ENTITY panel.setphotos.taken.label "Taken">
50<!ENTITY panel.setphotos.taken.tip "When the picture was taken">
51<!ENTITY panel.setphotos.upload.label "Uploaded">
52<!ENTITY panel.setphotos.upload.tip "When the picure was uploaded">
53
54<!ENTITY panel.tagslist.tag.label "Tag">
55
56<!ENTITY panel.uploadlist.file.label "File name">
57<!ENTITY panel.uploadlist.title.label "Title">
58<!ENTITY panel.uploadlist.status.label "Status">
59
60<!ENTITY panel.upload_props.filename.label "File:">
61<!ENTITY panel.upload_props.title.label "Title:">
62<!ENTITY panel.upload_props.tags.label "Tags:">
63
64<!ENTITY panel.uploads.upload.label "Upload">
65<!ENTITY panel.uploads.clear.label "Clear">
66<!ENTITY panel.uploads.remove.label "Remove">
67<!ENTITY panel.uploads.add.label "Add">
68<!ENTITY panel.uploads.generate_html "Generate HTML">
69
70<!ENTITY generated.title "Fireflix: Generated content">
71<!ENTITY generated.copy "copy">
72
73<!ENTITY browser.sidebar.label "Fireflix">
74<!ENTITY browser.sidebar.title "Fireflix">
75
76<!ENTITY photosetprops.title "Photoset properties">
77<!ENTITY photosetprops.set_title.label "Photoset title:">
78<!ENTITY photosetprops.set_desc.label "Photoset description:">
79
diff --git a/locale/en-US/fireflix.properties b/locale/en-US/fireflix.properties
new file mode 100644
index 0000000..7caa12f
--- a/dev/null
+++ b/locale/en-US/fireflix.properties
@@ -0,0 +1,11 @@
1postUploadPhotoset=Create a new photoset for uploaded photos (cancel if you don't want to create a photoset)
2
3menutitle_Images=Images
4menutitle_Links=Linked to…
5urltype_s=Small square (75x75)
6urltype_t=Thumbnail (fits in 100x100)
7urltype_m=Small (fits in 240x240)
8urltype__=Medium (fits in 500x500)
9urltype_b=Large (fits in 1024x1024)
10urltype_o=Original image
11urltype_p=Flickr photo URL
diff --git a/update.rdf.in b/update.rdf.in
new file mode 100644
index 0000000..7a78470
--- a/dev/null
+++ b/update.rdf.in
@@ -0,0 +1,26 @@
1<?xml version="1.0"?>
2<RDF:RDF
3 xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
4 xmlns:em="http://www.mozilla.org/2004/em-rdf#">
5
6 <RDF:Description about="urn:mozilla:extension:@MOZ_EXT_ID@">
7 <em:updates>
8 <RDF:Seq>
9 <RDF:li resource="urn:mozilla:extension:@MOZ_EXT_ID@:@VERSION@"/>
10 </RDF:Seq>
11 </em:updates>
12 </RDF:Description>
13
14 <RDF:Description about="urn:mozilla:extension:@MOZ_EXT_ID@:@VERSION@">
15 <em:version>@VERSION@</em:version>
16 <em:targetApplication>
17 <Description>
18 <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
19 <em:minVersion>1.5</em:minVersion>
20 <em:maxVersion>1.5.0.7</em:maxVersion>
21 <em:updateLink>http://kin.klever.net/dist/@PACKAGE@-@VERSION@.xpi</em:updateLink>
22 </Description>
23 </em:targetApplication>
24 </RDF:Description>
25
26</RDF:RDF>