Permalink
Browse files

Added simple screenshot editor and Redmine support

  • Loading branch information...
1 parent ad2e62f commit f7941abb4ac656e8e6e8a858d58d5fb3f0a0131b @jun66j5 jun66j5 committed Jun 26, 2012
Showing with 4,963 additions and 509 deletions.
  1. +0 −8 build-chrome.cmd
  2. +28 −0 build-chrome.sh
  3. +4 −3 build-firefox.sh
  4. +12 −0 build.sh
  5. +1 −1 chrome/background.html
  6. +402 −0 chrome/css/editor.css
  7. +1 −0 chrome/css/images
  8. +70 −20 chrome/css/main.css
  9. +1 −1 chrome/css/settings.css
  10. +152 −0 chrome/editor.html
  11. BIN chrome/images/bts/ciklone.png
  12. BIN chrome/images/bts/redmine.png
  13. BIN chrome/images/bts/trac.png
  14. +21 −12 chrome/main.html
  15. +2 −1 chrome/manifest.json
  16. +7 −1 chrome/scripts/background.js
  17. +32 −0 chrome/scripts/bts-driver/ciklone.js
  18. +930 −0 chrome/scripts/bts-driver/redmine.js
  19. +395 −0 chrome/scripts/bts-driver/trac.js
  20. +33 −0 chrome/scripts/bts.js
  21. +30 −8 chrome/scripts/content.js
  22. +1,711 −0 chrome/scripts/editor.js
  23. +90 −3 chrome/scripts/http.js
  24. +28 −0 chrome/scripts/libs/formatstring.js
  25. +15 −15 chrome/scripts/libs/jquery-1.5.2.min.js
  26. +1 −46 chrome/scripts/libs/xmlrpc_lib.js
  27. +77 −0 chrome/scripts/local-file.js
  28. +196 −298 chrome/scripts/main.js
  29. +3 −6 chrome/scripts/popup.js
  30. +12 −2 chrome/scripts/screenshot-sender.js
  31. +20 −2 chrome/scripts/settings-manager.js
  32. +32 −40 chrome/scripts/settings.js
  33. 0 chrome/scripts/{libs → }/sjcl.js
  34. +64 −0 chrome/scripts/startup-editor.js
  35. +60 −10 chrome/scripts/startup-main.js
  36. +23 −3 chrome/scripts/startup-settings.js
  37. +7 −4 chrome/settings.html
  38. +1 −0 chrome/stamps
  39. +1 −0 firefox/chrome/content/css/editor.css
  40. BIN firefox/chrome/content/css/images/000000.png
  41. BIN firefox/chrome/content/css/images/000080.png
  42. BIN firefox/chrome/content/css/images/0000FF.png
  43. BIN firefox/chrome/content/css/images/008000.png
  44. BIN firefox/chrome/content/css/images/008080.png
  45. BIN firefox/chrome/content/css/images/00FF00.png
  46. BIN firefox/chrome/content/css/images/00FFFF.png
  47. BIN firefox/chrome/content/css/images/800000.png
  48. BIN firefox/chrome/content/css/images/800080.png
  49. BIN firefox/chrome/content/css/images/808000.png
  50. BIN firefox/chrome/content/css/images/808080.png
  51. BIN firefox/chrome/content/css/images/C0C0C0.png
  52. BIN firefox/chrome/content/css/images/FF0000.png
  53. BIN firefox/chrome/content/css/images/FF00FF.png
  54. BIN firefox/chrome/content/css/images/FFFF00.png
  55. BIN firefox/chrome/content/css/images/FFFFFF.png
  56. BIN firefox/chrome/content/css/images/Redmine24.png
  57. BIN firefox/chrome/content/css/images/Trac24.png
  58. BIN firefox/chrome/content/css/images/arrow_1.png
  59. BIN firefox/chrome/content/css/images/arrow_2.png
  60. BIN firefox/chrome/content/css/images/bar.png
  61. BIN firefox/chrome/content/css/images/bar_c.png
  62. BIN firefox/chrome/content/css/images/bar_l.png
  63. BIN firefox/chrome/content/css/images/bar_r.png
  64. BIN firefox/chrome/content/css/images/bk_h.png
  65. BIN firefox/chrome/content/css/images/bk_v.png
  66. BIN firefox/chrome/content/css/images/border.png
  67. BIN firefox/chrome/content/css/images/box_1.png
  68. BIN firefox/chrome/content/css/images/box_2.png
  69. BIN firefox/chrome/content/css/images/circle.png
  70. BIN firefox/chrome/content/css/images/cursor/arrow.png
  71. BIN firefox/chrome/content/css/images/cursor/both-arrow.png
  72. BIN firefox/chrome/content/css/images/cursor/free.png
  73. BIN firefox/chrome/content/css/images/cursor/line.png
  74. BIN firefox/chrome/content/css/images/cursor/move.png
  75. BIN firefox/chrome/content/css/images/delete.png
  76. BIN firefox/chrome/content/css/images/edit.png
  77. BIN firefox/chrome/content/css/images/free.png
  78. BIN firefox/chrome/content/css/images/fulmo24.png
  79. BIN firefox/chrome/content/css/images/l_2px.png
  80. BIN firefox/chrome/content/css/images/l_4px.png
  81. BIN firefox/chrome/content/css/images/l_6px.png
  82. BIN firefox/chrome/content/css/images/l_8px.png
  83. BIN firefox/chrome/content/css/images/l_sel.png
  84. BIN firefox/chrome/content/css/images/line.png
  85. BIN firefox/chrome/content/css/images/number.png
  86. BIN firefox/chrome/content/css/images/openable.png
  87. BIN firefox/chrome/content/css/images/paint.png
  88. BIN firefox/chrome/content/css/images/point.png
  89. BIN firefox/chrome/content/css/images/save.png
  90. BIN firefox/chrome/content/css/images/soff.png
  91. BIN firefox/chrome/content/css/images/text.png
  92. +1 −0 firefox/chrome/content/editor.html
  93. +1 −0 firefox/chrome/content/scripts/bts-driver
  94. +1 −0 firefox/chrome/content/scripts/bts.js
  95. +1 −0 firefox/chrome/content/scripts/editor.js
  96. +65 −7 firefox/chrome/content/scripts/http.js
  97. +64 −0 firefox/chrome/content/scripts/local-file.js
  98. +28 −0 firefox/chrome/content/scripts/login.js
  99. +40 −0 firefox/chrome/content/scripts/settings-manager.js
  100. +1 −0 firefox/chrome/content/scripts/sjcl.js
  101. +65 −0 firefox/chrome/content/scripts/startup-editor.js
  102. +57 −6 firefox/chrome/content/scripts/startup-main.js
  103. +35 −0 firefox/chrome/content/scripts/startup-settings.js
  104. +31 −0 firefox/chrome/content/scripts/startup.js
  105. +7 −5 firefox/chrome/content/settings.xul
  106. BIN firefox/chrome/content/stamps/en/approved.png
  107. BIN firefox/chrome/content/stamps/en/as_is.png
  108. BIN firefox/chrome/content/stamps/en/confidential.png
  109. BIN firefox/chrome/content/stamps/en/copy.png
  110. BIN firefox/chrome/content/stamps/en/departmental.png
  111. BIN firefox/chrome/content/stamps/en/done.png
  112. BIN firefox/chrome/content/stamps/en/draft.png
  113. BIN firefox/chrome/content/stamps/en/experimental.png
  114. BIN firefox/chrome/content/stamps/en/expired.png
  115. BIN firefox/chrome/content/stamps/en/final.png
  116. BIN firefox/chrome/content/stamps/en/for_comment.png
  117. BIN firefox/chrome/content/stamps/en/not_approved.png
  118. BIN firefox/chrome/content/stamps/en/not_public_release.png
  119. BIN firefox/chrome/content/stamps/en/public_release.png
  120. BIN firefox/chrome/content/stamps/en/sample.png
  121. BIN firefox/chrome/content/stamps/en/to_do.png
  122. BIN firefox/chrome/content/stamps/en/top_secret.png
  123. BIN firefox/chrome/content/stamps/jp/approved.png
  124. BIN firefox/chrome/content/stamps/jp/as_is.png
  125. BIN firefox/chrome/content/stamps/jp/confidential.png
  126. BIN firefox/chrome/content/stamps/jp/copy.png
  127. BIN firefox/chrome/content/stamps/jp/departmental.png
  128. BIN firefox/chrome/content/stamps/jp/done.png
  129. BIN firefox/chrome/content/stamps/jp/draft.png
  130. BIN firefox/chrome/content/stamps/jp/experimental.png
  131. BIN firefox/chrome/content/stamps/jp/expired.png
  132. BIN firefox/chrome/content/stamps/jp/final.png
  133. BIN firefox/chrome/content/stamps/jp/for_comment.png
  134. BIN firefox/chrome/content/stamps/jp/not_approved.png
  135. BIN firefox/chrome/content/stamps/jp/not_public_release.png
  136. BIN firefox/chrome/content/stamps/jp/public_release.png
  137. BIN firefox/chrome/content/stamps/jp/sample.png
  138. BIN firefox/chrome/content/stamps/jp/to_do.png
  139. BIN firefox/chrome/content/stamps/jp/top_secret.png
  140. BIN firefox/chrome/content/stamps/number_bg.png
  141. +4 −4 firefox/install.rdf
  142. +99 −2 locale.json
  143. +1 −1 tools/createLocaleFiles.rb
View
8 build-chrome.cmd
@@ -1,8 +0,0 @@
-@echo off
-setlocal
-set D=%~dp0
-REM set CHROME=C:\usr\apps\GoogleChrome\12.0.742.122\chrome.exe
-REM set CHROME_OPTS=--user-data-dir=C:\usr\apps\GoogleChrome\12.0.742.122\profile
-if not exist "%CHROME%" set CHROME=chrome.exe
-"%CHROME%" %CHROME_OPTS% --pack-extension="%D%chrome" --pack-extension-key="%D%fulmo.pem"
-endlocal
View
28 build-chrome.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+# Based on bash-script in http://code.google.com/chrome/extensions/crx.html
+set -e
+dir="${0%/*}"
+[ -z "$dir" ] && dir="$PWD"
+name=fulmo
+key="$dir/$name.pem"
+output="$dir/release/$name-chrome.crx"
+pub="$dir/$name-chrome.pub"
+sig="$dir/$name-chrome.sig"
+zip="$dir/release/$name-chrome.zip"
+trap 'rm -f "$pub" "$sig"' EXIT
+(cd "$dir/chrome" && zip -qr -9 -X "$zip" .)
+openssl sha1 -sha1 -binary -sign "$key" < "$zip" > "$sig"
+openssl rsa -pubout -outform DER < "$key" > "$pub" 2>/dev/null
+byte_swap () {
+ # Take "abcdefgh" and return it as "ghefcdab"
+ echo "${1:6:2}${1:4:2}${1:2:2}${1:0:2}"
+}
+crmagic_hex="4372 3234" # Cr24
+version_hex="0200 0000" # 2
+pub_len_hex=$(byte_swap $(printf '%08x\n' $(ls -l "$pub" | awk '{print $5}')))
+sig_len_hex=$(byte_swap $(printf '%08x\n' $(ls -l "$sig" | awk '{print $5}')))
+(
+ echo "$crmagic_hex $version_hex $pub_len_hex $sig_len_hex" | xxd -r -p
+ cat "$pub" "$sig" "$zip"
+) > "$output"
+echo "Created $output" >&2
View
7 build-firefox.sh
@@ -1,8 +1,9 @@
#! /bin/sh
set -e
-dir="${0/*}"
+dir="${0%/*}"
[ -z "$dir" ] && dir="$PWD"
+output="$dir/release/fulmo-firefox.xpi"
cd "$dir/firefox"
-output=../fulmo-firefox.xpi
rm -f -- "$output" || :
-zip -r "$output" $(find -L . -type f \! -path '*/.svn/*')
+zip -qr -9 -X "$output" $(find -L . -type f \! -path '*/.svn/*')
+echo "Created $output" >&2
View
12 build.sh
@@ -0,0 +1,12 @@
+#! /bin/sh
+set -e
+dir="${0%/*}"
+case "$dir" in
+ /*) ;;
+ '') dir="$PWD";;
+ *) dir="$PWD/$dir";;
+esac
+[ -d "$dir/release" ] || mkdir "$dir/release"
+"$dir/tools/createLocaleFiles.rb"
+"$dir/build-firefox.sh"
+"$dir/build-chrome.sh"
View
2 chrome/background.html
@@ -4,7 +4,7 @@
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<title>background page</title>
-<script type="text/javascript" src="scripts/libs/sjcl.js" ></script>
+<script type="text/javascript" src="scripts/sjcl.js" ></script>
<script type="text/javascript" src="scripts/settings-manager.js" ></script>
<script type="text/javascript" src="scripts/background.js"></script>
<style type="text/css">
View
402 chrome/css/editor.css
@@ -0,0 +1,402 @@
+* {
+ font-family: "Arial","Meiryo","メイリオ","MS-PGothic","MS Pゴシック","Osaka","verdana","palatino","sans-serif";
+ font-size: 12px;
+}
+
+body {
+ margin: 0;
+ padding: 0;
+ background: url(images/bk_v.png);
+ position: relative;
+}
+
+#tool-bar-wrapper {
+ z-index: 999;
+ background: url(images/bar_c.png);
+ position: fixed;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 46px;
+ width: 100%;
+ box-shadow: 0 0 15px rgba(0,0,0,0.5);
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ user-select: none;
+}
+
+#tool-bar {
+/* width: 100%;*/
+ height: 100%;
+ margin: 0 auto 0 auto;
+ padding: 0;
+ list-style: none;
+ min-width: 720px;
+ text-align: center;
+ white-space: nowrap;
+}
+
+#tool-bar-wrapper-left {
+ float: left;
+ background: url(images/bar_l.png);
+ width: 16px;
+ height: 100%;
+}
+
+#tool-bar-wrapper-right {
+ float: right;
+ background: url(images/bar_r.png);
+ width: 16px;
+ height: 100%;
+}
+
+#tool-bar li {
+ display: inline-block;
+}
+
+#tool-bar a.menu {
+ display: block;
+ float: left;
+ font-size: 12px;
+ background-color: transparent;
+ border: 1px transparent solid;
+ margin: 8px 4px 0px 4px;
+ text-decoration: none;
+ color: #f4f4f4;
+ text-shadow: 0 -1px 0 rgba(0,0,0,0.25);
+ border-radius: 2px;
+ height: 28px;
+}
+#tool-bar a.menu:hover {
+ text-shadow: 0 -1px 0 rgba(224,224,224,0.25);
+}
+
+#tool-bar a.menu.separator {
+ margin-left: 16px;
+}
+
+#tool-bar a.menu.icon {
+ padding: 0;
+}
+
+#tool-bar a.icon {
+ padding: 0;
+}
+
+#tool-bar a.icon {
+ text-indent: -1000em;
+ overflow: hidden;
+ height: 28px;
+ width: 28px;
+ background-repeat: no-repeat;
+ background-position: -3px 0px;
+}
+
+#tool-bar li a.menu.selected {
+ outline: none;
+ background-color: #ccc;
+}
+
+#tool-bar li a.menu.icon.selected {
+}
+
+#tool-bar a:hover {
+ color: #111;
+ background-color: #ccc;
+}
+
+#tool-bar a.menu span {
+ display: block;
+ padding: 7px 4px 3px 4px;
+ font-weight: bold;
+}
+
+#tool-bar a.menu span.openable {
+ background-image: url(images/openable.png);
+ background-position: 100% 100%;
+ background-repeat: no-repeat;
+ overflow: hidden;
+}
+
+#tool-bar a.menu.icon span.openable {
+ text-indent: -1000em;
+ width: 100%;
+ height: 100%;
+ padding: 0;
+}
+
+
+#tool-bar li a.menu.icon.selected:hover {
+ background-position: -3px -30px;
+}
+
+#tool-bar li a.menu.icon:hover {
+ background-position: -3px -30px;
+}
+
+#tool-bar li a.txt {
+ color: #333;
+ font-weight: bold;
+ text-shadow: 0 -1px 0 rgba(224,224,224,0.25);
+}
+#tool-bar li .selector a.txt {
+ display: block;
+ width: 100px;
+ text-align: center;
+}
+
+#tool-bar li .selector a.txt:hover, #tool-bar li .selector a.txt.selected {
+}
+
+#tool-bar li #pen-width a:hover, #tool-bar li #pen-width a.selected {
+}
+
+#tool-bar li #pen-width a {
+ display: block;
+ width: 60px;
+ text-indent: -1000em;
+ color: #333;
+ background-repeat: no-repeat;
+ height: 4px;
+ background-position: 50% 50%;
+ overflow: hidden;
+}
+
+#tool-bar .selector a.selected, .multi-selector a.selected {
+ outline: none;
+ background-position: -3px -30px;
+ background-color: #aaa;
+ color: #eee;
+ text-shadow: 0 -1px 0 rgba(0,0,0,0.25);
+}
+
+#tool-bar .selector a:hover, .multi-selector a:hover {
+ background-position: -3px 0;
+ background-color: #aaa;
+ color: #eee;
+ text-shadow: 0 -1px 0 rgba(0,0,0,0.25);
+}
+
+#pen-color a {
+ position: relative;
+ display: block;
+ float: left;
+ width: 16px;
+ height: 16px;
+ margin: 2px;
+ padding: 0;
+ font-size: 13px;
+ overflow: hidden;
+ color: #fff;
+ border: 1px solid rgba(0,0,0,0.3);
+ border-radius: 2px;
+ outline: none;
+}
+#pen-color a.selected.darktext,
+#pen-color a.darktext:hover {
+ color: #000;
+}
+#pen-color a:nth-child(2n+1) {
+ clear: both;
+}
+#pen-color a.selected, #pen-color a:hover {
+ border-color: rgba(0,0,0,0.7);
+}
+#pen-color a.selected::before, #pen-color a:hover::before {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 16px;
+ height: 16px;
+ line-height: 16px;
+ text-align: center;
+ content: "";
+}
+
+#width-2 {
+ background-image: url(images/l_2px.png);
+}
+#width-4 {
+ background-image: url(images/l_4px.png);
+}
+#width-6 {
+ background-image: url(images/l_6px.png);
+}
+#width-8 {
+ background-image: url(images/l_8px.png);
+}
+
+
+#btn-sel a.menu {
+ background-image: url(images/point.png);
+}
+
+#btn-poly a.menu {
+ background-image: url(images/box_2.png);
+}
+
+#btn-edit a.menu {
+ background-image: url(images/soff.png);
+}
+
+#btn-text a.menu {
+ background-image: url(images/text.png);
+}
+
+#btn-line a.menu {
+ background-image: url(images/line.png);
+}
+
+#btn-arrow a.menu {
+ background-image: url(images/arrow_1.png);
+}
+
+#btn-both-arrow a.menu {
+ background-image: url(images/arrow_2.png);
+}
+
+#btn-free a.menu {
+ background-image: url(images/free.png);
+}
+
+#btn-color a.menu {
+ background-image: url(images/0000FF.png);
+}
+
+#btn-pen-width a.menu {
+ background-image: url(images/l_sel.png);
+}
+
+#btn-num a.menu {
+ background-image: url(images/number.png);
+}
+
+#paint-mode-edge {
+ background-image: url(images/border.png);
+}
+
+#paint-mode-all {
+ width: 35px;
+ background-image: url(images/paint.png);
+}
+
+#poly-rect {
+ width: 35px;
+ background-image: url(images/box_1.png);
+}
+
+#poly-radius-rect {
+ width: 35px;
+ background-image: url(images/box_2.png);
+}
+
+#poly-ellipse {
+ width: 35px;
+ background-image: url(images/circle.png);
+}
+
+#btn-lib .ui-pane a img {
+ padding: 2px;
+}
+
+#btn-lib .ui-pane a:hover img {
+ background: #fff;
+}
+
+div.ui-pane {
+ position: fixed;
+ display: none;
+ margin: 0;
+ background: #ccc;
+ border: 1px solid rgba(0,0,0,0.2);
+ color: #333;
+ border-radius: 3px;
+ font-size: 12px;
+ padding: 4px;
+ box-shadow: 0 4px 4px rgba(0,0,0,0.2);
+}
+
+.selector a, .multi-selector a {
+ text-decoration: none;
+ display: inline-block;
+ padding: 4px;
+ background: #ccc;
+ margin: 1px;
+ border-radius: 3px;
+ border: 1px transparent solid;
+}
+
+#canvas-wrapper {
+ position: relative;
+ margin: 58px auto 8px auto;
+}
+
+#ctrl-canvas {
+ position: absolute;
+ top: 0;
+ left 0;
+ box-shadow: 0 0 15px rgba(0,0,0,0.5);
+}
+
+div#margin {
+ height: 32px;
+}
+
+div.drag-box {
+ position: absolute;
+ display: none;
+ width: 6px;
+ height: 6px;
+ top: 0;
+ left: 0;
+ border: 1px #fff solid;
+ background: #000;
+ z-index: 10;
+}
+
+#drag-TL { cursor: nw-resize}
+#drag-T { cursor: n-resize}
+#drag-TR { cursor: ne-resize}
+#drag-L { cursor: w-resize}
+/* #drag-Delete { cursor: }*/
+#drag-R { cursor: e-resize}
+#drag-BL { cursor: sw-resize}
+#drag-B { cursor: s-resize}
+#drag-BR { cursor: se-resize}
+
+#prim-delete {
+ position: absolute;
+ display: none;
+ width: 15px;
+ height: 15px;
+ top: 0;
+ left: 0;
+ border: 1px #666 solid;
+ background: #fff url(images/delete.png);
+ z-index: 10;
+}
+
+
+#save-file {
+ display: none;
+}
+
+#work {
+ display: none;
+}
+
+#text-editor {
+ font-family: "Arial","Meiryo","メイリオ","MS-PGothic","MS Pゴシック","Osaka","verdana","palatino","sans-serif";
+ font-size: 12px;
+ display: none;
+ width: auto;
+ height: auto;
+ top: 100px;
+ left: 100px;
+ background: #fff;
+ position: absolute;
+ z-index: 100;
+ border: none;
+ padding: 2px;
+ overflow: hidden;
+}
View
1 chrome/css/images
View
90 chrome/css/main.css
@@ -1,5 +1,5 @@
* {
- font-family: "Arial", "san-serif", "メイリオ", "MS Pゴシック","Osaka","verdana","palatino";
+ font-family: "Arial","メイリオ","MS Pゴシック","Osaka","verdana","palatino","sans-serif";
font-size: 12px;
}
@@ -17,16 +17,17 @@ body {
}
form {
+ padding-bottom: 40px;
}
#screenshot-sender-header {
height: 32px;
}
-#screenshot-sender-main-body-filed {
+#screenshot-sender-body-field {
}
-#screenshot-sender-main-body-filed p {
+#screenshot-sender-body-field p {
margin: 0;
padding: 4px 16px 0 4px;
}
@@ -35,11 +36,11 @@ form {
margin: 4px 0 0 0;
}
-#screenshot-sender-main-body-filed p {
+#screenshot-sender-body-field p {
position:relative;
}
-.screenshot-sender-main-item-label {
+.screenshot-sender-item-label {
position: absolute;
z-index: 1;
top: 8px;
@@ -49,7 +50,7 @@ form {
font-weight: normal;
}
-#screenshot-sender-main-body-filed input {
+#screenshot-sender-body-field input {
position: relative;
z-index: 3;
border-radius: 8px;
@@ -61,7 +62,7 @@ form {
background: transparent;
}
-#screenshot-sender-main-body-filed textarea {
+#screenshot-sender-body-field textarea {
position: relative;
z-index: 3;
border-radius: 8px;
@@ -79,7 +80,8 @@ form {
.screenshot-sender-property {
display: block;
- margin: 0 0 0 108px;
+ margin: 0 4px 0 32%;
+ width: 60%;
}
.screenshot-sender-property label {
@@ -111,7 +113,7 @@ h2 {
font-size: 12px;
}
-#screenshot-sender-main-image-filed {
+#screenshot-sender-image-field {
position: absolute;
top: 8px;
left: 100%;
@@ -128,54 +130,101 @@ h2 {
overflow: hidden;
}
-#screenshot-sender-main-image-filed p {
+#screenshot-sender-image-field p {
padding: 0;
margin: 0 0 12px 0;
+ width: 120px;
}
-#screenshot-sender-main-image-filed p span {
+#screenshot-sender-image-field p span {
color: #ffffff;
padding-left: 20px;
background: no-repeat;
text-decoration: underline;
cursor: pointer;
}
-#screenshot-sender-main-property-filed {
+#screenshot-sender-image-field ul {
+ margin: 0 0 0 120px;
+ padding: 0;
+}
+
+#screenshot-sender-image-field ul li {
+ display: inline;
+ float: right;
+ padding: 0;
+ margin: 0 0 0 4px;
+}
+
+#screenshot-sender-image-field ul li a {
+ color: #333;
+ background: #ccc;
+ padding: 2px;
+ border: #666 1px solid;
+ border-radius: 4px;
+ text-decoration: none;
+}
+
+#screenshot-sender-image-field ul li a:hover {
+ color: #f00;
+ background: #ccc;
+}
+
+
+#screenshot-sender-property-field {
clear: both;
}
-#screenshot-sender-main-image {
+#screenshot-sender-image {
-moz-box-shadow: 2px 2px 10px #000;
-webkit-box-shadow: 2px 2px 10px #000;
}
-#property-block {
+#screenshot-sender-image-field p span#screenshot-sender-image-open {
+ text-decoration: none;
+}
+
+#screenshot-sender-image-field-save, #screenshot-sender-image-field-edit {
+ display: inline-block;
+ width: 16px;
+ height: 16px;
+ background: transparent none no-repeat center;
+ cursor: pointer;
+ opacity: 0.75;
+}
+#screenshot-sender-image-field-save:hover, #screenshot-sender-image-field-edit:hover {
+ opacity: 1;
+}
+#screenshot-sender-image-field-save { background-image: url(images/save.png) }
+#screenshot-sender-image-field-edit { background-image: url(images/edit.png) }
+
+#screenshot-sender-property-list {
margin: 12px 0 0 0 ;
padding: 0 0 12px 0;
}
-#property-block li {
+#screenshot-sender-property-list li {
list-style: none;
float: left;
- width: 276px;
+ width: 40%;
+ min-width: 270px;
min-height: 22px;
padding: 2px;
}
-#property-block input {
+#screenshot-sender-property-list input {
border: 1px #ccc solid;
}
-#property-block select {
+#screenshot-sender-property-list select {
border: 1px #ccc solid;
}
.screenshot-sender-properties-label {
float: left;
font-weight: bold;
display: inline-block;
- width: 100px;
+ width: 30%;
vertical-align: top;
text-align: right;
padding: 2px 4px 2px 2px;
@@ -211,7 +260,7 @@ h2 {
background: #fff;
}
-#screenshot-sender-main-submit-wrapeer {
+#screenshot-sender-submit-wrapeer {
padding: 0;
position: absolute;
text-align: center;
@@ -239,3 +288,4 @@ h2 {
display: inline-block;
width: 100px;
}
+
View
2 chrome/css/settings.css
@@ -1,5 +1,5 @@
* {
- font-family: "Arial", "san-serif", "メイリオ", "MS Pゴシック","Osaka","verdana","palatino";
+ font-family: "Arial","メイリオ","MS Pゴシック","Osaka","verdana","palatino","sans-serif";
}
body {
View
152 chrome/editor.html
@@ -0,0 +1,152 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<title class="i18n">fulmo_editor_title</title>
+<link rel="stylesheet" type="text/css" href="css/themes/d/jquery-ui-1.8.14.custom.css" />
+<link rel="stylesheet" type="text/css" href="css/jquery.multiselect.css" />
+<link rel="stylesheet" type="text/css" href="css/editor.css?9" />
+<link rel="INDEX" href="./" />
+<meta http-equiv="Content-Script-Type" content="text/javascript" />
+<script type="text/javascript" src="scripts/libs/jquery-1.5.2.min.js"></script>
+<script type="text/javascript" src="scripts/libs/jquery-ui-1.8.12.custom.min.js" ></script>
+<script type="text/javascript" src="scripts/libs/jquery.multiselect.js" ></script>
+<script type="text/javascript" src="scripts/libs/jquery.infieldlabel.js" ></script>
+<script type="text/javascript" src="scripts/editor.js"></script>
+<script type="text/javascript" src="scripts/local-file.js"></script>
+<script type="text/javascript" src="scripts/startup-editor.js"></script>
+<script type="text/javascript">/*<![CDATA[*/
+ jQuery(document).ready(function($) {
+ $('a').attr('href', 'javascript:void(0)');
+ $('#tool-bar a').each(function() {
+ if (this.getAttribute('draggable') === null) {
+ this.setAttribute('draggable', 'false');
+ }
+ });
+ });
+/*]]>*/</script>
+</head>
+<body>
+<div id="tool-bar-wrapper">
+<div id="tool-bar-wrapper-left"></div>
+<div id="tool-bar-wrapper-right"></div>
+<ul id="tool-bar">
+ <li class="mode-selector" id="btn-sel"><a class="menu icon">Select</a>
+ </li>
+ <li class="mode-selector" id="btn-poly"><a class="menu icon selected"><span class="openable">Poly</span></a>
+ <div class="ui-pane">
+ <div id="poly-type" class="selector typify">
+ <a id="poly-rect" class="icon">Rect</a><br />
+ <a id="poly-radius-rect" class="icon selected">Radical Rect</a><br />
+ <a id="poly-ellipse" class="icon">Ellipse</a><br />
+ </div>
+ <hr />
+ <div id="paint-mode" class="selector">
+ <a id="paint-mode-edge" class="icon selected">Edge</a><br />
+ <a id="paint-mode-all" class="icon">Paint</a>
+ </div>
+ </div>
+ </li>
+ <li class="mode-selector" id="btn-edit"><a class="menu icon">Correction</a>
+ </li>
+ <li class="mode-selector" id="btn-text"><a class="menu icon"><span class="openable">Text</span></a>
+ <div class="ui-pane">
+ <div id="text-size" class="selector">
+ <a id="text-size-10" class="i18n txt">fulmo_editor_text_small</a>
+ <a id="text-size-12" class="i18n txt selected">fulmo_editor_text_middle</a>
+ <a id="text-size-16" class="i18n txt">fulmo_editor_text_large</a>
+ <a id="text-size-20" class="i18n txt">fulmo_editor_text_xlarge</a>
+ </div>
+ </div>
+ </li>
+ <li class="mode-selector" id="btn-line"><a class="menu icon">Line</a> </li>
+ <li class="mode-selector" id="btn-arrow"><a class="menu icon">Arrow</a></li>
+ <li class="mode-selector" id="btn-both-arrow"><a class="menu icon">Both arrow</a></li>
+ <li class="mode-selector" id="btn-free"><a class="menu icon">Free hand</a></li>
+
+ <li id="btn-pen-width"><a class="menu icon"><span class="openable">Line type</span></a>
+ <div class="ui-pane">
+ <div id="pen-width" class="selector">
+ <a id="width-2">Thin</a>
+ <a id="width-4" class="selected" >Middle</a>
+ <a id="width-6">Thick</a>
+ <a id="width-8">Very thick</a>
+ </div>
+ </div>
+ </li>
+
+ <li id="btn-color"><a class="menu icon"><span class="openable">Color</span></a>
+ <div class="ui-pane">
+ <div id="pen-color" class="selector">
+ <a id="color-000000"></a>
+ <a id="color-808080"></a>
+ <a id="color-FF0000" class="selected"></a>
+ <a id="color-000080"></a>
+ <a id="color-FFFF00" class="darktext"></a>
+ <a id="color-808000"></a>
+ <a id="color-00FF00" class="darktext"></a>
+ <a id="color-008000"></a>
+ <a id="color-00FFFF" class="darktext"></a>
+ <a id="color-008080"></a>
+ <a id="color-0000FF"></a>
+ <a id="color-000080"></a>
+ <a id="color-FF00FF"></a>
+ <a id="color-800080"></a>
+ <a id="color-FFFFFF" class="darktext"></a>
+ <a id="color-C0C0C0" class="darktext"></a>
+ </div>
+ </div>
+ </li>
+
+ <li class="mode-selector" id="btn-num"><a class="menu icon">Number</a>
+ <li class="mode-selector" id="btn-lib"><a class="menu"><span class="openable i18n">fulmo_editor_btn_library</span></a>
+ <div class="ui-pane">
+ <a><img src="stamps/en/approved.png" width="100" /></a><a><img src="stamps/jp/approved.png" width="100" /></a><br />
+ <a><img src="stamps/en/as_is.png" width="100" /></a><a><img src="stamps/jp/as_is.png" width="100" /></a><br />
+ <a><img src="stamps/en/confidential.png" width="100" /></a><a><img src="stamps/jp/confidential.png" width="100" /></a><br />
+ <a><img src="stamps/en/copy.png" width="100" /></a><a><img src="stamps/jp/copy.png" width="100" /></a><br />
+ <a><img src="stamps/en/departmental.png" width="100" /></a><a><img src="stamps/jp/departmental.png" width="100" /></a><br />
+ <a><img src="stamps/en/done.png" width="100" /></a><a><img src="stamps/jp/done.png" width="100" /></a><br />
+ <a><img src="stamps/en/draft.png" width="100" /></a><a><img src="stamps/jp/draft.png" width="100" /></a><br />
+ <a><img src="stamps/en/experimental.png" width="100" /></a><a><img src="stamps/jp/experimental.png" width="100" /></a><br />
+ <a><img src="stamps/en/expired.png" width="100" /></a><a><img src="stamps/jp/expired.png" width="100" /></a><br />
+ <a><img src="stamps/en/final.png" width="100" /></a><a><img src="stamps/jp/final.png" width="100" /></a><br />
+ <a><img src="stamps/en/for_comment.png" width="100" /></a><a><img src="stamps/jp/for_comment.png" width="100" /></a><br />
+ <a><img src="stamps/en/not_approved.png" width="100" /></a><a><img src="stamps/jp/not_approved.png" width="100" /></a><br />
+ <a><img src="stamps/en/not_public_release.png" width="100" /></a><a><img src="stamps/jp/not_public_release.png" width="100" /></a><br />
+ <a><img src="stamps/en/public_release.png" width="100" /></a><a><img src="stamps/jp/public_release.png" width="100" /></a><br />
+ <a><img src="stamps/en/sample.png" width="100" /></a><a><img src="stamps/jp/sample.png" width="100" /></a><br />
+ <a><img src="stamps/en/to_do.png" width="100" /></a><a><img src="stamps/jp/to_do.png" width="100" /></a><br />
+ <a><img src="stamps/en/top_secret.png" width="100" /></a><a><img src="stamps/jp/top_secret.png" width="100" /></a><br />
+ </div>
+ </li>
+
+ <li><a class="menu separator" id="btn-drag" draggable="true"><span class="i18n">fulmo_editor_btn_drag</span></a></li>
+ <li><a class="menu" id="btn-save" draggable="true"><span class="i18n">fulmo_editor_btn_save</span></a></li>
+ <li><a class="menu" id="btn-send"><span class="i18n">fulmo_editor_btn_send</span></a></li>
+</ul>
+</div>
+<div id="tool-window">
+</div>
+<div id="canvas-wrapper">
+ <canvas id="main-canvas"></canvas>
+ <div id="layers">
+ </div>
+ <canvas id="ctrl-canvas">
+ </canvas>
+ <div id="primitive-tools">
+ <div id="drag-TL" class="drag-box"></div>
+ <div id="drag-T" class="drag-box"></div>
+ <div id="drag-TR" class="drag-box"></div>
+ <div id="drag-L" class="drag-box"></div>
+ <div id="drag-R" class="drag-box"></div>
+ <div id="drag-BL" class="drag-box"></div>
+ <div id="drag-B" class="drag-box"></div>
+ <div id="drag-BR" class="drag-box"></div>
+ <div id="prim-delete"></div>
+ </div>
+</div>
+<div><textarea id="text-editor" cols="10" rows="5"></textarea></div>
+<div id="work"><img id="num-bg" src="stamps/number_bg.png" /></div>
+</body>
View
BIN chrome/images/bts/ciklone.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN chrome/images/bts/redmine.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN chrome/images/bts/trac.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
33 chrome/main.html
@@ -6,7 +6,7 @@
<title class="i18n">fulmo_main_title</title>
<link rel="stylesheet" type="text/css" href="css/themes/d/jquery-ui-1.8.14.custom.css" />
<link rel="stylesheet" type="text/css" href="css/jquery.multiselect.css" />
-<link rel="stylesheet" type="text/css" href="css/main.css" />
+<link rel="stylesheet" type="text/css" href="css/main.css?2" />
<link rel="INDEX" href="./" />
<meta http-equiv="Content-Script-Type" content="text/javascript" />
<script type="text/javascript" src="scripts/http.js"></script>
@@ -15,35 +15,44 @@
<script type="text/javascript" src="scripts/libs/jquery-ui-1.8.12.custom.min.js" ></script>
<script type="text/javascript" src="scripts/libs/jquery.multiselect.js" ></script>
<script type="text/javascript" src="scripts/libs/jquery.infieldlabel.js" ></script>
-<script type="text/javascript" src="scripts/libs/sjcl.js" ></script>
+<script type="text/javascript" src="scripts/sjcl.js" ></script>
+<script type="text/javascript" src="scripts/bts-driver/trac.js" ></script>
+<script type="text/javascript" src="scripts/bts-driver/ciklone.js" ></script>
+<script type="text/javascript" src="scripts/bts-driver/redmine.js" ></script>
+<script type="text/javascript" src="scripts/bts.js" ></script>
<script type="text/javascript" src="scripts/settings-manager.js" ></script>
<script type="text/javascript" src="scripts/main.js"></script>
+<script type="text/javascript" src="scripts/local-file.js"></script>
<script type="text/javascript" src="scripts/startup-main.js"></script>
</head>
<body>
<form method="post" action="#">
<div id="screenshot-sender-header" >
<img id="screenshot-sender-application-image" width="32" height="32" /><h1 class="i18n">fulmo_main_h1</h1>
- <div id="screenshot-sender-main-image-filed">
- <p><span class="i18n">fulmo_main_anchor_attachment</span></p>
- <div id="image-wrap"><img id="screenshot-sender-main-image" /></div>
+ <div id="screenshot-sender-image-field">
+ <ul>
+ <li><span id="screenshot-sender-image-field-save"></span></li>
+ <li><span id="screenshot-sender-image-field-edit"></span></li>
+ </ul>
+ <p><span id="screenshot-sender-image-open">&nbsp;</span></p>
+ <div id="image-wrap"><img id="screenshot-sender-image" /></div>
</div>
- <select id="screenshot-sender-main-site"><option value="0" class="i18n">screenshot_general_message_please_select</option></select>
+ <select id="screenshot-sender-site"><option value="0" class="i18n">screenshot_general_message_please_select</option></select>
</div>
<h2 class="i18n">fulmo_main_h2_create_ticket</h2>
-<div id="screenshot-sender-main-body-filed">
- <p><label class="screenshot-sender-main-item-label i18n" for="screenshot-sender-property-sumarry">fulmo_main_label_summary</label><input type="text" id="screenshot-sender-property-sumarry" /></p>
- <p id="screenshot-sender-property-reporter-wrapper"><label id="screenshot-sender-property-reporter-lv" class="screenshot-sender-main-item-label i18n" for="screenshot-sender-property-reporter">fulmo_main_label_reporter</label><input type="text" id="screenshot-sender-property-reporter" /></p>
- <p><label class="screenshot-sender-main-item-label i18n" for="screenshot-sender-property-description">fulmo_main_label_description</label><textarea id="screenshot-sender-property-description" cols="60" rows="7" ></textarea></p>
+<div id="screenshot-sender-body-field">
+ <p><label class="screenshot-sender-item-label i18n" for="screenshot-sender-property-sumarry">fulmo_main_label_summary</label><input type="text" id="screenshot-sender-property-sumarry" /></p>
+ <p id="screenshot-sender-property-reporter-wrapper"><label id="screenshot-sender-property-reporter-lv" class="screenshot-sender-item-label i18n" for="screenshot-sender-property-reporter">fulmo_main_label_reporter</label><input type="text" id="screenshot-sender-property-reporter" /></p>
+ <p><label class="screenshot-sender-item-label i18n" for="screenshot-sender-property-description">fulmo_main_label_description</label><textarea id="screenshot-sender-property-description" cols="60" rows="7" ></textarea></p>
</div>
<h2 class="i18n">fulmo_main_h2_attribute</h2>
-<ul id="property-block"></ul>
+<ul id="screenshot-sender-property-list"></ul>
<div id="screenshot-sender-property-loading"><img /></div>
<div id="screenshot-sender-property-block-footer"></div>
</form>
<div id="footer">
-<div id="screenshot-sender-main-submit-wrapeer"><button id="screenshot-sender-main-submit" class="i18n">fulmo_main_button_title_send_ticket</button></div>
+<div id="screenshot-sender-submit-wrapeer"><button id="screenshot-sender-submit" class="i18n">fulmo_main_button_title_send_ticket</button></div>
<label id="screenshot-sender-open-ticket-wrapper" ><input type="checkbox" id="screenshot-sender-open-ticket" vlaue="1" /><span class="i18n">fulmo_main_label_view_ticket</span></label>
</div>
View
3 chrome/manifest.json
@@ -1,6 +1,6 @@
{
"name": "__MSG_fulmo_name__",
- "version": "0.2",
+ "version": "1.0.1",
"description": "__MSG_fulmo_description__",
"options_page": "settings.html",
"background_page": "background.html",
@@ -25,6 +25,7 @@
"permissions": [
"tabs",
"contextMenus",
+ "cookies",
"http://*/*",
"https://*/*"
]
View
8 chrome/scripts/background.js
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, OpenGroove, Inc. All rights reserved.
+ * Copyright (C) 2012, OpenGroove, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -87,6 +87,12 @@ chrome.extension.onRequest.addListener(
imageParams = req.params;
window.open('main.html', '_blank', 'resizable,centerscreen,scrollbars,width=600,height=800');
sendResponse({});
+ } else if (req.command == 'openEditor') {
+ imageParams = req.params;
+ chrome.windows.getCurrent(function(w){
+ window.open('editor.html', '_blank', 'resizable,centerscreen,scrollbars,width=' + w.width + ',height=' + w.height);
+ sendResponse({});
+ })
} else if (req.command == 'openSettingWindow') {
window.open('settings.html');
sendResponse({});
View
32 chrome/scripts/bts-driver/ciklone.js
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2012, OpenGroove, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+function fulmo_bts_driver_ciklone(){}
+fulmo_bts_driver_ciklone.prototype = new fulmo_bts_driver_trac();
+fulmo_bts_driver_ciklone.prototype.label = 'ciklone.com';
+fulmo_bts_driver_ciklone.prototype.icon = 'ciklone.png';
View
930 chrome/scripts/bts-driver/redmine.js
@@ -0,0 +1,930 @@
+/*
+ * Copyright (C) 2012, OpenGroove, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+function fulmo_bts_driver_redmine(){}
+fulmo_bts_driver_redmine.prototype = {
+
+ /***
+ * 設定画面のBTS選択画面に表示するラベル
+ */
+ label: 'Redmine',
+
+ icon: 'redmine.png',
+
+ _normalizationPath: function(account) {
+ path = account.path;
+ url = account.url;
+ if (path.charAt(path.length - 1) != '/') {
+ url += '/';
+ path += '/';
+ }
+ var tmp = url.split('/');
+ if (tmp.length < 3 || tmp[tmp.length - 3] != 'projects') return null;
+ return {path: path, url: url};
+ },
+
+ _openChain: function (client, params, account, success, error) {
+ var index = 0;
+ function _openOne() {
+ if (client.overrideMimeType) {
+ client.overrideMimeType('application/xml; charset=utf-8');
+ }
+ if (account.authType == 'none') {
+ client.open('GET', params[index][0], true);
+ } else {
+ client.open('GET', params[index][0], true, account.userId, account.password);
+ }
+ client.send('', function() {
+ if (client.status == 200) {
+ if (params[index][1] !== null) params[index][1](index);
+ index++;
+ if (index < params.length) {
+ _openOne();
+ } else {
+ success();
+ }
+ } else {
+ error(index, params[index][0]);
+ }
+ });
+ }
+ _openOne();
+ },
+
+ /***
+ * ログインテストを行う
+ @param {hash} p パラメータをハッシュで与える
+ @param {hash} p.account アカウント情報
+ @param {string} p.account.id アカウントの一意なID
+ @param {string} p.account.name アカウント名
+ @param {string} p.account.url アクセス先のURL
+ @param {string} p.account.siteType アカウントドライバーの名前 bts.js で定義される
+ @param {string} p.account.autoType 認証方法 'none': なし 'http': basic認証、またはdigest認証 (その他の文字列が入る可能性もあり)
+ @param {string} p.account.userId 認証に利用するユーザーID
+ @param {string} p.account.password 認証に利用するパスワード
+ @param {string} p.account.protocol アクセスするプロトコル 'http', 'https' 等が入る。p.account.url から導出される
+ @param {string} p.account.host アクセス先のホスト名。p.account.url から導出される
+ @param {number} p.account.port アクセス先のポート番号。p.account.url から導出される
+ @param {string} p.account.path アクセス先のパス名。p.account.url から導出される
+ @param {function} formatString sprintfライクな関数オブジェクト。環境依存になるのでドライバ内で書式付き文字列を利用したい場合、この関数オブジェクトを利用する事
+ @param {function} success: 成功時に呼ばれる関数オブジェクト。success()はひとつの引数を取り、成功時のメッセージを渡す
+ @param {function} error: 失敗時に呼ばれる関数オブジェクト。success()はひとつの引数を取り、失敗時のメッセージを渡す
+ @detail
+ 設定画面でログインテストを行う時に呼ばれる関数です。ドライバはサーバーに接続し、与えられた認証パラメータで認証を行い、その結果を返す必要があります。
+ */
+ loginTest: function(p) {
+ var normParam = this._normalizationPath(p.account);
+ if (normParam === null) {
+ p.error(p.formatString('fulmo_redmine_test_message_url_invalid', []));
+ return;
+ }
+ xmlHttpRequestCredential.cleanup();
+ var client = new FulmoXMLHttpRequest();
+ var projectUrl = normParam.url;
+ var tmp = projectUrl.split('/');
+ tmp.pop();
+ tmp.pop();
+ tmp.pop();
+ var baseUrl = tmp.join('/') + '/';
+ var testUrl = baseUrl + 'users/current.xml';
+
+ this._openChain(
+ client,
+ [
+ [testUrl, null]
+ ],
+ p.account,
+ function() { // success
+ p.success(p.formatString('fulmo_test_message_succeeded_no_version', [normParam.url]));
+ },
+ function(index, url) { // error
+ var errorStr = client.statusText;
+ if (errorStr == '') errorStr = 'Response Code = ' + client.status;
+ p.error(p.formatString('fulmo_test_message_failed', [testUrl ,errorStr]));
+ }
+ );
+ },
+
+ /**
+ * ログインし、フィルードのリストを取得する
+ @param {hash} p パラメータをハッシュで与える
+ @param {hash} p.account アカウント情報
+ @param {string} p.account.id アカウントの一意なID
+ @param {string} p.account.name アカウント名
+ @param {string} p.account.url アクセス先のURL
+ @param {string} p.account.siteType アカウントドライバーの名前 bts.js で定義される
+ @param {string} p.account.autoType 認証方法 'none': なし 'http': basic認証、またはdigest認証 (その他の文字列が入る可能性もあり)
+ @param {string} p.account.userId 認証に利用するユーザーID
+ @param {string} p.account.password 認証に利用するパスワード
+ @param {string} p.account.protocol アクセスするプロトコル 'http', 'https' 等が入る。p.account.url から導出される
+ @param {string} p.account.host アクセス先のホスト名。p.account.url から導出される
+ @param {number} p.account.port アクセス先のポート番号。p.account.url から導出される
+ @param {string} p.account.path アクセス先のパス名。p.account.url から導出される
+ @param {function} p.success: 成功時に呼ばれる関数オブジェクト。success()はひとつの引数を取り、成功時のメッセージを渡す
+ @param {function} p.error: 失敗時に呼ばれる関数オブジェクト。success()はひとつの引数を取り、失敗時のメッセージを渡す
+ @param {function} formatString sprintfライクな関数オブジェクト。環境依存になるのでドライバ内で書式付き文字列を利用したい場合、この関数オブジェクトを利用する事
+ @param {function} p.resetup: フィールドを再構成する関数。再構成を要求したい時にコールする
+ @param resetup {boolean} 再構成の要求の時に真になる
+ @detail
+ サイトにログインし、fulmotのメイン画面に表示するための構成要素をサーバーから取得します。この関数はfulmoのメイン画面を
+ 表示する直前に「規定のサイト」の情報を取得するために呼ばれます。また、ユーザーがメイン画面の接続先を切り替えた時にも
+ 呼ばれます。
+ ドライバ側では、サイトに接続後、構成要素を取得するためのリクエストを発行し、それを保存します。保存した結果は、p.success()
+ 関数の第一パラメータで返します。この内容は以後呼び出される createProperty() 関数と、 send() 関数で引数として与えられます。
+ このパラメータは本体側で一時保存されますが、内容については関与しません。従って、自由に内容を定義する事ができます。
+ 再構成要求フラグである resetup は、この要求が新規の要求であるか、フィールドの値が変化した事に伴う再構成要求かを保持しています。
+ 再構成要求の場合、以前に作成したフィールドの内容が存在していますので、フィールドの値を読み取り、フィールドの値をカスタマイズ
+ する事ができます。これはプロジェクトやトラッカーが変化した場合に要求されます。
+ なお、再構成の要求はドライバが自分自身で発行しなければなりません。そのために、必要なタイミングで p.resetup() をコールして
+ 下さい。具体的には、プロジェクトやトラッカー等のUIが変化した時に、p.resetup() をコールするようにして下さい。
+ */
+ loginAndGetFields: function(idPrefix, p, resetup) {
+ var _THIS = this;
+ this._extGetFields(idPrefix, p, resetup, function(){_THIS._stdGetFields(p);});
+ },
+
+ _extGetFields: function(idPrefix, p, resetup, errorFunc) {
+ var trackerId = 1;
+ var projectId = 0;
+ if (resetup) {
+ projectId = parseInt($('#' + idPrefix + 'project_id').val());
+ trackerId = parseInt($('#'+ idPrefix + 'tracker_id').val());
+ }
+ var normParam = this._normalizationPath(p.account);
+ if (normParam === null) {
+ p.error(p.formatString('fulmo_redmine_test_message_url_invalid', []));
+ return;
+ }
+ xmlHttpRequestCredential.cleanup();
+ var client = new FulmoXMLHttpRequest();
+ var projectUrl = normParam.url;
+ var tmp = projectUrl.split('/');
+ tmp.pop();
+ var projectIdentifier = tmp.pop();
+ tmp.pop();
+ var baseUrl = tmp.join('/') + '/';
+ var params1 = [];
+ var params2 = [];
+ var params3 = [];
+
+ var urlParams = [
+ [
+ // プロジェクトl情報取得
+ baseUrl + 'projects.xml',function(index) {
+ var arr = client.responseXML.getElementsByTagName('project');
+ tmp = [];
+ for (var i = 0; i < arr.length; i++) {
+ var tmpId = arr[i].getElementsByTagName('id')[0].textContent;
+ var tmpIdentifier = arr[i].getElementsByTagName('identifier')[0].textContent;
+ var is_default;
+ if (!projectId) {
+ is_default = tmpIdentifier == projectIdentifier;
+ } else {
+ is_default = tmpId == projectId;
+ }
+ tmp.push({
+ id: tmpId,
+ name: arr[i].getElementsByTagName('name')[0].textContent,
+ is_default: is_default
+ });
+ if (is_default) {
+ projectId = tmpId;
+ projectUrl = baseUrl + 'projects/' + tmpIdentifier + '/';
+ }
+ }
+ params1.push(
+ {
+ label: p.formatString('fulmo_redmine_label_project',[]),
+ name: 'project_id',
+ format: 'list',
+ items: tmp,
+ is_required: true,
+ reload: true
+ }
+ );
+ urlParams[1][0] = projectUrl + 'trackers.xml';
+ }
+ ],
+ [
+ // トラッカー情報取得
+ '', // URLはプロジェクト情報取得時に決定している
+ function(index) {
+ var arr = client.responseXML.getElementsByTagName('tracker');
+ var tmp = [];
+ for (var i = 0; i < arr.length; i++) {
+ var tmpId = arr[i].getElementsByTagName('id')[0].textContent;
+ var is_default = trackerId == tmpId;
+ tmp.push({
+ id: tmpId,
+ name: arr[i].getElementsByTagName('name')[0].textContent,
+ is_default: is_default
+ });
+ }
+ params1.push(
+ {
+ label: p.formatString('fulmo_redmine_label_tracker',[]),
+ name: 'tracker_id',
+ format: 'list',
+ items: tmp,
+ is_required: true,
+ reload: true
+ }
+ );
+ urlParams[2][0] = projectUrl + 'issues/attributes.xml?tracker_id=' + trackerId;
+ }
+ ],
+ [
+ // チケットフィールド情報取得
+ '', // URLはトラッカー情報取得時に決定している
+ function(index) {
+ var stdArr = client.responseXML.getElementsByTagName('standard_attribute');
+ var cstArr = client.responseXML.getElementsByTagName('custom_attribute');
+ var attrs = {
+ label: '',
+ format: '',
+ is_required: false,
+ default_value: '',
+ min_length: 0,
+ max_length: 0,
+ multiple: false,
+ regexp: '',
+ is_required: false
+ };
+ for (var i = 0; i < stdArr.length; i++) {
+ var tmp = {};
+ tmp['name'] = stdArr[i].getAttribute('name');
+ for (var j in attrs) {
+ if (stdArr[i].getElementsByTagName(j).length) {
+ var tmp2 = stdArr[i].getElementsByTagName(j)[0].textContent;
+ switch (typeof(attrs[j])) {
+ case 'number':
+ tmp[j] = parseInt(tmp2);
+ break;
+ case 'boolean':
+ tmp[j] = (tmp2 == 'true');
+ break;
+ case 'string':
+ default:
+ tmp[j] = tmp2;
+ break;
+ }
+ } else {
+ tmp[j] = attrs[j];
+ }
+ if (tmp['format'] == 'list' || tmp['format'] == 'user' || tmp['format'] == 'version') {
+ var tmp2 = [];
+ var arrLists = stdArr[i].getElementsByTagName('possible_values')[0].getElementsByTagName('value');
+ for (var k = 0; k < arrLists.length; k++) {
+ tmp2.push({
+ id: arrLists[k].getAttribute('id'),
+ name: arrLists[k].textContent,
+ is_default: arrLists[k].getAttribute('id') == tmp['default_value']
+ });
+ }
+ tmp['items'] = tmp2;
+ }
+ }
+ params2.push(tmp);
+ }
+ for (i = 0; i < cstArr.length; i++) {
+ var tmp = {};
+ var tmpId = cstArr[i].getAttribute('id');
+ for (var j in attrs) {
+ if (cstArr[i].getElementsByTagName(j).length) {
+ var tmp2 = cstArr[i].getElementsByTagName(j)[0].textContent;
+ switch (typeof(attrs[j])) {
+ case 'number':
+ tmp[j] = parseInt(tmp2);
+ break;
+ case 'boolean':
+ if (tmp2 == 'true') tmp[j] = true;
+ else tmp[j] = false;
+ break;
+ case 'string':
+ default:
+ tmp[j] = tmp2;
+ break;
+ }
+ } else {
+ tmp[j] = attrs[j];
+ }
+ tmp['label'] = cstArr[i].getElementsByTagName('name')[0].textContent;
+ if (cstArr[i].getElementsByTagName('id').length) {
+ tmpId = cstArr[i].getElementsByTagName('id')[0].textContent;
+ }
+ tmp['name'] = 'custom-attributes-' + tmpId;
+ if (tmp['format'] == 'list' || tmp['format'] == 'user' || tmp['format'] == 'version') {
+ var tmp2 = [];
+ var arrLists = cstArr[i].getElementsByTagName('possible_values')[0].getElementsByTagName('value');
+ for (var k = 0; k < arrLists.length; k++) {
+ tmp2.push({
+ id: arrLists[k].getAttribute('id'),
+ name: arrLists[k].textContent,
+ is_default: arrLists[k].getAttribute('id') == tmp['default_value']
+ });
+ }
+ tmp['items'] = tmp2;
+ }
+ }
+ params3.push(tmp);
+ }
+ }
+ ]
+ ];
+ this._openChain(
+ client,
+ urlParams,
+ p.account,
+ function() { // success
+ var out = {
+ resetup: p.resetup,
+ account: p.account,
+ client: client,
+ baseUrl: baseUrl,
+ projectUrl: projectUrl,
+ projectId: projectId,
+ top: [
+ ],
+ bottom: [params1, params2, params3],
+ }
+ p.success(out);
+
+ },
+ function(index, url) { // error
+ if (errorFunc && client.status == 404) {
+ errorFunc();
+ } else {
+ var errorStr = client.statusText;
+ if (errorStr == '') errorStr = 'Response Code = ' + client.status;
+ errorStr = 'URL: ' + url + '\n' + errorStr;
+ p.error(errorStr);
+ }
+ }
+ );
+ },
+
+ _stdGetFields: function(p) {
+ var normParam = this._normalizationPath(p.account);
+ if (normParam === null) {
+ p.error(p.formatString('fulmo_redmine_test_message_url_invalid', []));
+ return;
+ }
+ xmlHttpRequestCredential.cleanup();
+ var client = new FulmoXMLHttpRequest();
+ var projectUrl = normParam.url;
+ var tmp = projectUrl.split('/');
+ tmp.pop();
+ var projectIdentifier = tmp.pop();
+ tmp.pop();
+ var baseUrl = tmp.join('/') + '/';
+ var params = {};
+
+ this._openChain(
+ client,
+ [
+ [
+ // カテゴリ取得
+ projectUrl + 'issue_categories.xml',function(index) {
+ var arr = client.responseXML.getElementsByTagName('issue_category');
+ params.categories = [];
+ for (var i = 0; i < arr.length; i++) {
+ params.categories.push({
+ id: arr[i].getElementsByTagName('id')[0].textContent,
+ name: arr[i].getElementsByTagName('name')[0].textContent
+ });
+ }
+ }
+ ],
+ [
+ // ステータス取得
+ baseUrl + 'issue_statuses.xml',function(index) {
+ var arr = client.responseXML.getElementsByTagName('issue_status');
+ params.statuses = [];
+ for (var i = 0; i < arr.length; i++) {
+ var id = arr[i].getElementsByTagName('id')[0].textContent;
+ var name = arr[i].getElementsByTagName('name')[0].textContent;
+ params.statuses.push({
+ id: arr[i].getElementsByTagName('id')[0].textContent,
+ name: arr[i].getElementsByTagName('name')[0].textContent,
+ is_default: arr[i].getElementsByTagName('is_default')[0].textContent == 'true',
+ is_closes: arr[i].getElementsByTagName('is_closed')[0].textContent == 'true'
+ });
+ }
+ }
+ ],
+ [
+ // トラッカー取得
+ baseUrl + 'trackers.xml',function(index) {
+ var arr = client.responseXML.getElementsByTagName('tracker');
+ params.trackers = [];
+ for (var i = 0; i < arr.length; i++) {
+ params.trackers.push({
+ id: arr[i].getElementsByTagName('id')[0].textContent,
+ name: arr[i].getElementsByTagName('name')[0].textContent,
+ });
+ }
+ }
+ ],
+ [
+ // プロジェクト取得
+ baseUrl + 'projects.xml',function(index) {
+ var arr = client.responseXML.getElementsByTagName('project');
+ params.projects = [];
+ for (var i = 0; i < arr.length; i++) {
+ params.projects.push({
+ id: arr[i].getElementsByTagName('id')[0].textContent,
+ name: arr[i].getElementsByTagName('name')[0].textContent,
+ is_default: projectIdentifier == arr[i].getElementsByTagName('identifier')[0].textContent,
+ });
+ }
+
+ }
+ ]
+ ],
+ p.account,
+ function() { // success
+ var projectId = 1;
+ if (params.projects[projectIdentifier]) {
+ projectId = params.projects[projectIdentifier];
+ }
+ var out = {
+ account: p.account,
+ client: client,
+ baseUrl: baseUrl,
+ projectUrl: projectUrl,
+ projectId: projectId,
+ top: [
+ ],
+ bottom: [
+ [
+ {
+ label: p.formatString('fulmo_redmine_label_project',[]),
+ name: 'project_id',
+ format: 'list',
+ reload: true,
+ is_required: true,
+ items: params.projects,
+ },
+ {
+ label: p.formatString('fulmo_redmine_label_tracker',[]),
+ name: 'tracker_id',
+ format: 'list',
+ reload: true,
+ is_required: true,
+ items: params.trackers
+ },
+ ],
+ [
+ {
+ label: p.formatString('fulmo_redmine_label_status',[]),
+ name: 'status_id',
+ format: 'list',
+ is_required: true,
+ items: params.statuses
+ },
+ {
+ label: p.formatString('fulmo_redmine_label_priority',[]),
+ format: 'list',
+ name: 'priority_id',
+ is_required: true,
+ items: [
+ {id: '3', name: 'Low'},
+ {id: '4', name: 'Normal', is_default: true},
+ {id: '5', name: 'High'},
+ {id: '6', name: 'Urgent'},
+ {id: '7', name: 'Immediate'}
+ ]
+ },
+ {
+ label: p.formatString('fulmo_redmine_label_category',[]),
+ format: 'list',
+ name: 'category_id',
+ items: params.categories
+ },
+
+ {
+ label: p.formatString('fulmo_redmine_label_parent_issue',[]),
+ name: 'parent_issue_id',
+ format: 'string'
+ },
+ {
+ label: p.formatString('fulmo_redmine_label_start_date',[]),
+ name: 'start_date',
+ format: 'date'
+ },
+ {
+ label: p.formatString('fulmo_redmine_label_due_date',[]),
+ name: 'due_date',
+ format: 'date'
+ },
+ {
+ label: p.formatString('fulmo_redmine_label_estimated_hours',[]),
+ name: 'estimated_hours',
+ format: 'string'
+ },
+ {
+ label: p.formatString('fulmo_redmine_label_done_ratio',[]),
+ name: 'done_ratio',
+ format: 'list',
+ items: [
+ {id: '0', name: '0 %'},
+ {id: '10', name: '10 %'},
+ {id: '20', name: '20 %'},
+ {id: '30', name: '30 %'},
+ {id: '40', name: '40 %'},
+ {id: '50', name: '50 %'},
+ {id: '60', name: '60 %'},
+ {id: '70', name: '70 %'},
+ {id: '80', name: '80 %'},
+ {id: '90', name: '90 %'},
+ {id: '100', name: '100 %'}
+ ]
+ }
+ ],
+ []
+ ],
+ }
+ p.success(out);
+ },
+ function(index, url) { // error
+ var errorStr = client.statusText;
+ if (errorStr == '') errorStr = 'Response Code = ' + client.status;
+ errorStr = 'URL: ' + url + '\n' + errorStr;
+ p.error(errorStr);
+ }
+
+ );
+ },
+
+
+ /**
+ * 画面に各フィールドのhtml要素を構築する
+ @param {string} idPrefix html要素のID属性のprefix
+ @param {hash} p loginAndGetFields() 関数が success() 関数で返した、パラメータ情報
+ @detail
+ fulmo のメイン画面に、サーバーから取得したプロパティ情報を利用し、入力フィールドを生成します。
+ この関数内でjQuery 等を利用しながらDOMを操作します。
+ 入力要素は必ず screenshot-sender-property クラスを設定し、ID名には、idPrefix で指定された prefix を
+ 利用するようにして下さい。
+ */
+ createProperty: function(idPrefix, p) {
+
+ function createElement(target, val) {
+
+ function reloadAttribute() {
+ p.resetup();
+ }
+
+ var createInput = {
+ 'string': function(val) {
+ var inp = $('<input>');
+ if (val.default_value) inp.val(val.default_value);
+ inp.attr('id', idPrefix + name);
+ inp.addClass('screenshot-sender-property');
+ var label = $('<label>');
+ label.addClass('screenshot-sender-properties-label');
+ label.attr('for', idPrefix + name);
+ label.text(val.label + ': ');
+ label.append(inp);
+ var span = $('<span>');
+ span.append(label);
+ span.append(inp);
+ return span;
+ },
+ 'list': function(val) {
+ var sel = $('<select>');
+ if (val.multiple) {
+ sel.attr('multiple', 'multiple');
+ sel.attr('size', '5');
+ }
+ if (val.items) {
+ if (!val.is_required && !val.multiple) {
+ sel.append($('<option>'));
+ }
+ for (var i = 0; i < val.items.length; i++) {
+ var opt = $('<option>');
+ opt.text(val.items[i].name);
+ opt.val(val.items[i].id);
+ if (val.items[i].is_default) {
+ opt.attr('selected', 'selected');
+ }
+ sel.append(opt);
+ }
+ }
+ sel.attr('id', idPrefix + name);
+ sel.addClass('screenshot-sender-property');
+ if (val.reload) {
+ sel.change(reloadAttribute);
+ }
+ var label = $('<label>');
+ label.addClass('screenshot-sender-properties-label');
+ label.attr('for', idPrefix + name);
+ label.text(val.label + ': ');
+ var span = $('<span>');
+ span.append(label);
+ span.append(sel);
+ return span;
+ },
+ 'bool': function(val) {
+ var inp = $('<input>');
+ inp.val('1');
+ if (val.default_value != '0') { // チェック済の場合、'1' や 'on' が入る
+ inp.attr('checked', 'checked');
+ }
+ inp.css('width', '1.5em');
+ inp.attr('id', idPrefix + name);
+ inp.attr('type', 'checkbox');
+ inp.addClass('screenshot-sender-property');
+ var label = $('<label>');
+ label.addClass('screenshot-sender-properties-label');
+ label.attr('for', idPrefix + name);
+ label.text(val.label + ': ');
+ label.append(inp);
+ var span = $('<span>');
+ span.append(label);
+ span.append(inp);
+ return span;
+ },
+ 'radio': function(val) {
+ if (!val.options) return false;
+ var span = $('<span>');
+ var glabel = $('<span>');
+ glabel.text(val.label + ': ');
+ glabel.addClass('screenshot-sender-properties-label');
+ span.append(glabel);
+ var def = val.default_value ? val.default_value : null;
+ var wrap = $('<span>');
+ wrap.addClass('screenshot-sender-property');
+ wrap.attr('id', idPrefix + name);
+ for (var i = 0; i < val.options.length; i++) {
+ var label = $('<label>');
+ label.addClass('screenshot-sender-radio-label');
+ label.text(val.options[i]);
+ var inp = $('<input>');
+ inp.attr('name', idPrefix + name);
+ inp.attr('id', idPrefix + name + '-' + i);
+ inp.attr('type', 'radio');
+ inp.val(val.options[i]);
+ if (val.options[i] == def) inp.attr('checked', 'checked');
+ label.prepend(inp);
+ wrap.append(label);
+ }
+ span.append(wrap);
+ return span;
+ },
+ 'text': function(val) {
+ var inp = $('<textarea>');
+ if (val.default_value) inp.text(val.default_value);
+ inp.attr('id', idPrefix + name);
+ inp.addClass('screenshot-sender-property');
+ if (val.width) inp.attr('cols', val.width);
+ if (val.height) inp.attr('rows', val.height);
+ var label = $('<label>');
+ label.addClass('screenshot-sender-properties-label');
+ label.attr('for', idPrefix + name);
+ label.text(val.label + ': ');
+ label.append(inp);
+ var span = $('<span>');
+ span.append(label);
+ span.append(inp);
+ return span;
+ }
+ };
+ createInput['user'] = createInput['list'];
+ createInput['version'] = createInput['list'];
+
+ var li = $('<li>');
+ if (val === null) {
+ li.addClass('dummy');
+ target.append(li);
+ } else {
+ var format = val.format;
+ var name = val.name;
+ if (!createInput[format]) format = 'string';
+ var inp = createInput[format](val);
+ if (inp) {
+ li.append(inp);
+ target.append(li);
+ }
+ }
+ return li;
+ }
+
+ var target = $('#screenshot-sender-property-list');
+ target.html('');
+ var offset;
+ var el;
+
+ offset = ~~((p.bottom[0].length + 1) / 2);
+ for (var i = 0; i < offset; i++) {
+ el = createElement(target, p.bottom[0][i]);
+ el.css('clear', 'left');
+ if (i + offset < p.bottom[0].length) createElement(target, p.bottom[0][i + offset]);
+ }
+ var left = ['status_id', 'priority_id', 'assigned_to_id', 'category_id', 'fixed_version_id'];
+ var right = ['parent_issue_id', 'start_date', 'due_date', 'estimated_hours', 'done_ratio'];
+ var hash = {};
+ for (i = 0; i < p.bottom[1].length; i++) {
+ hash[p.bottom[1][i].name] = p.bottom[1][i];
+ }
+ while (left.length || right.length) {
+ var id;
+ id = '';
+ while (!(id in hash) && left.length)
+ id = left.shift();
+ if (id in hash) {
+ el = createElement(target, hash[id]);
+ } else {
+ el = createElement(target, null);
+ }
+ el.css('clear', 'left');
+ id = '';
+ while (!(id in hash) && right.length)
+ id = right.shift();
+ if (id in hash) {
+ createElement(target, hash[id]);
+ }
+ }
+ offset = ~~((p.bottom[2].length + 1) / 2);
+ for (i = 0; i < offset; i++) {
+ el = createElement(target, p.bottom[2][i]);
+ el.css('clear', 'left');
+ if (i + offset < p.bottom[2].length) createElement(target, p