summaryrefslogtreecommitdiffabout
authorMichael Krelin <hacker@klever.net>2006-09-26 19:21:57 (UTC)
committer Michael Krelin <hacker@klever.net>2006-09-26 19:21:57 (UTC)
commitc5ea6ff7abd6e376ae151c9724d24f6fe156766e (patch) (side-by-side diff)
tree0df2e4ecf02f89c4b85d1950b5c6f4fd2f67f9bd
parent5207247fdc837ae9de1d81db903f6cd92532595e (diff)
downloadfireflix-c5ea6ff7abd6e376ae151c9724d24f6fe156766e.zip
fireflix-c5ea6ff7abd6e376ae151c9724d24f6fe156766e.tar.gz
fireflix-c5ea6ff7abd6e376ae151c9724d24f6fe156766e.tar.bz2
Initial import into public repository0.0
git-svn-id: http://svn.klever.net/kin/fireflix/trunk@159 fe716a7a-6dde-0310-88d9-d003556173a8
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 @@
+Klever dissected:
+ Michael 'hacker' Krelin <hacker@klever.net>
+ 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 @@
+Copyright (c) 2006 Klever Group (http://www.klever.net/)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
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 @@
+SUBDIRS = content locale
+
+xpi_DATA = \
+ install.rdf \
+ AUTHORS COPYING NEWS
+
+EXTRA_DIST = NEWS NEWS.xml NEWS.xsl chrome.manifest
+
+XPI=${PACKAGE}-${VERSION}.xpi
+
+xpi: ${XPI}
+
+${XPI}: install
+ cd ${xpichromedir} \
+ && ${ZIP} -r -m ${PACKAGE}.jar */
+ cd ${xpidir} \
+ && ${ZIP} -r @abs_builddir@/$@ .
+
+install-data-local: ${xpidir}/chrome.manifest
+
+${xpidir}/chrome.manifest: chrome.manifest Makefile
+ sed \
+ -e 's,^content[[:space:]]\+\([^[:space:]]\+\)[[:space:]]\+\([^[:space:]]\+\)$$,content \1 jar:chrome/${PACKAGE}.jar!/\2,' \
+ -e 's,^locale[[:space:]]\+\([^[:space:]]\+\)[[:space:]]\+\([^[:space:]]\+\)[[:space:]]\+\([^[:space:]]\+\)$$,locale \1 \2 jar:chrome/${PACKAGE}.jar!/\3,' \
+ $< >$@
+
+clean-local:
+ rm -rf ${xpidir} ${XPI}
+
+all-local: NEWS
+
+NEWS: NEWS.xsl NEWS.xml
+ ${XSLTPROC} -o $@ NEWS.xsl NEWS.xml
+
+mozextptr: ${MOZ_EXT_ID}
+${MOZ_EXT_ID}:
+ 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 @@
+0.0 (September 26th, 2006)
+ - 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 @@
+<?xml version="1.0" encoding="us-ascii"?>
+<news>
+ <version version="0.0" date="September 26th, 2006">
+ <ni>Initial release</ni>
+ </version>
+</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 @@
+<?xml version="1.0" encoding="us-ascii"?>
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ >
+ <xsl:output
+ method="text"
+ encoding="us-ascii"
+ media-type="text/plain" />
+
+ <xsl:template match="news">
+ <xsl:apply-templates/>
+ </xsl:template>
+ <xsl:template match="version">
+ <xsl:value-of select="concat(@version,' (',@date,')&#xA;')"/>
+ <xsl:apply-templates/>
+ </xsl:template>
+ <xsl:template match="ni">
+ <xsl:text> - </xsl:text>
+ <xsl:apply-templates mode="text"/>
+ <xsl:text>&#xA;</xsl:text>
+ </xsl:template>
+ <xsl:template match="*|text()"/>
+
+</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 @@
+#!/bin/sh
+WANT_AUTOMAKE=1.8
+export WANT_AUTOMAKE
+aclocal \
+&& automake -a \
+&& autoconf \
+&& ./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 @@
+content fireflix content/
+
+overlay chrome://browser/content/browser.xul chrome://fireflix/content/browser.xul
+
+locale 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 @@
+AC_INIT([fireflix], [0.0], [fireflix-bugs@klever.net])
+AC_CONFIG_SRCDIR([install.rdf.in])
+AM_INIT_AUTOMAKE([dist-bzip2])
+AC_SUBST([MOZ_EXT_ID],[{4269f719-86de-4668-b8ad-04752c23a69e}])
+
+AC_PATH_PROG([ZIP],[zip],[false])
+if test "${ZIP}" = "false" ; then
+ AC_MSG_ERROR([zip is required to produce packaged extension])
+fi
+AC_PATH_PROG([XSLTPROC],[xsltproc],[true])
+
+AC_SUBST([xpidir],[\${top_builddir}/xpi])
+AC_SUBST([xpichromedir],[\${xpidir}/chrome])
+AC_SUBST([xpichromecontentdir],[\${xpichromedir}/content])
+AC_SUBST([xpichromelocaledir],[\${xpichromedir}/locale])
+
+COPYING="`sed -e 's/\\"/\\&quot;/g' -e 's,$,<br/>,g' COPYING|tr '\n' ' '`"
+AC_SUBST([COPYING])
+
+AC_CONFIG_FILES([
+ Makefile
+ install.rdf
+ update.rdf
+ content/Makefile
+ content/autoconf.dtd
+ locale/Makefile
+])
+AC_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 @@
+xpichromecontent_DATA = \
+ autoconf.dtd \
+ browser.xul about.xul fireflix-panel.xul photoset-props.xul \
+ generated-content.xul \
+ photoset-props.js fireflix.js flickr.js md5.js \
+ generated-content.js \
+ fireflix.css \
+ background.jpeg
+
+sized_icons = \
+ $(addsuffix .png, \
+ fireflix \
+ )
+
+nobase_xpichromecontent_DATA = \
+ $(addprefix icons/, \
+ $(addprefix 16x16/,${sized_icons}) \
+ $(addprefix 32x32/,${sized_icons}) \
+ )
+
+EXTRA_DIST = \
+ ${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 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="fireflix.css" type="text/css"?>
+<!DOCTYPE window SYSTEM "chrome://fireflix/locale/fireflix.dtd">
+<window
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ id="aboutFireflix" title="&aboutFireflix;"
+ orient="vertical"
+ width="400" height="200"
+ >
+
+ <hbox class="about wholething" flex="1">
+ <hbox flex="1" class="insides">
+ <vbox>
+ <spacer flex="1"/>
+ <image src="icons/32x32/fireflix.png" width="32" height="32" />
+ <spacer flex="1"/>
+ </vbox>
+ <groupbox flex="1" class="text" orient="vertical">
+ <spacer flex="2"/>
+ <hbox>
+ <spacer flex="1"/>
+ <label value="Fireflix &autoconf.version;" class="title"/>
+ <spacer flex="1"/>
+ </hbox>
+ <spacer flex="1"/>
+ <hbox>
+ <label value="Copyright © 2006 "/>
+ <label value="Klever Group" class="link"
+ onclick="window.openDialog('chrome://browser/content/browser.xul','_blank','chrome,all,dialog=no','http://www.klever.net/',null,null)" />
+ </hbox>
+ <spacer flex="2"/>
+ </groupbox>
+ <vbox>
+ <spacer flex="10" />
+ <button label="&about.license.label;" tooltiptext="&about.license.tip;"
+ oncommand="window.openDialog('chrome://fireflix/content/copying.xul','copying')" />
+ <spacer flex="2" />
+ <button label="OK" oncommand="window.close()"/>
+ <spacer flex="1" />
+ </vbox>
+ </hbox>
+ </hbox>
+
+</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 @@
+<!ENTITY autoconf.version "@VERSION@">
+<!ENTITY autoconf.package "@PACKAGE@">
+<!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 @@
+<?xml version="1.0"?>
+<!DOCTYPE overlay SYSTEM "chrome://fireflix/locale/fireflix.dtd">
+<overlay id="fireflixOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" >
+
+ <menupopup id="viewSidebarMenu">
+ <menuitem observes="viewFireflixSidebar" />
+ </menupopup>
+
+ <broadcasterset id="mainBroadcasterSet">
+ <broadcaster id="viewFireflixSidebar"
+ autoCheck="false" label="&browser.sidebar.label;"
+ type="checkbox" group="sidebar"
+ sidebarurl="chrome://fireflix/content/fireflix-panel.xul"
+ sidebartitle="&browser.sidebar.title;"
+ oncommand="toggleSidebar('viewFireflixSidebar')" />
+ </broadcasterset>
+</overlay>
+
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 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="fireflix.css" type="text/css"?>
+<!DOCTYPE dialog SYSTEM "chrome://fireflix/locale/fireflix.dtd">
+<dialog
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ id="copying" title="&copying.title;" orient="vertical"
+ buttons="accept">
+
+ <div xmlns="http://www.w3.org/1999/xhtml">
+ &autoconf.copying;
+ </div>
+
+</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 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="fireflix.css" type="text/css"?>
+<!DOCTYPE page SYSTEM "chrome://fireflix/locale/fireflix.dtd">
+<page
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ id="fireflixwindow" title="Fireflix"
+ onload="fireflix.init()"
+ orient="vertical"
+ ondragover="nsDragAndDrop.dragOver(event,fireflix.uploadObserver)"
+ ondragdrop="nsDragAndDrop.drop(event,fireflix.uploadObserver)"
+ >
+
+ <script src="chrome://global/content/nsDragAndDrop.js"/>
+ <script src="chrome://global/content/nsTransferable.js"/>
+ <script type="application/x-javascript" src="md5.js" />
+ <script type="application/x-javascript" src="flickr.js" />
+ <script type="application/x-javascript" src="fireflix.js" />
+
+ <stringbundleset>
+ <stringbundle id="loc_strings" src="chrome://fireflix/locale/fireflix.properties" />
+ </stringbundleset>
+
+ <commandset id="cmdset_search">
+ <command id="cmd_search" label="&panel.search.cmd_search.label;"
+ oncommand="fireflix.foundphotos.search_photos()"/>
+ <command id="cmd_search_open" label="&panel.search.cmd_search_open.label;"
+ oncommand="fireflix.foundphotos.on_cmd_open(event)" />
+ </commandset>
+
+ <commandset id="cmdset_sets">
+ <command id="cmd_refresh_sets" label="&panel.sets.cmd_refresh_sets;"
+ oncommand="fireflix.on_refresh_sets()" />
+ <command id="cmd_set_props" label="&panel.sets.cmd_properties;"
+ oncommand="fireflix.on_set_props()" disabled="true" />
+ </commandset>
+
+ <popupset>
+ <popup id="sets_menu">
+ <menuitem command="cmd_set_props"/>
+ <menuitem command="cmd_refresh_sets"/>
+ <menuseparator/>
+ <menu label="&panel.sets.generate_html;" id="sets_html_menu"/>
+ </popup>
+ </popupset>
+
+ <commandset id="cmdset_uploads">
+ <command id="cmd_uploads_clear" label="&panel.uploads.clear.label;"
+ oncommand="fireflix.uploads.on_clear()" />
+ <command id="cmd_uploads_upload" label="&panel.uploads.upload.label;"
+ oncommand="fireflix.uploads.on_upload()" />
+ <command id="cmd_uploads_remove" label="&panel.uploads.remove.label;"
+ oncommand="fireflix.uploads.on_remove()" />
+ <command id="cmd_uploads_add" label="&panel.uploads.add.label;"
+ oncommand="fireflix.uploads.on_add()" />
+ </commandset>
+
+ <popupset>
+ <popup id="uploads_menu">
+ <menuitem command="cmd_uploads_add"/>
+ <menuitem command="cmd_uploads_clear"/>
+ <menuitem command="cmd_uploads_remove"/>
+ <menuseparator/>
+ <menuitem command="cmd_uploads_upload"/>
+ <menuseparator/>
+ <menu label="&panel.uploads.generate_html;" id="uploads_html_menu"/>
+ </popup>
+ </popupset>
+
+ <vbox class="wholething" flex="1">
+
+ <groupbox>
+ <caption label="&panel.auth_info;"/>
+ <hbox>
+ <label id="auth_info" value="&panel.no_auth_info;" flex="1" disabled="true"/>
+ <button id="b_auth" label="&panel.auth_button;" oncommand="fireflix.on_auth()"/>
+ <button id="b_auth_done" label="&panel.auth_complete_button;" hidden="true"
+ oncommand="fireflix.on_auth_done()"/>
+ <button label="&panel.flickr_button.label;"
+ tooltiptext="&panel.flickr_button.tip;"
+ oncommand="fireflix.openTab('http://www.flickr.com/')" />
+ </hbox>
+ </groupbox>
+
+ <tabbox flex="1" id="fireflix_tabs">
+
+ <tabs>
+ <tab label="&panel.tabs.search;"/>
+ <tab label="&panel.tabs.sets;"/>
+ <tab label="&panel.tabs.tags;" hidden="true"/> <!-- TODO: -->
+ <tab id="tab_upload" label="&panel.tabs.upload;"/>
+ </tabs>
+
+ <tabpanels flex="1">
+
+ <tabpanel id="tabpanel_search" flex="1">
+ <vbox flex="1">
+ <groupbox class="search_params" orient="vertical" onkeypress="if(event.keyCode==event.DOM_VK_RETURN) fireflix.foundphotos.search_photos()">
+ <hbox>
+ <label control="search_for" value="&panel.search.search_for.label;"
+ accesskey="s"/>
+ <textbox id="search_for" flex="1"/>
+ </hbox>
+ <hbox>
+ <checkbox id="search_tags" label="&panel.search.mode.tagsonly.label;"
+ tooltiptext="&panel.search.mode.tagsonly.tip;" checked="false"
+ accesskey="t" />
+ <checkbox id="search_mine" label="&panel.search.mode.mine.label;" checked="true" accesskey="m"/>
+ <spacer flex="1"/>
+ <button command="cmd_search"/>
+ </hbox>
+ </groupbox>
+ <tree id="searchresults" rows="4" flex="1"
+ onselect="fireflix.foundphotos.on_select()"
+ ondblclick="fireflix.foundphotos.on_cmd_open(event)"
+ onkeypress="if(event.keyCode==event.DOM_VK_RETURN)
+ fireflix.foundphotos.on_cmd_open(event)">
+ <treecols>
+ <treecol id="sr_title" label="&panel.search.col.title.label;" flex="2" crop="end" align="start" />
+ </treecols>
+ <treechildren/>
+ </tree>
+ <groupbox id="searchresult_props" orient="horizontal" hidden="true">
+ <vbox width="100" pack="center">
+ <hbox pack="center">
+ <image id="search_photo"/>
+ </hbox>
+ </vbox>
+ <vbox flex="1">
+ <label id="searchresult_title"/>
+ <textbox flex="1" multiline="true" class="plain" readonly="true" id="searchresult_description"/>
+ <hbox pack="end">
+ <button command="cmd_search_open"/>
+ </hbox>
+ </vbox>
+ </groupbox>
+ </vbox>
+ </tabpanel>
+
+ <tabpanel id="tabpanel_sets" flex="1"
+ onkeypress="if(event.keyCode==event.DOM_VK_RETURN)
+ document.getElementById('setphotos').focus()">
+ <vbox flex="1">
+ <tree id="setslist" rows="4" onselect="fireflix.photosets.on_select()"
+ flex="1" context="sets_menu"
+ >
+ <treecols>
+ <treecol id="sl_name" label="&panel.sets.name.label;" flex="4" crop="end" align="start" tooltiptext="&panel.sets.name.tip;"/>
+ <splitter class="tree-splitter" />
+ <treecol id="sl_photos" label="&panel.sets.photos.label;" flex="1" align="end" tooltiptext="&panel.sets.photos.tip;" />
+ </treecols>
+ <treechildren/>
+ </tree>
+ <hbox>
+ <button command="cmd_refresh_sets" />
+ <button command="cmd_set_props" />
+ </hbox>
+ <tree id="setphotos" rows="4" onselect="fireflix.photoset.on_select()"
+ flex="1">
+ <treecols>
+ <treecol id="sp_title" label="&panel.setphotos.title.label;" flex="1" crop="end" align="start" tooltiptext="&panel.setphotos.title.tip;" />
+ <splitter class="tree-splitter" />
+ <treecol id="sp_taken" label="&panel.setphotos.taken.label;" crop="end" align="start" tooltiptext="&panel.setphotos.taken.tip;" hidden="true" />
+ <treecol id="sp_upload" label="&panel.setphotos.upload.label;" crop="end" align="start" tooltiptext="&panel.setphotos.upload.tip;" hidden="true" />
+ </treecols>
+ <treechildren/>
+ </tree>
+ <groupbox id="set_photo_props" orient="horizontal">
+ <vbox width="100" pack="center">
+ <hbox pack="center">
+ <image id="set_photo" hidden="true"/>
+ </hbox>
+ </vbox>
+ <spacer flex="1"/>
+ </groupbox>
+ </vbox>
+ </tabpanel>
+
+ <tabpanel id="tabpanel_tags">
+ <listbox id="tagslist" rows="8" flex="1">
+ <listhead>
+ <listheader label="&panel.tagslist.tag.label;"/>
+ </listhead>
+ <listcols>
+ <listcol flex="1"/>
+ </listcols>
+ </listbox>
+ </tabpanel>
+
+ <tabpanel id="tabpanel_upload">
+ <vbox flex="1">
+ <tree id="uploadlist" rows="8" flex="1"
+ onselect="fireflix.uploads.selectionChanged()"
+ context="uploads_menu">
+ <treecols>
+ <treecol id="up_file" label="&panel.uploadlist.file.label;" flex="4" crop="start" align="start"/>
+ <splitter class="tree-splitter" />
+ <treecol id="up_title" label="&panel.uploadlist.title.label;" flex="5" crop="end" align="start" />
+ <splitter class="tree-splitter" />
+ <treecol id="up_status" label="&panel.uploadlist.status.label;" flex="1" crop="end" align="start" />
+ </treecols>
+ <treechildren/>
+ </tree>
+ <progressmeter id="upload_progress" mode="undetermined" hidden="true" />
+ <groupbox id="upload_file_props" orient="horizontal" hidden="true">
+ <image id="upload_file_preview" width="100" height="100" />
+ <grid flex="1">
+ <columns>
+ <column/>
+ <column flex="1"/>
+ </columns>
+ <rows>
+ <row>
+ <label control="upload_filename"
+ value="&panel.upload_props.filename.label;" />
+ <textbox id="upload_filename"
+ oninput="fireflix.uploads.propsToSel('filename')"/>
+ </row>
+ <row>
+ <label control="upload_title" value="&panel.upload_props.title.label;" />
+ <textbox id="upload_title"
+ oninput="fireflix.uploads.propsToSel('title')"/>
+ </row>
+ <row>
+ <label control="uplod_tags" value="&panel.upload_props.tags.label;" />
+ <textbox id="upload_tags"
+ oninput="fireflix.uploads.propsToSel('tags')"/>
+ </row>
+ <!-- TODO: description, public, friend, family -->
+ </rows>
+ </grid>
+ </groupbox>
+ <hbox>
+ <button command="cmd_uploads_add" />
+ <spacer flex="1"/>
+ <button command="cmd_uploads_remove" />
+ <spacer flex="1"/>
+ <button command="cmd_uploads_clear" />
+ </hbox>
+ <hbox pack="center">
+ <button command="cmd_uploads_upload" flex="1"/>
+ </hbox>
+ </vbox>
+ </tabpanel>
+
+ </tabpanels>
+
+ </tabbox>
+
+ </vbox>
+
+</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 @@
+.generated.wholething,
+.about.wholething,
+tabbox, tabpanels, tabpanel {
+ background: url("background.jpeg");
+}
+tabpanels {
+ padding: 0px;
+}
+
+tree {
+ margin-top: 2px;
+ background: rgb(12,167,0);
+ color: rgb(255,255,0);
+ font-size: 90%;
+}
+tree treechildren { /* for windows */
+ background: rgb(12,167,0);
+}
+
+tree#uploadlist treechildren::-moz-tree-cell-text(pending) {
+}
+tree#uploadlist treechildren::-moz-tree-cell-text(completed) {
+ color: white;
+}
+tree#uploadlist treechildren::-moz-tree-row(failed) {
+ background: yellow;
+}
+tree#uploadlist treechildren::-moz-tree-cell-text(failed) {
+ color: red;
+}
+tree#uploadlist treechildren::-moz-tree-cell-text(uploading) {
+ font-weight: bold;
+}
+
+groupbox#searchresult_props,
+groupbox.search_params,
+groupbox#upload_file_props,
+groupbox#set_props,
+groupbox#set_photo_props {
+ background: white;
+}
+
+groupbox#upload_file_props label {
+ text-align: right;
+}
+
+image#set_photo, image#set_primary {
+ border: black 1px solid;
+}
+
+.about .insides {
+ margin: 1ex;
+}
+.about .text {
+ border: yellow solid 1px;
+ background: green;
+}
+.about .title {
+ font-size: 300%;
+ font-weight: bold;
+ color: yellow;
+}
+.about .link {
+ text-decoration: underline;
+ color: white;
+ cursor: pointer;
+}
+
+menuitem.menuhead {
+ background: gray;
+ color: black;
+ font-weight: bold;
+}
+
+label#searchresult_description {
+ font-weight: bold;
+}
+textbox#searchresult_description {
+ padding: 1px 3px !important;
+ background: white;
+}
+
+#copying div {
+ margin: 1ex 1em;
+ font-family: courier, monospace;
+ font-size: 9pt;
+ padding: 2px;
+ border: dotted 1px gray;
+ background: white;
+}
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 @@
+function splitascii(s) {
+ var rv='';
+ for(var i=0;i<s.length;++i) {
+ var w = s.charCodeAt(i);
+ rv += String.fromCharCode(
+ w&0xff, (w>>8)&0xff );
+ }
+ return rv;
+}
+
+
+var fireflix = {
+ flickr: new Flickr(),
+ init: function() {
+ this.loc_strings = document.getElementById('loc_strings');
+ this.build_menus();
+ this.cmd_set_props = document.getElementById('cmd_set_props');
+ this.foundphotos.init(this);
+ this.photosets.init(this);
+ this.photoset.init(this);
+ this.uploads.init(this);
+ this.uploadObserver.init(this);
+ this.flickr.api_key = '9c43cd66947a57e6f29db1a9da3f72e3';
+ this.flickr.api_shs = '9c33c9e2f0f0cfd5';
+ this.flickr.prefs_root = 'net.klever.kin.fireflix';
+ this.flickr.load_token();
+ document.getElementById('setslist').view = this.photosets;
+ document.getElementById('setphotos').view = this.photoset;
+ document.getElementById('uploadlist').view = this.uploads;
+ this.flickr.no_auth_info_label = document.getElementById('auth_info').value;
+ if(this.flickr.token) {
+ this.refresh_stuff();
+ document.getElementById('auth_info').value =
+ this.flickr.user.fullname+' ['+this.flickr.user.username+']';
+ document.getElementById('auth_info').disabled = false;
+ document.getElementById('b_auth').hidden = true;
+ }
+ },
+ on_auth: function() {
+ var _this = this;
+ this.flickr.authorize_0(
+ function() {
+ document.getElementById('b_auth').hidden = true;
+ document.getElementById('b_auth_done').hidden = false;
+ }, function(x,s,c,m) {
+ _this.flickr_failure(x,s,c,m);
+ }
+ );
+ },
+ on_auth_done: function() {
+ document.getElementById('b_auth_done').hidden = true;
+ var _this = this;
+ this.flickr.authorize_1(
+ function() {
+ _this.flickr.save_token();
+ _this.refresh_stuff();
+ document.getElementById('auth_info').value =
+ _this.flickr.user.fullname+' ['+_this.flickr.user.username+']';
+ document.getElementById('auth_info').disabled = false;
+ }, function(x,s,c,m) {
+ document.getElementById('b_auth').hidden = false;
+ _this.flickr_failure(x,s,c,m);
+ }
+ );
+ },
+
+ refresh_sets: function() { this.photosets.refresh_sets(); },
+ refresh_stuff: function() {
+ this.refresh_sets();
+ this.refresh_user_tags();
+ },
+
+ /* photoset treeview */
+ photoset: {
+ photos: new Array(),
+ fireflix: null,
+ init: function(f) {
+ this.fireflix = f;
+ },
+ rowCount: 0,
+ getCellText: function(r,c) {
+ var p = this.photos[r];
+ if(c.id=='sp_title') return p.title;
+ if(c.id=='sp_taken') return p.datetaken;
+ if(c.id=='sp_upload') return p.dateupload; /* TODO: unixtime conversion */
+ return c.id;
+ },
+ setTree: function(t) { this.tree = t },
+ isContainer: function(r) { return false; },
+ isSeparator: function(r) { return false; },
+ isSorted: function(r) { return false; },
+ getLevel: function(r) { return 0; },
+ getImageSrc: function(r,c) { return null },
+ getRowProperties: function(r,p) {},
+ getCellProperties: function(cid,cel,p) {},
+ getColumnProperties: function(cid,cel,p) { },
+ cycleHeader: function(cid,e) { },
+ getParentIndex: function(r) { return -1; },
+ drop: function(r,o) { },
+ canDropBeforeAfter: function(r,b) { return false },
+
+ importXPR: function(xp) {
+ this.tree.beginUpdateBatch();
+ this.photos = new Array();
+ var n; while(n=xp.iterateNext()) {
+ this.photos.push(new Photo(n));
+ }
+ this.rowCount = this.photos.length;
+ this.tree.endUpdateBatch();
+ },
+ load_photos: function(psid) {
+ var _this = this;
+ this.fireflix.flickr.api_call(
+ {
+ method: 'flickr.photosets.getPhotos',
+ auth_token: 'default',
+ photoset_id: psid,
+ extras: 'license,date_upload,date_taken,owner_name,icon_server,original_format,last_update'
+ }, function(xr) {
+ var x = xr.responseXML;
+ var xp = x.evaluate(
+ '/rsp/photoset/photo', x, null,
+ XPathResult.ORDERED_NODE_ITERATOR_TYPE, null );
+ _this.importXPR(xp);
+ }, function(x,s,c,m) {
+ _this.fireflix.flickr_failure(x,s,c,m);
+ }
+ );
+ },
+ on_select: function() {
+ if(this.selection.count==1) {
+ var p = this.photos[this.selection.currentIndex];
+ document.getElementById('set_photo').src =
+ this.fireflix.flickr.get_photo_url(p.server,p.id,p.secret,'t');
+ document.getElementById('set_photo').hidden = false;
+ }else{
+ document.getElementById('set_photo').hidden = true;
+ }
+ }
+ },
+
+ /* photosets treeview */
+ photosets: {
+ sets: new Array(),
+ fireflix: null,
+ init: function(f) {
+ this.fireflix = f;
+ },
+ rowCount: 0,
+ getCellText: function(r,c) {
+ var s = this.sets[r];
+ if(c.id=='sl_name') return s.title;
+ if(c.id=='sl_photos') return s.photos;
+ return c.id;
+ },
+ setTree: function(t) { this.tree = t },
+ isContainer: function(r) { return false; },
+ isSeparator: function(r) { return false; },
+ isSorted: function() { return false; },
+ getLevel: function(r) { return 0; },
+ getImageSrc: function(r,c) { return null },
+ getRowProperties: function(r,p) {},
+ getCellProperties: function(cid,cel,p) { },
+ getColumnProperties: function(cid,cel,p) { },
+ cycleHeader: function(cid,e) { },
+ getParentIndex: function(r) { return -1; },
+ drop: function(r,o) { },
+ canDropBeforeAfter: function(r,b) { return false },
+
+ importXPR: function(xp) {
+ this.tree.beginUpdateBatch();
+ this.sets = new Array();
+ var n; while(n=xp.iterateNext()) {
+ this.sets.push(new Photoset(n));
+ }
+ this.rowCount = this.sets.length;
+ this.tree.endUpdateBatch();
+ },
+ refresh_sets: function() {
+ var _this = this;
+ this.fireflix.flickr.api_call(
+ {
+ method: 'flickr.photosets.getList',
+ auth_token: 'default'
+ }, function(xr) {
+ var x = xr.responseXML;
+ var xp = x.evaluate(
+ '/rsp/photosets/photoset', x, null,
+ XPathResult.ORDERED_NODE_ITERATOR_TYPE, null );
+ _this.importXPR(xp);
+ }, function(x,s,c,m) {
+ _this.fireflix.flickr_failure(x,s,c,m);
+ }
+ );
+ },
+ on_select: function() {
+ if(this.selection.count==1) {
+ this.fireflix.cmd_set_props.setAttribute('disabled','false');
+ var s = this.sets[this.selection.currentIndex];
+ this.fireflix.photoset.load_photos(s.id);
+ }else{
+ this.fireflix.cmd_set_props.setAttribute('disabled','true');
+ }
+ }
+ },
+
+ refresh_user_tags: function() {
+ var lb = document.getElementById('tagslist');
+ var _this = this;
+ this.flickr.api_call(
+ {
+ method: 'flickr.tags.getListUser',
+ auth_token: 'default',
+ }, function(xr) {
+ var x = xr.responseXML;
+ var xp = x.evaluate(
+ '/rsp/who/tags/tag', x, null,
+ XPathResult.ORDERED_NODE_ITERATOR_TYPE, null );
+ // TODO: clear list
+ var n; while(n=xp.iterateNext()) {
+ lb.appendItem(n.firstChild.nodeValue);
+ }
+ }, function(x,s,c,m) {
+ _this.flickr_failure(x,s,c,m);
+ }
+ );
+ },
+
+ uploadObserver: {
+ fireflix: null,
+ init: function(f) {
+ this.fireflix = f;
+ },
+ getSupportedFlavours: function() {
+ var rv = new FlavourSet();
+ rv.appendFlavour('application/x-moz-file','nsIFile');
+ rv.appendFlavour('application/x-moz-url');
+ rv.appendFlavour('text/uri-list');
+ rv.appendFlavour('text/unicode');
+ return rv;
+ },
+ canHandleMultipleItems: true,
+ onDragOver: function(ev,fl,sess) {
+ return true;
+ },
+ onDrop: function(ev,dd,s) {
+ var ldf = null;
+ for(var i in dd.dataList) {
+ var di = dd.dataList[i];
+ var dif = di.first;
+ if(
+ ldf==null
+ || ldf.flavour.contentType!=dif.flavour.contentType
+ || ldf.contentLength!=dif.contentLength
+ || ldf.data!=dif.data )
+ this.drop_item(ev,di,s);
+ ldf = dif;
+ }
+ },
+ drop_item: function(ev,di,s) {
+ var d = di.first;
+ switch(d.flavour.contentType) {
+ case 'text/unicode':
+ this.drop_urilist(ev,d.data,s);
+ break;
+ case 'application/x-moz-file':
+ this.fireflix.uploads.add(d.data.path);
+ document.getElementById('fireflix_tabs').selectedTab
+ = document.getElementById('tab_upload');
+ break;
+ case 'text/uri-list':
+ // is it ascii or could it be utf8?
+ this.drop_urilist(ev,splitascii(d.data),s);
+ break;
+ default: alert(d.flavour.contentType+':'+d.data); break;
+ };
+ },
+ drop_urilist: function(ev,ul,s) {
+ // TODO: check for being a file?
+ var us = decodeURIComponent(ul).split(/[\r\n]/);
+ for(var ui in us)
+ if(/\S/.test(us[ui]))
+ this.fireflix.uploads.add(us[ui]);
+ document.getElementById('fireflix_tabs').selectedTab
+ = document.getElementById('tab_upload');
+ }
+ },
+
+ uploads: {
+ fireflix: null,
+ init: function(f) {
+ this.fireflix=f;
+ this.upload_filename = document.getElementById('upload_filename');
+ this.upload_title = document.getElementById('upload_title');
+ this.upload_file_preview = document.getElementById('upload_file_preview');
+ this.upload_file_props = document.getElementById('upload_file_props');
+ this.upload_progress = document.getElementById('upload_progress');
+ this.upload_tags = document.getElementById('upload_tags');
+ },
+ files: new Array(),
+ rowCount: 0,
+ getCellText: function(r,c) {
+ var f = this.files[r];
+ if(c.id=='up_file') return f.file;
+ if(c.id=='up_title') return f.title;
+ if(c.id=='up_status') return f.state;
+ return c.id;
+ },
+ setTree: function(t) { this.tree = t },
+ isContainer: function(r) { return false; },
+ isSeparator: function(r) { return false; },
+ isSorted: function(r) { return false; },
+ getLevel: function(r) { return 0; },
+ getImageSrc: function(r,c) { return null },
+ getRowProperties: function(r,p) {
+ try {
+ if(!Components) return;
+ }catch(e) { return }
+ var f = this.files[r];
+ var as = Components.classes['@mozilla.org/atom-service;1'].
+ getService(Components.interfaces.nsIAtomService);
+ p.AppendElement(as.getAtom(f.state));
+ },
+ getCellProperties: function(r,c,p) { this.getRowProperties(r,p); },
+ getColumnProperties: function(c,p) { },
+ cycleHeader: function(cid,e) { },
+ getParentIndex: function(r) { return -1; },
+ drop: function(r,o) { },
+ canDropBeforeAfter: function(r,b) { return false },
+
+ add: function(f) {
+ if(f.indexOf('file:/')==0) {
+ f = f.substr(5);
+ while(f.substr(0,2)=='//') { // XXX: not very performant, is it? ;-)
+ f = f.substr(1);
+ }
+ }
+ var t = f;
+ var ls = t.lastIndexOf('/');
+ if(ls>0) t = t.substr(ls+1);
+ ls = t.lastIndexOf('\\');
+ if(ls>0) t = t.substr(ls+1);
+ var ld = t.lastIndexOf('.');
+ if(ld>0) t = t.substr(0,ld);
+ this.files.push( {
+ file: f,
+ title: t,
+ tags: '',
+ state: 'pending'
+ } );
+ this.rowCount = this.files.length;
+ this.tree.rowCountChanged(this.rowCount-1,1);
+ },
+
+ upload_worker: function() {
+ for(var f in this.files) {
+ if(this.files[f].state=='pending') {
+ var ff = this.files[f];
+ dump('upload '+ff.file+'\n');
+ this.on_file_upload(ff);
+ ff.state='uploading';
+ this.tree.invalidate();
+ var _this = this;
+ this.fireflix.flickr.upload_file(
+ ff.file, { title: ff.title, tags: ff.tags },
+ function(x,p) {
+ ff.photoid = p;
+ _this.batch_ids.push(p);
+ ff.state='completed';
+ _this.tree.invalidate();
+ window.setTimeout(_this.upload_to,0,_this);
+ }, function(x,s,c,m) {
+ ff.state='failed';
+ ff.flickr_errcode = c;
+ ff.flickr_errmsg = m;
+ _this.tree.invalidate();
+ window.setTimeout(_this.upload_to,0,_this);
+ }
+ );
+ return;
+ }
+ }
+ dump('uploading done\n');
+ this.on_finish_upload();
+ },
+ upload_to: function(_this) { _this.upload_worker(); },
+ on_file_upload: function(f) {
+ document.getElementById('cmd_uploads_upload').setAttribute('disabled','true');
+ for(var fi in this.files) {
+ if(this.files[fi].file==f.file) {
+ this.tree.ensureRowIsVisible(fi);
+ this.selection.rangedSelect(fi,fi,false);
+ this.selection.currentIndex = fi;
+ this.selToProps();
+ break;
+ }
+ }
+ },
+ on_finish_upload: function() {
+ if(this.batch_ids.length) {
+ var psn = prompt(this.fireflix.loc_strings.getString('postUploadPhotoset'));
+ if(psn!=null) {
+ var pids = this.batch_ids.join(',');
+ var ppid = this.batch_ids[0];
+ var _this = this;
+ this.fireflix.flickr.api_call(
+ {
+ method: 'flickr.photosets.create',
+ auth_token: 'default',
+ title: psn,
+ primary_photo_id: ppid
+ }, function(x) {
+ var npid =
+ x.responseXML.getElementsByTagName('photoset').item(0).getAttribute('id');
+ _this.fireflix.flickr.api_call(
+ {
+ method: 'flickr.photosets.editPhotos',
+ auth_token: 'default',
+ photoset_id: npid,
+ primary_photo_id: ppid,
+ photo_ids: pids
+ }, function(x) {
+ _this.fireflix.refresh_sets();
+ }, function(x,s,c,m) {
+ _this.fireflix.flickr_failure(x,s,c,m);
+ }
+ );
+ }, function(x,s,c,m) {
+ _this.fireflix.flickr_failure(x,s,c,m);
+ }
+ );
+ }
+ }
+ this.selection.clearSelection();
+ document.getElementById('cmd_uploads_upload').setAttribute('disabled','false');
+ this.upload_progress.setAttribute('hidden','true');
+ },
+
+ clear_list: function() {
+ this.tree.beginUpdateBatch();
+ this.rowCount = 0;
+ this.files = new Array();
+ this.tree.endUpdateBatch();
+ this.selToProps();
+ },
+ selectionChanged: function() {
+ this.selToProps();
+ },
+ disableProps: function() {
+ this.upload_filename.value='';
+ this.upload_filename.disabled = true;
+ this.upload_title.value='';
+ this.upload_title.disabled = true;
+ this.upload_file_preview.src = null;
+ this.upload_file_props.hidden = true;
+ this.upload_tags.value='';
+ this.upload_tags.disabled = true;
+ },
+ selToProps: function() {
+ if(!this.selection.count) {
+ this.disableProps();
+ }else if(this.selection.count==1) {
+ var f=this.files[this.selection.currentIndex];
+ if(f==null || f.state!='pending') {
+ this.disableProps();
+ }else{
+ this.upload_filename.value = f.file;
+ this.upload_filename.disabled = false;
+ this.upload_title.value = f.title;
+ this.upload_title.disabled = false;
+ this.upload_file_preview.src = 'file:///'+f.file;
+ this.upload_file_props.hidden = false;
+ this.upload_tags.value = f.tags;
+ this.upload_tags.disabled = false;
+ }
+ }else{
+ var ftitle = null; var onetitle = true;
+ var ftags = null; var onetag = true;
+ var fs = 0;
+ for(var ff in this.files) {
+ if(this.selection.isSelected(ff) && this.files[ff].state=='pending' ) {
+ ++fs;
+ if(ftitle==null) {
+ ftitle = this.files[ff].title;
+ }else if(ftitle!=this.files[ff].title) {
+ onetitle = false;
+ }
+ if(ftags==null) {
+ ftags = this.files[ff].tags;
+ }else if(ftags!=this.files[ff].tags) {
+ onetag = false;
+ }
+ }
+ }
+ if(fs) {
+ this.upload_filename.value='';
+ this.upload_filename.disabled = true;
+ if(onetitle)
+ this.upload_title.value = ftitle;
+ this.upload_title.disabled = false;
+ if(onetag)
+ this.upload_tags.value = ftags;
+ this.upload_tags.disabled = false;
+ this.upload_file_preview.src = null;
+ this.upload_file_props.hidden = false;
+ }else
+ this.disableProps();
+ }
+ },
+ propsToSel: function(prop) {
+ if(this.selection.count<=0) return;
+ for(var ff in this.files) {
+ if(this.selection.isSelected(ff) && this.files[ff].state=='pending') {
+ if(prop=='filename')
+ this.files[ff].file = this.upload_filename.value;
+ if(prop=='title')
+ this.files[ff].title = this.upload_title.value;
+ if(prop=='tags')
+ this.files[ff].tags = this.upload_tags.value;
+ this.tree.invalidateRow(ff);
+ }
+ }
+ },
+
+ on_upload: function() {
+ this.selToProps();
+ this.batch_ids = new Array();
+ this.upload_progress.value=0;
+ this.upload_progress.setAttribute('hidden','false');
+ this.upload_worker();
+ },
+ on_clear: function() {
+ this.clear_list();
+ },
+ on_remove: function() {
+ if(this.selection.count) {
+ this.tree.beginUpdateBatch();
+ for(var i=this.files.length-1;i>=0;--i) {
+ if(this.selection.isSelected(i)) {
+ this.files.splice(i,1);
+ this.rowCount--;
+ }
+ }
+ this.tree.endUpdateBatch();
+ this.selection.clearSelection();
+ }
+ },
+ on_add: function() {
+ var ifp = Components.interfaces.nsIFilePicker;
+ var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(ifp);
+ fp.init(window, "Select a File", ifp.modeOpenMultiple);
+ fp.appendFilters(ifp.filterImages);
+ var rv = fp.show();
+ if(rv==ifp.returnOK) {
+ var ff = fp.files;
+ while(ff.hasMoreElements()) {
+ var f = ff.getNext();
+ f.QueryInterface(Components.interfaces.nsIFile);
+ this.add(f.path);
+ }
+ }
+ }
+ },
+
+ on_set_props: function() {
+ var pset = this.photosets.sets[this.photosets.selection.currentIndex];
+ window.openDialog(
+ "chrome://fireflix/content/photoset-props.xul",
+ null, "dependent,modal,dialog,chrome", this,
+ pset );
+ if(pset.dirty) {
+ var _this = this;
+ this.flickr.api_call(
+ {
+ method: 'flickr.photosets.editMeta',
+ auth_token: 'default',
+ photoset_id: pset.id,
+ title: pset.title,
+ description: pset.description
+ }, function(xr) {
+ pset.dirty = false;
+ _this.flickr.api_call(
+ {
+ method: 'flickr.photosets.getPhotos',
+ auth_token: 'default',
+ photoset_id: pset.id
+ }, function(xr) {
+ var x = xr.responseXML;
+ var xp = x.evaluate(
+ '/rsp/photoset/photo', x, null,
+ XPathResult.ORDERED_NODE_ITERATOR_TYPE, null );
+ var phids = new Array();
+ var priph = null;
+ var n; while(n=xp.iterateNext()) {
+ var pid = n.getAttribute('id');
+ phids.push( pid );
+ if(pid==pset.primary && n.getAttribute('isprimary')!='1')
+ priph = pid;
+ }
+ if(priph) {
+ _this.flickr.api_call(
+ {
+ method: 'flickr.photosets.editPhotos',
+ auth_token: 'default',
+ photoset_id: pset.id,
+ primary_photo_id: priph,
+ photo_ids: phids.join(',')
+ }, function() { }, function(x,s,c,m) { /* flickr.photosets.editPhotos */
+ _this.flickr_failure(x,s,c,m);
+ }
+ );
+ }
+ }, function(x,s,c,m) { /* flickr.photosets.getPhotos */
+ _this.flickr_failure(x,s,c,m);
+ }
+ );
+ }, function(x,s,c,m) { /* flickr.photosets.editMeta */
+ _this.flickr_failure(x,s,c,m);
+ }
+ );
+ }
+ },
+ on_refresh_sets: function() {
+ this.refresh_sets();
+ },
+ on_cmd_sets_html: function(csfx,ev) {
+ var uti = csfx.charAt(0); var utl = csfx.charAt(1);
+ var rv = this.build_html(this.photoset.photos,uti,utl);
+ this.popup_content(rv);
+ },
+
+ on_cmd_uploads_html: function(csfx,ev) {
+ var uti = csfx.charAt(0); var utl = csfx.charAt(1);
+ var pids = new Array();
+ for(var f in this.uploads.files) {
+ if(this.uploads.selection.isSelected(f))
+ if(this.uploads.files[f].photoid)
+ pids.push(this.uploads.files[f].photoid);
+ }
+ var pp = this.uploads.rowCount*2; if(pp>500) pp = 500;
+ var _this = this;
+ this.flickr.api_call(
+ {
+ method: 'flickr.photos.search',
+ auth_token: 'default',
+ extras: 'original_format',
+ user_id: 'me',
+ per_page: pp
+ },
+ function(xr) {
+ var x = xr.responseXML;
+ var rv = '';
+ for(var pn in pids) {
+ var p = pids[pn];
+ var pp = new Photo(xp_node('/rsp/photos/photo[@id='+p+']',x));
+ rv += _this.photo_html(pp,uti,utl)+'\n';
+ }
+ _this.popup_content(rv);
+ }, function(x,s,c,m) {
+ _this.flickr_failure(x,s,c,m);
+ }
+ );
+ },
+
+ /*
+ *
+ */
+ foundphotos: {
+ fireflix: null,
+ init: function(f) {
+ this.fireflix = f;
+ this.search_for = document.getElementById('search_for');
+ this.search_tags= document.getElementById('search_tags');
+ this.search_mine = document.getElementById('search_mine');
+ document.getElementById('searchresults').view = this;
+ this.searchresult_props = document.getElementById('searchresult_props');
+ this.search_photo = document.getElementById('search_photo');
+ this.searchresult_title = document.getElementById('searchresult_title');
+ this.searchresult_description = document.getElementById('searchresult_description');
+ },
+ photos: new Array(),
+ rowCount: 0,
+ getCellText: function(r,c) {
+ var p = this.photos[r];
+ if(c.id=='sr_title') return p.title;
+ return c.id;
+ },
+ setTree: function(t) { this.tree = t },
+ isContainer: function(r) { return false },
+ isSeparator: function(r) { return false },
+ isSorted: function(r) { return false },
+ getLevel: function(r) { return 0 },
+ getImageSrc: function(r,c) { return null },
+ getRowProperties: function(r,p) { },
+ getCellProperties: function(cid,cel,p) { },
+ getColumnProperties: function(cid,cel,p) { },
+ cycleHeader: function(cid,e) { },
+ getParentIndex: function(r) { return -1 },
+ drop: function(r,o) { },
+ canDropBeforeAfter: function(r,b) { return false },
+
+ importXPR: function(xp) {
+ this.selection.clearSelection();
+ this.selection.currentIndex = -1;
+ this.searchresult_props.hidden = true;
+ this.tree.beginUpdateBatch();
+ this.photos = new Array();
+ var n; while(n=xp.iterateNext()) {
+ this.photos.push(new Photo(n));
+ }
+ this.rowCount = this.photos.length;
+ this.tree.endUpdateBatch();
+ },
+ search_photos: function() {
+ var pars = {
+ method: 'flickr.photos.search',
+ auth_token: 'default',
+ extras: 'license,date_upload,date_taken,owner_name,icon_server,original_format,last_update,geo'
+ };
+ if(this.search_mine.checked)
+ pars.user_id='me';
+ if(this.search_tags.checked) {
+ pars.tags=this.search_for.value.split(/ +/).join(',');
+ }else{
+ pars.text=this.search_for.value;
+ }
+ var _this = this;
+ this.fireflix.flickr.api_call( pars,
+ function(xr) {
+ var x = xr.responseXML;
+ var xp = x.evaluate(
+ '/rsp/photos/photo', x, null,
+ XPathResult.ORDERED_NODE_ITERATOR_TYPE, null );
+ _this.importXPR(xp);
+ _this.on_select();
+ }, function(x,s,c,m) {
+ _this.fireflix.flickr_failure(x,s,c,m);
+ }
+ );
+ },
+ on_select: function() {
+ if(this.selection.currentIndex<0) {
+ this.searchresult_props.hidden = true;
+ }else{
+ var p = this.photos[this.selection.currentIndex];
+ if(!p) {
+ this.searchresult_props.hidden = true;
+ }else{
+ this.search_photo.src = this.fireflix.flickr.make_photo_url(p,'t');
+ this.searchresult_title.value = p.title;
+ this.searchresult_description.value = null;
+ if(p.description==null && p.description==undefined) {
+ var pid = p.id;
+ var ci = this.selection.currentIndex;
+ var _this = this;
+ this.fireflix.flickr.api_call(
+ {
+ method: 'flickr.photos.getInfo',
+ auth_token: 'default',
+ photo_id: p.id,
+ secret: p.secret
+ }, function(xr) {
+ var pp = _this.photos[ci];
+ if(ci==_this.selection.currentIndex && pp.id==pid) {
+ var n = xp_node('/rsp/photo',xr.responseXML);
+ pp.fromNode_(n);
+ _this.searchresult_description.value=pp.description?pp.description:null;
+ }
+ }, function(x,s,c,m) {
+ _this.fireflix.flickr_failure(x,s,c,m);
+ }
+ );
+ this.searchresult_props.hidden = false;
+ }else{
+ this.searchresult_description.value=p.description?p.description:null;
+ }
+ }
+ }
+ },
+ on_cmd_open: function(ev) {
+ if(this.selection.currentIndex<0)
+ return;
+ var p = this.photos[this.selection.currentIndex];
+ if(!p.id)
+ return;
+ this.fireflix.openTab(this.fireflix.flickr.make_photo_url(p,'p'));
+ }
+ },
+
+ photo_html: function(p,i,l) {
+ // TODO: add alt/title when possible
+ var rv =
+ '<a href="'+this.flickr.make_photo_url(p,l)+'">' +
+ '<img src="'+this.flickr.make_photo_url(p,i)+'" />'+
+ '</a>';
+ return rv;
+ },
+ build_html: function(photos,uti,utl) {
+ var rv = '';
+ for(var i in photos) {
+ var p = photos[i];
+ rv += this.photo_html(p,utl,uti)+'\n';
+ }
+ return rv;
+ },
+
+ popup_content: function(s) {
+ window.openDialog(
+ "chrome://fireflix/content/generated-content.xul",
+ null, "dialog,chrome", this, s );
+ },
+ copy_to_clipboard: function(s) {
+ var ch = Components.classes["@mozilla.org/widget/clipboardhelper;1"]
+ .getService(Components.interfaces.nsIClipboardHelper);
+ ch.copyString(s);
+ },
+ openTab: function(l) {
+ var wm = Components.classes['@mozilla.org/appshell/window-mediator;1'].getService(
+ Components.interfaces.nsIWindowMediator );
+ var bw = wm.getMostRecentWindow('navigator:browser');
+ var b = bw.getBrowser();
+ var t = b.addTab(l);
+ b.selectedTab = t;
+ },
+
+ build_menus: function() {
+ this.append_html_menu(
+ document.getElementById('sets_html_menu'),
+ 'stm_','m_bop','cmdset_sets','cmd_sets_html'
+ );
+ this.append_html_menu(
+ document.getElementById('uploads_html_menu'),
+ 'stm_','m_bop','cmdset_uploads','cmd_uploads_html'
+ );
+ return;
+ },
+ append_html_menu: function(m,imgt,lnkt,csid,cpfx) {
+ var mp = m.appendChild(document.createElement('menupopup'));
+ var t;
+ t=mp.appendChild(document.createElement('menuitem'));
+ t.setAttribute('label',this.loc_strings.getString('menutitle_Images'));
+ t.setAttribute('class','menuhead');t.setAttribute('disabled','true');
+ mp.appendChild(document.createElement('menuseparator'));
+ var cs = document.getElementById(csid);
+ for(var iti=0;iti<imgt.length;++iti) {
+ t = mp.appendChild(document.createElement('menu'));
+ t.setAttribute('label',this.loc_strings.getString('urltype_'+imgt.charAt(iti)));
+ var smp = t.appendChild(document.createElement('menupopup'));
+ t=smp.appendChild(document.createElement('menuitem'));
+ t.setAttribute('label',this.loc_strings.getString('menutitle_Links'));
+ t.setAttribute('class','menuhead');t.setAttribute('disabled','true');
+ smp.appendChild(document.createElement('menuseparator'));
+ for(var lti=0;lti<lnkt.length;++lti) {
+ var csfx = imgt.charAt(iti)+lnkt.charAt(lti);
+ t=smp.appendChild(document.createElement('menuitem'));
+ t.setAttribute('label',this.loc_strings.getString('urltype_'+lnkt.charAt(lti)));
+ t.setAttribute('command',cpfx+'_'+csfx);
+ t=cs.appendChild(document.createElement('command'));
+ t.setAttribute('id',cpfx+'_'+csfx);
+ t.setAttribute('oncommand','fireflix.on_'+cpfx+"('"+csfx+"',event)");
+ }
+ }
+ return mp;
+ },
+
+ flickr_failure: function(x,s,c,m) {
+ if(c==98) { // Invalid auth token
+ this.flickr.reset_token();
+ document.getElementById('auth_info').value = this.no_auth_info_label;
+ document.getElementById('auth_info').disabled = true;
+ document.getElementById('b_auth').hidden = false;
+ return;
+ }
+ // TODO: is that beauty
+ alert('flickr api call failed\n'+c+' '+m);
+ }
+
+};
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 @@
+/*
+ * Photoset
+ */
+
+function Photoset(s) {
+ if(s instanceof Photoset) {
+ for(var p in s) this[p]=s[p];
+ }else
+ this.fromNode(s);
+}
+Photoset.prototype = {
+ id: null,
+ primary: null,
+ secret: null,
+ server: null,
+ photos: null,
+ title: null,
+ description: null,
+ fromNode: function(n) {
+ this.id = n.getAttribute('id');
+ this.primary = n.getAttribute('primary');
+ this.secret = n.getAttribute('secret');
+ this.server = n.getAttribute('server');
+ this.photos = n.getAttribute('photos');
+ this.title = n.getElementsByTagName('title').item(0).firstChild.nodeValue;
+ this.description = n.getElementsByTagName('description').item(0).firstChild;
+ if(this.description) this.description = this.description.nodeValue;
+ }
+};
+
+/*
+ * Photo
+ */
+function Photo(s) {
+ if(s instanceof Photo) {
+ for(var p in s) this[p]=s[p];
+ }else
+ this.fromNode(s);
+}
+Photo.prototype = {
+ id: null, secret: null,
+ server: null,
+ title: null,
+ isprimary: null,
+ license: null,
+ dateupload: null, datetaken: null, datetakengranularity: null,
+ ownername: null,
+ iconserver: null,
+ originalformat: null,
+ lastupdate: null,
+ fromNode: function(n) {
+ this.id = n.getAttribute('id'); this.secret = n.getAttribute('secret');
+ this.server = n.getAttribute('server');
+ this.title = n.getAttribute('title');
+ this.isprimary = n.getAttribute('isprimary');
+ this.license = n.getAttribute('license');
+ this.dateupload = n.getAttribute('dateupload');
+ this.datetaken = n.getAttribute('datetaken'); this.datetakengranularity = n.getAttribute('datetakengranularity');
+ this.ownername = n.getAttribute('ownername');
+ this.iconserver = n.getAttribute('iconserver');
+ this.originalformat = n.getAttribute('originalformat');
+ this.lastupdate = n.getAttribute('lastupdate');
+ },
+ fromNode_: function(n) {
+ var t;
+ // TODO: @rotation @isfavorite
+ this.owner = {};
+ t = n.getElementsByTagName('owner').item(0);
+ if(t) {
+ this.owner.nsid=t.getAttribute('nsid');
+ this.owner.username=t.getAttribute('username');
+ this.owner.realname=t.getAttribute('realname');
+ this.owner.location=t.getAttribute.location;
+ }
+ t = n.getElementsByTagName('description').item(0);
+ if(t && t.firstChild) {
+ this.description = t.firstChild.nodeValue;
+ }
+ // TODO: visibility/@ispublic visibility/@isfriend visibility/@isfamily
+ // TODO: dates/@posted dates/@taken dates/@takengranularity dates/@lastupdate
+ // TODO: permissions/@permcomment permsiions/@permaddmeta
+ // TODO: editability/@canaddcomment editability/@canaddmeta
+ // TODO: comments
+ // TODO: notes/note/@id notes/note/@author notes/note/@authorname
+ // TODO: notes/note/@x notes/note/@y notes/note/@w notes/note/@h
+ // TODO: notes/note
+ // TODO: tags/tag/@id tags/tag/@author tags/tag/@raw tags/tag
+ // TODO: urls/url/@type urls/url
+ }
+};
+
+function toutf8(ucode) {
+ var rv = '';
+ for(var i=0;i<ucode.length;++i) {
+ var cc = ucode.charCodeAt(i);
+ if(cc<=0x7F)
+ rv += ucode.charAt(i);
+ else if(cc<=0x7ff)
+ rv += String.fromCharCode(
+ 0xc0|((cc>> 6)&0x1f),
+ 0x80|( cc &0x3f) );
+ else if(cc<=0xffff)
+ rv += String.fromCharCode(
+ 0xe0|((cc>>12)&0x0f),
+ 0x80|((cc>> 6)&0x3f),
+ 0x80|( cc &0x3f) );
+ else if(cc<=0x1fffff)
+ rv += String.fromCharCode(
+ 0xf0|((cc>>18)&0x07),
+ 0x80|((cc>>12)&0x3f),
+ 0x80|((cc>> 6)&0x3f),
+ 0x80|( cc &0x3f) );
+ else if(cc<=0x03ffffff)
+ rv += String.fromCharCode(
+ 0xf8|((cc>>24)&0x03),
+ 0x80|((cc>>18)&0x3f),
+ 0x80|((cc>>12)&0x3f),
+ 0x80|((cc>> 6)&0x3f),
+ 0x80|( cc &0x3f) );
+ else if(cc<=0x7fffffff)
+ rv += String.fromCharCode(
+ 0xfc|((cc>>30)&0x01),
+ 0x80|((cc>>24)&0x3f),
+ 0x80|((cc>>18)&0x3f),
+ 0x80|((cc>>12)&0x3f),
+ 0x80|((cc>> 6)&0x3f),
+ 0x80|( cc &0x3f) );
+ }
+ return rv;
+}
+function xp_str(xp,x) {
+ var rv = x.evaluate(
+ xp, x, null, XPathResult.STRING_TYPE, null );
+ return rv.stringValue;
+}
+function xp_node(xp,x) {
+ var rv = x.evaluate(
+ xp, x, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null );
+ return rv.singleNodeValue;
+}
+
+function Flickr() { }
+Flickr.prototype = {
+
+ rest_url: 'http://www.flickr.com/services/rest/',
+ auth_url: 'http://flickr.com/services/auth/',
+ photo_url: 'http://static.flickr.com/',
+ photos_url: 'http://www.flickr.com/photos/',
+ upload_url: 'http://www.flickr.com/services/upload/',
+
+ api_sig: function(paramstr) {
+ return MD5(toutf8(this.api_shs+paramstr));
+ },
+ api_call_url: function(params,url) {
+ params.api_key = this.api_key;
+ var pp = new Array();
+ for(var p in params) {
+ pp.push(p);
+ }
+ var pstr = '';
+ var rv = (url?url:this.rest_url)+'?';
+ for(var p in pp.sort()) {
+ var pn = pp[p];
+ pstr += pn+params[pn];
+ rv += pn+'='+params[pn]+'&';
+ }
+ rv += 'api_sig='+this.api_sig(pstr);
+ return rv;
+ },
+ api_call: function(params, on_success, on_failure) {
+ if(params.auth_token == 'default')
+ params.auth_token = this.token;
+ var x = new XMLHttpRequest();
+ x.open("GET",this.api_call_url(params));
+ x.onreadystatechange=function() {
+ if(x.readyState!=4) return false;
+ if(x.status==200) {
+ var stat = x.responseXML.firstChild.getAttribute('stat');
+ if(stat=='ok') {
+ if(on_success) on_success(x);
+ }else{
+ var e = x.responseXML.getElementsByTagName('err').item(0);
+ var ecode = e.getAttribute('code');
+ var emsg = e.getAttribute('msg');
+ dump(params.method+' failed: '+ecode+' '+emsg+'\n');
+ if(on_failure) on_failure(x,stat,ecode,emsg);
+ }
+ }else{
+ if(on_failure) on_failure(x);
+ }
+ return true;
+ }
+ x.send(null);
+ return true;
+ },
+
+ frob: null,
+ authorize_0: function(on_s, on_f) {
+ var _this = this;
+ this.api_call(
+ { method: 'flickr.auth.getFrob' },
+ function(x) {
+ _this.frob = xp_str('/rsp/frob',x.responseXML);
+ var u = _this.api_call_url(
+ { frob: _this.frob, perms: 'delete' }, _this.auth_url );
+ var wm = Components.classes['@mozilla.org/appshell/window-mediator;1'].getService(
+ Components.interfaces.nsIWindowMediator );
+ var bw = wm.getMostRecentWindow('navigator:browser');
+ var b = bw.getBrowser();
+ var t = b.addTab(u);
+ b.selectedTab = t;
+ if(on_s) on_s();
+ }, function(x,s,c,m) {
+ if(on_f) on_f(x,s,c,m);
+ }
+ );
+ },
+ token: null,
+ perms: null,
+ user: null,
+ authorize_1: function(on_s, on_f) {
+ var _this = this;
+ this.api_call(
+ { method: 'flickr.auth.getToken', frob: this.frob },
+ function(x) {
+ _this.token = xp_str('/rsp/auth/token',x.responseXML);
+ _this.perms = xp_str('/rsp/auth/perms',x.responseXML);
+ var u = xp_node('/rsp/auth/user',x.responseXML);
+ _this.user = {
+ nsid: u.getAttribute('nsid'),
+ username: u.getAttribute('username'),
+ fullname: u.getAttribute('fullname')
+ };
+ if(on_s) on_s(x);
+ }, function(x,s,c,m) {
+ if(on_f) on_f(x,s,c,m);
+ }
+ );
+ },
+
+ prefs: Components.classes['@mozilla.org/preferences-service;1'].getService(
+ Components.interfaces.nsIPrefBranch
+ ),
+ prefs_root: 'net.klever.kin.flickr',
+ save_token: function() {
+ // TODO: don't clear when there's nothing to clear or catch exceptions
+ if(this.token)
+ this.prefs.setCharPref(this.prefs_root+'.auth_token',this.token);
+ else
+ this.prefs.clearUserPref(this.prefs_root+'.auth_token');
+ if(this.perms)
+ this.prefs.setCharPref(this.prefs_root+'.auth_perms',this.perms);
+ else
+ this.prefs.clearUserPref(this.prefs_root+'.auth_perms');
+ if(this.user && this.user.nsid!=null && this.user.nsid!=undefined)
+ this.prefs.setCharPref(this.prefs_root+'.auth_user.nsid',this.user.nsid);
+ else
+ this.prefs.clearUserPref(this.prefs_root+'.auth_user.nsid');
+ if(this.user && this.user.username!=null && this.user.username!=undefined)
+ this.prefs.setCharPref(this.prefs_root+'.auth_user.username',this.user.username);
+ else
+ this.prefs.clearUserPref(this.prefs_root+'.auth_user.username');
+ if(this.user && this.user.fullname!=null && this.user.fullname!=undefined)
+ this.prefs.setCharPref(this.prefs_root+'.auth_user.fullname',this.user.fullname);
+ else
+ this.prefs.clearUserPref(this.prefs_root+'.auth_user.fullname');
+ },
+ _reset_token: function() {
+ this.token = null; this.perms = null; this.user = null;
+ return false;
+ },
+ load_token: function() {
+ try {
+ if(this.prefs.getPrefType(this.prefs_root+'.auth_token')!=this.prefs.PREF_STRING)
+ return this._reset_token();
+ this.token = this.prefs.getCharPref(this.prefs_root+'.auth_token');
+ if(this.prefs.getPrefType(this.prefs_root+'.auth_perms')!=this.prefs.PREF_STRING)
+ return this._reset_token();
+ this.perms = this.prefs.getCharPref(this.prefs_root+'.auth_perms');
+ if(this.prefs.getPrefType(this.prefs_root+'.auth_user.nsid')!=this.prefs.PREF_STRING)
+ return this._reset_token();
+ this.user = new Object();
+ this.user.nsid = this.prefs.getCharPref(this.prefs_root+'.auth_user.nsid');
+ if(this.prefs.getPrefType(this.prefs_root+'.auth_user.username')!=this.prefs.PREF_STRING)
+ return this._reset_token();
+ this.user.username = this.prefs.getCharPref(this.prefs_root+'.auth_user.username');
+ if(this.prefs.getPrefType(this.prefs_root+'.auth_user.fullname')!=this.prefs.PREF_STRING)
+ return this._reset_token();
+ this.user.fullname = this.prefs.getCharPref(this.prefs_root+'.auth_user.fullname');
+ }catch(e) { return this._reset_token(); }
+ return true;
+ },
+ reset_token: function() {
+ this._reset_token();
+ this.save_token();
+ },
+
+ get_photo_url: function(ser,id,sec,sfx,ext) {
+ var rv = this.photo_url + ser + '/' + id + '_' + sec;
+ if(sfx && sfx!='_') rv += '_'+sfx;
+ rv += ext?'.'+ext:'.jpg';
+ return rv;
+ },
+ get_image_url: function(o,sfx) {
+ return this.get_photo_url(
+ o.server,
+ (o instanceof Photoset)? o.primary : o.id,
+ o.secret,
+ sfx,
+ (sfx=='o')?o.originalformat:null
+ );
+ },
+ get_photo_page_url: function(p) {
+ if(p instanceof Photo) // TODO: half wrong, what if no owner?
+ return this.photos_url + (p.owner.nsid?p.owner.nsid:this.user.nsid) + '/' + p.id;
+ else // TODO: take owner into account?
+ return this.photos_url + this.user.nsid + '/' + p;
+ },
+ make_photo_url: function(p,sfx) {
+ if(sfx=='p')
+ return this.get_photo_page_url(p);
+ else
+ return this.get_image_url(p,sfx);
+ },
+
+ upload_file: function(f,fa,on_success,on_failure) {
+ try {
+ var fi = Components.classes["@mozilla.org/file/local;1"]
+ .createInstance(Components.interfaces.nsILocalFile);
+ fi.initWithPath( f );
+ var st = Components.classes["@mozilla.org/network/file-input-stream;1"]
+ .createInstance(Components.interfaces.nsIFileInputStream);
+ st.init(fi,0x01,00004,null);
+ var bis = Components.classes["@mozilla.org/binaryinputstream;1"]
+ .createInstance(Components.interfaces.nsIBinaryInputStream);
+ bis.setInputStream(st);
+
+ // allocate and initialize temp storage string
+ var pbs = Components.classes["@mozilla.org/storagestream;1"]
+ .createInstance(Components.interfaces.nsIStorageStream);
+ pbs.init(1024,10000000,null);
+ // create output stream
+ var pbos = pbs.getOutputStream(0);
+ // and a binaryoutputstream interface
+ var pbbos = Components.classes["@mozilla.org/binaryoutputstream;1"]
+ .createInstance(Components.interfaces.nsIBinaryOutputStream);
+ pbbos.setOutputStream(pbos);
+
+ /* create POST body */
+ var boundarytoken = 'kadaroloongazaduviaxamma';
+ var boundary = '--'+boundarytoken;
+ var b = '';
+
+ var parms = { api_key: this.api_key, auth_token: this.token };
+ for(var p in fa) parms[p] = fa[p];
+ var pns = new Array();
+ for(var p in parms) pns.push(p);
+ var pstr = '';
+ for(var p in pns.sort()) {
+ var pn = pns[p];
+ pstr += pn+parms[pn];
+ b += boundary+'\nContent-Disposition: form-data; name="'+pn+'"\n\n'+toutf8(parms[pn])+'\n';
+ }
+ b += boundary+'\nContent-Disposition: form-data; name="api_sig"\n\n'+this.api_sig(pstr)+'\n';
+ b += boundary+'\nContent-Disposition: form-data; name="photo"; filename="'+f+'"\nContent-Type: image/jpeg\nContent-Transfer-Encoding: binary\n\n';
+ pbbos.writeBytes(b,b.length);
+ var bisbytes = bis.available();
+ pbbos.writeBytes(bis.readBytes(bisbytes),bisbytes);
+ pbbos.writeBytes('\n'+boundary+'--',3+boundary.length); bis.close(); st.close();
+
+ pbbos.close(); pbos.close();
+
+ var x = new XMLHttpRequest();
+ x.open("POST",this.upload_url);
+ x.setRequestHeader('Content-Type', 'multipart/form-data; boundary="'+boundarytoken+'"');
+ x.setRequestHeader('Connection','close');
+ x.setRequestHeader('Content-Length',b.length);
+ x.onreadystatechange=function() {
+ if(x.readyState!=4) return false;
+ if(x.status==200) {
+ var stat = x.responseXML.firstChild.getAttribute('stat');
+ if(stat=='ok') {
+ var pid = xp_str('/rsp/photoid',x.responseXML);
+ if(on_success) on_success(x,pid);
+ }else{
+ var e = x.responseXML.getElementsByTagName('err').item(0);
+ var ecode = e.getAttribute('code');
+ var emsg = e.getAttribute('msg');
+ dump('upload failed: '+ecode+' '+emsg+'\n');
+ if(on_failure) on_failure(x,stat,ecode,emsg);
+ }
+ }else{
+ if(on_failure) on_failure(x);
+ }
+ return true;
+ };
+ x.send(pbs.newInputStream(0));
+ }catch(e) {
+ if(on_failure) on_failure(e,null,-1,e.message);
+ }
+ }
+
+};
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 @@
+var generated = {
+ fireflix: null,
+ data: null,
+
+ init: function() {
+ this.fireflix = window.arguments[0];
+ this.data = window.arguments[1];
+ this.databox = document.getElementById('data');
+ this.databox.value = this.data;
+ this.databox.select();
+ },
+ copy: function() {
+ var ch = Components.classes["@mozilla.org/widget/clipboardhelper;1"]
+ .getService(Components.interfaces.nsIClipboardHelper);
+ ch.copyString(this.data);
+ }
+};
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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xsml-stylesheet href="fireflix.css" type="text/css"?>
+<!DOCTYPE dialog SYSTEM "chrome://fireflix/locale/fireflix.dtd">
+<dialog
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ id="generated_content"
+ buttons="accept"
+ defaultbutton="accept"
+ title="&generated.title;"
+ onload="generated.init()"
+ >
+
+ <script src="generated-content.js" type="application/x-javascript"/>
+
+ <vbox class="generated wholething" flex="1">
+ <textbox flex="1" minheight="300" minwidth="300" id="data" multiline="true" readonly="true" />
+ <button id="copy" label="&generated.copy;" oncommand="generated.copy()" />
+ </vbox>
+</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 @@
+/* MD5 Message-Digest Algorithm - JavaScript
+' MODIFICATION HISTORY:
+' 1.0 16-Feb-2001 - Phil Fresle (sales@frez.co.uk) - Initial Version (VB/ASP code)
+' 1.0 21-Feb-2001 - Enrico Mosanghini (erik504@yahoo.com) - JavaScript porting
+*/
+function MD5(sMessage) {
+ function RotateLeft(lValue, iShiftBits) { return (lValue<<iShiftBits) | (lValue>>>(32-iShiftBits)); }
+ function AddUnsigned(lX,lY) {
+ var lX4,lY4,lX8,lY8,lResult;
+ lX8 = (lX & 0x80000000);
+ lY8 = (lY & 0x80000000);
+ lX4 = (lX & 0x40000000);
+ lY4 = (lY & 0x40000000);
+ lResult = (lX & 0x3FFFFFFF)+(lY & 0x3FFFFFFF);
+ if (lX4 & lY4) return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
+ if (lX4 | lY4) {
+ if (lResult & 0x40000000) return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
+ else return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
+ } else return (lResult ^ lX8 ^ lY8);
+ }
+ function F(x,y,z) { return (x & y) | ((~x) & z); }
+ function G(x,y,z) { return (x & z) | (y & (~z)); }
+ function H(x,y,z) { return (x ^ y ^ z); }
+ function I(x,y,z) { return (y ^ (x | (~z))); }
+ function FF(a,b,c,d,x,s,ac) {
+ a = AddUnsigned(a, AddUnsigned(AddUnsigned(F(b, c, d), x), ac));
+ return AddUnsigned(RotateLeft(a, s), b);
+ }
+ function GG(a,b,c,d,x,s,ac) {
+ a = AddUnsigned(a, AddUnsigned(AddUnsigned(G(b, c, d), x), ac));
+ return AddUnsigned(RotateLeft(a, s), b);
+ }
+ function HH(a,b,c,d,x,s,ac) {
+ a = AddUnsigned(a, AddUnsigned(AddUnsigned(H(b, c, d), x), ac));
+ return AddUnsigned(RotateLeft(a, s), b);
+ }
+ function II(a,b,c,d,x,s,ac) {
+ a = AddUnsigned(a, AddUnsigned(AddUnsigned(I(b, c, d), x), ac));
+ return AddUnsigned(RotateLeft(a, s), b);
+ }
+ function ConvertToWordArray(sMessage) {
+ var lWordCount;
+ var lMessageLength = sMessage.length;
+ var lNumberOfWords_temp1=lMessageLength + 8;
+ var lNumberOfWords_temp2=(lNumberOfWords_temp1-(lNumberOfWords_temp1 % 64))/64;
+ var lNumberOfWords = (lNumberOfWords_temp2+1)*16;
+ var lWordArray=Array(lNumberOfWords-1);
+ var lBytePosition = 0;
+ var lByteCount = 0;
+ while ( lByteCount < lMessageLength ) {
+ lWordCount = (lByteCount-(lByteCount % 4))/4;
+ lBytePosition = (lByteCount % 4)*8;
+ lWordArray[lWordCount] = (lWordArray[lWordCount] | (sMessage.charCodeAt(lByteCount)<<lBytePosition));
+ lByteCount++;
+ }
+ lWordCount = (lByteCount-(lByteCount % 4))/4;
+ lBytePosition = (lByteCount % 4)*8;
+ lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80<<lBytePosition);
+ lWordArray[lNumberOfWords-2] = lMessageLength<<3;
+ lWordArray[lNumberOfWords-1] = lMessageLength>>>29;
+ return lWordArray;
+ }
+ function WordToHex(lValue) {
+ var WordToHexValue="",WordToHexValue_temp="",lByte,lCount;
+ for (lCount = 0;lCount<=3;lCount++) {
+ lByte = (lValue>>>(lCount*8)) & 255;
+ WordToHexValue_temp = "0" + lByte.toString(16);
+ WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length-2,2);
+ }
+ return WordToHexValue;
+ }
+ var x=Array();
+ var k,AA,BB,CC,DD,a,b,c,d
+ var S11=7, S12=12, S13=17, S14=22;
+ var S21=5, S22=9 , S23=14, S24=20;
+ var S31=4, S32=11, S33=16, S34=23;
+ var S41=6, S42=10, S43=15, S44=21;
+ // Steps 1 and 2. Append padding bits and length and convert to words
+ x = ConvertToWordArray(sMessage);
+ // Step 3. Initialise
+ a = 0x67452301; b = 0xEFCDAB89; c = 0x98BADCFE; d = 0x10325476;
+ // Step 4. Process the message in 16-word blocks
+ for (k=0;k<x.length;k+=16) {
+ AA=a; BB=b; CC=c; DD=d;
+ a=FF(a,b,c,d,x[k+0], S11,0xD76AA478);
+ d=FF(d,a,b,c,x[k+1], S12,0xE8C7B756);
+ c=FF(c,d,a,b,x[k+2], S13,0x242070DB);
+ b=FF(b,c,d,a,x[k+3], S14,0xC1BDCEEE);
+ a=FF(a,b,c,d,x[k+4], S11,0xF57C0FAF);
+ d=FF(d,a,b,c,x[k+5], S12,0x4787C62A);
+ c=FF(c,d,a,b,x[k+6], S13,0xA8304613);
+ b=FF(b,c,d,a,x[k+7], S14,0xFD469501);
+ a=FF(a,b,c,d,x[k+8], S11,0x698098D8);
+ d=FF(d,a,b,c,x[k+9], S12,0x8B44F7AF);
+ c=FF(c,d,a,b,x[k+10],S13,0xFFFF5BB1);
+ b=FF(b,c,d,a,x[k+11],S14,0x895CD7BE);
+ a=FF(a,b,c,d,x[k+12],S11,0x6B901122);
+ d=FF(d,a,b,c,x[k+13],S12,0xFD987193);
+ c=FF(c,d,a,b,x[k+14],S13,0xA679438E);
+ b=FF(b,c,d,a,x[k+15],S14,0x49B40821);
+ a=GG(a,b,c,d,x[k+1], S21,0xF61E2562);
+ d=GG(d,a,b,c,x[k+6], S22,0xC040B340);
+ c=GG(c,d,a,b,x[k+11],S23,0x265E5A51);
+ b=GG(b,c,d,a,x[k+0], S24,0xE9B6C7AA);
+ a=GG(a,b,c,d,x[k+5], S21,0xD62F105D);
+ d=GG(d,a,b,c,x[k+10],S22,0x2441453);
+ c=GG(c,d,a,b,x[k+15],S23,0xD8A1E681);
+ b=GG(b,c,d,a,x[k+4], S24,0xE7D3FBC8);
+ a=GG(a,b,c,d,x[k+9], S21,0x21E1CDE6);
+ d=GG(d,a,b,c,x[k+14],S22,0xC33707D6);
+ c=GG(c,d,a,b,x[k+3], S23,0xF4D50D87);
+ b=GG(b,c,d,a,x[k+8], S24,0x455A14ED);
+ a=GG(a,b,c,d,x[k+13],S21,0xA9E3E905);
+ d=GG(d,a,b,c,x[k+2], S22,0xFCEFA3F8);
+ c=GG(c,d,a,b,x[k+7], S23,0x676F02D9);
+ b=GG(b,c,d,a,x[k+12],S24,0x8D2A4C8A);
+ a=HH(a,b,c,d,x[k+5], S31,0xFFFA3942);
+ d=HH(d,a,b,c,x[k+8], S32,0x8771F681);
+ c=HH(c,d,a,b,x[k+11],S33,0x6D9D6122);
+ b=HH(b,c,d,a,x[k+14],S34,0xFDE5380C);
+ a=HH(a,b,c,d,x[k+1], S31,0xA4BEEA44);
+ d=HH(d,a,b,c,x[k+4], S32,0x4BDECFA9);
+ c=HH(c,d,a,b,x[k+7], S33,0xF6BB4B60);
+ b=HH(b,c,d,a,x[k+10],S34,0xBEBFBC70);
+ a=HH(a,b,c,d,x[k+13],S31,0x289B7EC6);
+ d=HH(d,a,b,c,x[k+0], S32,0xEAA127FA);
+ c=HH(c,d,a,b,x[k+3], S33,0xD4EF3085);
+ b=HH(b,c,d,a,x[k+6], S34,0x4881D05);
+ a=HH(a,b,c,d,x[k+9], S31,0xD9D4D039);
+ d=HH(d,a,b,c,x[k+12],S32,0xE6DB99E5);
+ c=HH(c,d,a,b,x[k+15],S33,0x1FA27CF8);
+ b=HH(b,c,d,a,x[k+2], S34,0xC4AC5665);
+ a=II(a,b,c,d,x[k+0], S41,0xF4292244);
+ d=II(d,a,b,c,x[k+7], S42,0x432AFF97);
+ c=II(c,d,a,b,x[k+14],S43,0xAB9423A7);
+ b=II(b,c,d,a,x[k+5], S44,0xFC93A039);
+ a=II(a,b,c,d,x[k+12],S41,0x655B59C3);
+ d=II(d,a,b,c,x[k+3], S42,0x8F0CCC92);
+ c=II(c,d,a,b,x[k+10],S43,0xFFEFF47D);
+ b=II(b,c,d,a,x[k+1], S44,0x85845DD1);
+ a=II(a,b,c,d,x[k+8], S41,0x6FA87E4F);
+ d=II(d,a,b,c,x[k+15],S42,0xFE2CE6E0);
+ c=II(c,d,a,b,x[k+6], S43,0xA3014314);
+ b=II(b,c,d,a,x[k+13],S44,0x4E0811A1);
+ a=II(a,b,c,d,x[k+4], S41,0xF7537E82);
+ d=II(d,a,b,c,x[k+11],S42,0xBD3AF235);
+ c=II(c,d,a,b,x[k+2], S43,0x2AD7D2BB);
+ b=II(b,c,d,a,x[k+9], S44,0xEB86D391);
+ a=AddUnsigned(a,AA); b=AddUnsigned(b,BB); c=AddUnsigned(c,CC); d=AddUnsigned(d,DD);
+ }
+ // Step 5. Output the 128 bit digest
+ var temp= WordToHex(a)+WordToHex(b)+WordToHex(c)+WordToHex(d);
+ return temp.toLowerCase();
+}
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 @@
+
+var psetprops = {
+ fireflix: null,
+ photoset: null,
+ pripic: null,
+
+ settitle: null, setdesc: null,
+ primarypic: null,
+ photos: new Array(),
+ init: function() {
+ this.fireflix = window.arguments[0];
+ this.photoset = window.arguments[1];
+ this.settitle = document.getElementById('set_title');
+ this.settitle.value = this.photoset.title;
+ this.setdesc = document.getElementById('set_desc');
+ this.setdesc.value = this.photoset.description;
+ this.primarypic = document.getElementById('primary_picture');
+ this.primarypic.src =
+ this.fireflix.flickr.get_image_url( this.photoset, 't' );
+ this.primarypic.hidden = false;
+ this.picslist = document.getElementById('primary_picture_list');
+
+ var _this = this;
+ this.fireflix.flickr.api_call(
+ {
+ method: 'flickr.photosets.getPhotos',
+ auth_token: 'default',
+ photoset_id: this.photoset.id
+ }, function(xr) {
+ var x = xr.responseXML;
+ var xp = x.evaluate(
+ '/rsp/photoset/photo', x, null,
+ XPathResult.ORDERED_NODE_ITERATOR_TYPE, null );
+ _this.picslist.removeAllItems(); _this.photos= new Array();
+ var n; while(n=xp.iterateNext()) {
+ _this.photos.push(
+ {
+ id: n.getAttribute('id'),
+ secret: n.getAttribute('secret'),
+ server: n.getAttribute('server')
+ }
+ );
+ var ni = _this.picslist.appendItem(
+ n.getAttribute('title'), _this.photos.length-1
+ );
+ ni.setAttribute('command','cmd_select_picture');
+ if(n.getAttribute('isprimary')==1) {
+ _this.picslist.selectedItem = ni;
+ _this.pripic = _this.photos[_this.photos.length-1];
+ }
+ }
+ _this.picslist.hidden = false;
+ }, function() { }
+ );
+ },
+ on_select_picture: function(ev) {
+ var epic = ev.explicitOriginalTarget;
+ this.picslist.selectedItem = epic;
+ var pic = this.photos[this.picslist.selectedItem.value];
+ this.pripic = pic;
+ this.primarypic.src =
+ this.fireflix.flickr.get_photo_url(
+ pic.server,
+ pic.id,
+ pic.secret,
+ 't'
+ );
+ },
+ on_accept: function() {
+ this.photoset.title =
+ document.getElementById('set_title').value;
+ this.photoset.description =
+ document.getElementById('set_desc').value;
+ this.photoset.server = this.pripic.server;
+ this.photoset.primary = this.pripic.id;
+ this.photoset.secret = this.pripic.secret;
+ this.photoset.dirty = true;
+ return;
+ }
+};
+
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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xsml-stylesheet href="fireflix.css" type="text/css"?>
+<!DOCTYPE dialog SYSTEM "chrome://fireflix/locale/fireflix.dtd">
+<dialog
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ id="photoset_props"
+ buttons="accept,cancel"
+ defaultbutton="accept"
+ title="&photosetprops.title;"
+ onload="psetprops.init()"
+ ondialogaccept="psetprops.on_accept()"
+ >
+
+ <script src="photoset-props.js" type="application/x-javascript"/>
+
+ <commandset>
+ <command id="cmd_select_picture"
+ oncommand="psetprops.on_select_picture(event)"/>
+ </commandset>
+
+ <hbox class="wholething">
+ <vbox>
+ <menulist id="primary_picture_list" hidden="true" sizetopopup="always"/>
+ <hbox pack="center">
+ <box width="100" pack="center">
+ <image id="primary_picture" hidden="true"/>
+ </box>
+ </hbox>
+ </vbox>
+ <vbox flex="1" minwidth="300">
+ <label control="set_title" value="&photosetprops.set_title.label;"/>
+ <textbox id="set_title" />
+ <label control="set_desc" value="&photosetprops.set_desc.label;"/>
+ <textbox id="set_desc" multiline="true" rows="5" />
+ </vbox>
+ </hbox>
+</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 @@
+<?xml version="1.0"?>
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:install-manifest">
+
+ <em:id>@MOZ_EXT_ID@</em:id>
+ <em:name>Fireflix</em:name>
+ <em:version>@VERSION@</em:version>
+ <em:description>Flickr management tool</em:description>
+ <em:creator>Klever Group; http://www.klever.net/</em:creator>
+ <em:homepageURL>http://kin.klever.net/</em:homepageURL>
+ <em:iconURL>chrome://fireflix/content/icons/32x32/fireflix.png</em:iconURL>
+ <em:updateURL>http://kin.klever.net/fireflix/update?v=@VERSION@</em:updateURL>
+ <em:aboutURL>chrome://fireflix/content/about.xul</em:aboutURL>
+
+ <!-- Firefox -->
+ <em:targetApplication>
+ <Description>
+ <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
+ <em:minVersion>1.5</em:minVersion>
+ <em:maxVersion>1.5.0.7</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+
+</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 @@
+LOCALES=en-US
+nobase_xpichromelocale_DATA = \
+ $(addsuffix /fireflix.dtd, \
+ ${LOCALES} \
+ ) \
+ $(addsuffix /fireflix.properties, \
+ ${LOCALES} \
+ )
+
+EXTRA_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 @@
+<!ENTITY % autoconf SYSTEM "chrome://fireflix/content/autoconf.dtd">
+%autoconf;
+
+<!-- About Box -->
+
+<!ENTITY aboutFireflix "About Fireflix" >
+<!ENTITY about.ok.label "OK">
+<!ENTITY about.license.label "License">
+<!ENTITY about.license.tip "Show copying policy">
+
+<!-- COPYING -->
+
+<!ENTITY copying.title "Filreflix: copying policy">
+
+<!-- Sidebar -->
+
+<!ENTITY panel.auth_info "Authorization info">
+<!ENTITY panel.no_auth_info "No auth info available">
+<!ENTITY panel.auth_button "Authorize">
+<!ENTITY panel.auth_complete_button "Authorization complete">
+<!ENTITY panel.flickr_button.label "Flickr">
+<!ENTITY panel.flickr_button.tip "Open Flickr in new tab">
+
+<!ENTITY panel.tabs.search "Search" >
+<!ENTITY panel.tabs.sets "Sets" >
+<!ENTITY panel.tabs.tags "Tags" >
+<!ENTITY panel.tabs.upload "Upload" >
+
+<!ENTITY panel.search.cmd_search.label "Search" >
+<!ENTITY panel.search.search_for.label "Search for:" >
+<!ENTITY panel.search.mode.tagsonly.label "tags">
+<!ENTITY panel.search.mode.tagsonly.tip "Search tags only">
+<!ENTITY panel.search.mode.mine.label "mine">
+<!ENTITY panel.search.col.title.label "Title">
+<!ENTITY panel.search.cmd_search_open.label "Open">
+
+<!ENTITY panel.sets.name.label "Set">
+<!ENTITY panel.sets.name.tip "Photoset name">
+<!ENTITY panel.sets.photos.label "Photos">
+<!ENTITY panel.sets.photos.tip "Number of photos in set">
+
+<!ENTITY panel.sets.cmd_refresh_sets "Refresh">
+<!ENTITY panel.sets.cmd_properties "Properties">
+
+<!ENTITY panel.sets.generate_html "Generate HTML">
+
+<!ENTITY panel.setphotos.title.label "Title">
+<!ENTITY panel.setphotos.title.tip "Picture title">
+<!ENTITY panel.setphotos.taken.label "Taken">
+<!ENTITY panel.setphotos.taken.tip "When the picture was taken">
+<!ENTITY panel.setphotos.upload.label "Uploaded">
+<!ENTITY panel.setphotos.upload.tip "When the picure was uploaded">
+
+<!ENTITY panel.tagslist.tag.label "Tag">
+
+<!ENTITY panel.uploadlist.file.label "File name">
+<!ENTITY panel.uploadlist.title.label "Title">
+<!ENTITY panel.uploadlist.status.label "Status">
+
+<!ENTITY panel.upload_props.filename.label "File:">
+<!ENTITY panel.upload_props.title.label "Title:">
+<!ENTITY panel.upload_props.tags.label "Tags:">
+
+<!ENTITY panel.uploads.upload.label "Upload">
+<!ENTITY panel.uploads.clear.label "Clear">
+<!ENTITY panel.uploads.remove.label "Remove">
+<!ENTITY panel.uploads.add.label "Add">
+<!ENTITY panel.uploads.generate_html "Generate HTML">
+
+<!ENTITY generated.title "Fireflix: Generated content">
+<!ENTITY generated.copy "copy">
+
+<!ENTITY browser.sidebar.label "Fireflix">
+<!ENTITY browser.sidebar.title "Fireflix">
+
+<!ENTITY photosetprops.title "Photoset properties">
+<!ENTITY photosetprops.set_title.label "Photoset title:">
+<!ENTITY photosetprops.set_desc.label "Photoset description:">
+
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 @@
+postUploadPhotoset=Create a new photoset for uploaded photos (cancel if you don't want to create a photoset)
+
+menutitle_Images=Images
+menutitle_Links=Linked to…
+urltype_s=Small square (75x75)
+urltype_t=Thumbnail (fits in 100x100)
+urltype_m=Small (fits in 240x240)
+urltype__=Medium (fits in 500x500)
+urltype_b=Large (fits in 1024x1024)
+urltype_o=Original image
+urltype_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 @@
+<?xml version="1.0"?>
+<RDF:RDF
+ xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <RDF:Description about="urn:mozilla:extension:@MOZ_EXT_ID@">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li resource="urn:mozilla:extension:@MOZ_EXT_ID@:@VERSION@"/>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:@MOZ_EXT_ID@:@VERSION@">
+ <em:version>@VERSION@</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
+ <em:minVersion>1.5</em:minVersion>
+ <em:maxVersion>1.5.0.7</em:maxVersion>
+ <em:updateLink>http://kin.klever.net/dist/@PACKAGE@-@VERSION@.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </RDF:Description>
+
+</RDF:RDF>