diff options
80 files changed, 14653 insertions, 0 deletions
diff --git a/Designer.tmproj b/Designer.tmproj new file mode 100644 index 0000000..9997ff3 --- /dev/null +++ b/Designer.tmproj | |||
@@ -0,0 +1,278 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8"?> | ||
2 | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
3 | <plist version="1.0"> | ||
4 | <dict> | ||
5 | <key>currentDocument</key> | ||
6 | <string>syncdev</string> | ||
7 | <key>documents</key> | ||
8 | <array> | ||
9 | <dict> | ||
10 | <key>expanded</key> | ||
11 | <true/> | ||
12 | <key>name</key> | ||
13 | <string>aes_designer</string> | ||
14 | <key>regexFolderFilter</key> | ||
15 | <string>!.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$</string> | ||
16 | <key>sourceDirectory</key> | ||
17 | <string></string> | ||
18 | </dict> | ||
19 | </array> | ||
20 | <key>fileHierarchyDrawerWidth</key> | ||
21 | <integer>246</integer> | ||
22 | <key>metaData</key> | ||
23 | <dict> | ||
24 | <key>docroot/application.js</key> | ||
25 | <dict> | ||
26 | <key>caret</key> | ||
27 | <dict> | ||
28 | <key>column</key> | ||
29 | <integer>0</integer> | ||
30 | <key>line</key> | ||
31 | <integer>0</integer> | ||
32 | </dict> | ||
33 | <key>firstVisibleColumn</key> | ||
34 | <integer>0</integer> | ||
35 | <key>firstVisibleLine</key> | ||
36 | <integer>3</integer> | ||
37 | </dict> | ||
38 | <key>docroot/classes/application.js</key> | ||
39 | <dict> | ||
40 | <key>caret</key> | ||
41 | <dict> | ||
42 | <key>column</key> | ||
43 | <integer>29</integer> | ||
44 | <key>line</key> | ||
45 | <integer>28</integer> | ||
46 | </dict> | ||
47 | <key>columnSelection</key> | ||
48 | <false/> | ||
49 | <key>firstVisibleColumn</key> | ||
50 | <integer>0</integer> | ||
51 | <key>firstVisibleLine</key> | ||
52 | <integer>0</integer> | ||
53 | <key>selectFrom</key> | ||
54 | <dict> | ||
55 | <key>column</key> | ||
56 | <integer>22</integer> | ||
57 | <key>line</key> | ||
58 | <integer>28</integer> | ||
59 | </dict> | ||
60 | <key>selectTo</key> | ||
61 | <dict> | ||
62 | <key>column</key> | ||
63 | <integer>29</integer> | ||
64 | <key>line</key> | ||
65 | <integer>28</integer> | ||
66 | </dict> | ||
67 | </dict> | ||
68 | <key>docroot/classes/card.class.js</key> | ||
69 | <dict> | ||
70 | <key>caret</key> | ||
71 | <dict> | ||
72 | <key>column</key> | ||
73 | <integer>6</integer> | ||
74 | <key>line</key> | ||
75 | <integer>272</integer> | ||
76 | </dict> | ||
77 | <key>firstVisibleColumn</key> | ||
78 | <integer>0</integer> | ||
79 | <key>firstVisibleLine</key> | ||
80 | <integer>46</integer> | ||
81 | </dict> | ||
82 | <key>docroot/classes/cookie.class.js</key> | ||
83 | <dict> | ||
84 | <key>caret</key> | ||
85 | <dict> | ||
86 | <key>column</key> | ||
87 | <integer>0</integer> | ||
88 | <key>line</key> | ||
89 | <integer>0</integer> | ||
90 | </dict> | ||
91 | <key>firstVisibleColumn</key> | ||
92 | <integer>0</integer> | ||
93 | <key>firstVisibleLine</key> | ||
94 | <integer>17</integer> | ||
95 | </dict> | ||
96 | <key>docroot/classes/history.class.js</key> | ||
97 | <dict> | ||
98 | <key>caret</key> | ||
99 | <dict> | ||
100 | <key>column</key> | ||
101 | <integer>0</integer> | ||
102 | <key>line</key> | ||
103 | <integer>0</integer> | ||
104 | </dict> | ||
105 | <key>firstVisibleColumn</key> | ||
106 | <integer>0</integer> | ||
107 | <key>firstVisibleLine</key> | ||
108 | <integer>136</integer> | ||
109 | </dict> | ||
110 | <key>docroot/classes/layouts/layout.primary.class.js</key> | ||
111 | <dict> | ||
112 | <key>caret</key> | ||
113 | <dict> | ||
114 | <key>column</key> | ||
115 | <integer>0</integer> | ||
116 | <key>line</key> | ||
117 | <integer>0</integer> | ||
118 | </dict> | ||
119 | <key>firstVisibleColumn</key> | ||
120 | <integer>0</integer> | ||
121 | <key>firstVisibleLine</key> | ||
122 | <integer>358</integer> | ||
123 | </dict> | ||
124 | <key>docroot/classes/overlay.class.js</key> | ||
125 | <dict> | ||
126 | <key>caret</key> | ||
127 | <dict> | ||
128 | <key>column</key> | ||
129 | <integer>0</integer> | ||
130 | <key>line</key> | ||
131 | <integer>0</integer> | ||
132 | </dict> | ||
133 | <key>firstVisibleColumn</key> | ||
134 | <integer>0</integer> | ||
135 | <key>firstVisibleLine</key> | ||
136 | <integer>65</integer> | ||
137 | </dict> | ||
138 | <key>docroot/classes/roundcorners.class.js</key> | ||
139 | <dict> | ||
140 | <key>caret</key> | ||
141 | <dict> | ||
142 | <key>column</key> | ||
143 | <integer>0</integer> | ||
144 | <key>line</key> | ||
145 | <integer>0</integer> | ||
146 | </dict> | ||
147 | <key>firstVisibleColumn</key> | ||
148 | <integer>0</integer> | ||
149 | <key>firstVisibleLine</key> | ||
150 | <integer>7</integer> | ||
151 | </dict> | ||
152 | <key>docroot/data/strings.en.js</key> | ||
153 | <dict> | ||
154 | <key>caret</key> | ||
155 | <dict> | ||
156 | <key>column</key> | ||
157 | <integer>3</integer> | ||
158 | <key>line</key> | ||
159 | <integer>64</integer> | ||
160 | </dict> | ||
161 | <key>firstVisibleColumn</key> | ||
162 | <integer>0</integer> | ||
163 | <key>firstVisibleLine</key> | ||
164 | <integer>0</integer> | ||
165 | </dict> | ||
166 | <key>docroot/specialcases.css</key> | ||
167 | <dict> | ||
168 | <key>caret</key> | ||
169 | <dict> | ||
170 | <key>column</key> | ||
171 | <integer>0</integer> | ||
172 | <key>line</key> | ||
173 | <integer>0</integer> | ||
174 | </dict> | ||
175 | <key>firstVisibleColumn</key> | ||
176 | <integer>0</integer> | ||
177 | <key>firstVisibleLine</key> | ||
178 | <integer>0</integer> | ||
179 | </dict> | ||
180 | <key>syncdev</key> | ||
181 | <dict> | ||
182 | <key>caret</key> | ||
183 | <dict> | ||
184 | <key>column</key> | ||
185 | <integer>22</integer> | ||
186 | <key>line</key> | ||
187 | <integer>9</integer> | ||
188 | </dict> | ||
189 | <key>firstVisibleColumn</key> | ||
190 | <integer>0</integer> | ||
191 | <key>firstVisibleLine</key> | ||
192 | <integer>0</integer> | ||
193 | </dict> | ||
194 | </dict> | ||
195 | <key>openDocuments</key> | ||
196 | <array> | ||
197 | <string>docroot/classes/cookie.class.js</string> | ||
198 | <string>docroot/classes/overlay.class.js</string> | ||
199 | <string>docroot/classes/roundcorners.class.js</string> | ||
200 | <string>docroot/classes/history.class.js</string> | ||
201 | <string>docroot/classes/card.class.js</string> | ||
202 | <string>docroot/classes/layouts/layout.primary.class.js</string> | ||
203 | <string>docroot/classes/application.js</string> | ||
204 | <string>docroot/application.js</string> | ||
205 | <string>docroot/data/strings.en.js</string> | ||
206 | <string>docroot/specialcases.css</string> | ||
207 | <string>syncdev</string> | ||
208 | </array> | ||
209 | <key>showFileHierarchyDrawer</key> | ||
210 | <false/> | ||
211 | <key>showFileHierarchyPanel</key> | ||
212 | <true/> | ||
213 | <key>treeState</key> | ||
214 | <dict> | ||
215 | <key>aes_designer</key> | ||
216 | <dict> | ||
217 | <key>isExpanded</key> | ||
218 | <true/> | ||
219 | <key>subItems</key> | ||
220 | <dict> | ||
221 | <key>build_system</key> | ||
222 | <dict> | ||
223 | <key>isExpanded</key> | ||
224 | <true/> | ||
225 | <key>subItems</key> | ||
226 | <dict/> | ||
227 | </dict> | ||
228 | <key>cgi-bin</key> | ||
229 | <dict> | ||
230 | <key>isExpanded</key> | ||
231 | <true/> | ||
232 | <key>subItems</key> | ||
233 | <dict/> | ||
234 | </dict> | ||
235 | <key>docroot</key> | ||
236 | <dict> | ||
237 | <key>isExpanded</key> | ||
238 | <true/> | ||
239 | <key>subItems</key> | ||
240 | <dict> | ||
241 | <key>classes</key> | ||
242 | <dict> | ||
243 | <key>isExpanded</key> | ||
244 | <true/> | ||
245 | <key>subItems</key> | ||
246 | <dict> | ||
247 | <key>layouts</key> | ||
248 | <dict> | ||
249 | <key>isExpanded</key> | ||
250 | <true/> | ||
251 | <key>subItems</key> | ||
252 | <dict/> | ||
253 | </dict> | ||
254 | </dict> | ||
255 | </dict> | ||
256 | <key>data</key> | ||
257 | <dict> | ||
258 | <key>isExpanded</key> | ||
259 | <true/> | ||
260 | <key>subItems</key> | ||
261 | <dict/> | ||
262 | </dict> | ||
263 | <key>lib</key> | ||
264 | <dict> | ||
265 | <key>isExpanded</key> | ||
266 | <true/> | ||
267 | <key>subItems</key> | ||
268 | <dict/> | ||
269 | </dict> | ||
270 | </dict> | ||
271 | </dict> | ||
272 | </dict> | ||
273 | </dict> | ||
274 | </dict> | ||
275 | <key>windowFrame</key> | ||
276 | <string>{{151, 48}, {1199, 830}}</string> | ||
277 | </dict> | ||
278 | </plist> | ||
diff --git a/build_system/cmpcss b/build_system/cmpcss new file mode 100755 index 0000000..d347a63 --- /dev/null +++ b/build_system/cmpcss | |||
@@ -0,0 +1,125 @@ | |||
1 | #!/usr/bin/python | ||
2 | ############### | ||
3 | # cmpcss | ||
4 | # Compress a CSS file in prepration for production use. | ||
5 | # | ||
6 | # AUTHOR | ||
7 | # Michael Crute (mcrute@gmail.com) | ||
8 | # DATE | ||
9 | # 16 June 2006 | ||
10 | # VERSION | ||
11 | # 1.0 | ||
12 | # PURPOSE | ||
13 | # An implementation of a CSS compressor in Python. Written to | ||
14 | # prepare development CSS to production ready CSS. Does the | ||
15 | # following: | ||
16 | # * Removes spaces intelligently | ||
17 | # * Removes charset declarations | ||
18 | # * Removes extra semicolons | ||
19 | # * Flattens the file (tabs and line breaks) | ||
20 | # USAGE | ||
21 | # cmpcss site_dev.css > site_prod.css | ||
22 | # LICENSE | ||
23 | # You may copy and redistribute this program as you see fit, with no | ||
24 | # restrictions. | ||
25 | # WARRANTY | ||
26 | # This program comes with NO warranty, real or implied. If it eats | ||
27 | # your CSS, it's not my problem, you should have kept a backup. | ||
28 | ############### | ||
29 | |||
30 | import sys | ||
31 | import re | ||
32 | #import string | ||
33 | |||
34 | # The chewy caramel center of the program | ||
35 | # Don't bitch about the short variable names, it makes it easy to read | ||
36 | def cleanline(theLine): | ||
37 | # Kills line breaks, tabs, and double spaces | ||
38 | p = re.compile('(\n|\r|\t|\f|\v)+') | ||
39 | m = p.sub('',theLine) | ||
40 | |||
41 | # Kills double spaces | ||
42 | p = re.compile('( )+') | ||
43 | m = p.sub(' ',m) | ||
44 | |||
45 | # Removes last semicolon before } | ||
46 | p = re.compile('(; }|;})+') | ||
47 | m = p.sub('}',m) | ||
48 | |||
49 | # Removes space before { | ||
50 | p = re.compile('({ )+') | ||
51 | m = p.sub('{',m) | ||
52 | |||
53 | # Removes all comments | ||
54 | p = re.compile('/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/') | ||
55 | m = p.sub('',m) | ||
56 | |||
57 | # Strip off the Charset | ||
58 | p = re.compile('@CHARSET .*;') | ||
59 | m = p.sub('',m) | ||
60 | |||
61 | # Strip spaces before the { | ||
62 | p = re.compile(' {') | ||
63 | m = p.sub('{',m) | ||
64 | |||
65 | # Strip space after : | ||
66 | p = re.compile(': ') | ||
67 | m = p.sub(':',m) | ||
68 | |||
69 | # Strip space after , | ||
70 | p = re.compile(', ') | ||
71 | m = p.sub(',',m) | ||
72 | |||
73 | # Strip space after ; | ||
74 | p = re.compile('; ') | ||
75 | m = p.sub(';',m) | ||
76 | |||
77 | # May return to add color code compression | ||
78 | # not a huge space saver so also not high priority | ||
79 | # p = re.compile('.*#[0-9A-F]{6}.*') | ||
80 | # cc = p.match(m) | ||
81 | |||
82 | # if cc: | ||
83 | # cc = compcolor(m) | ||
84 | |||
85 | return m | ||
86 | |||
87 | #def compcolor(m): | ||
88 | |||
89 | # Boring usage info for people too dumb to use the program | ||
90 | def printusage(): | ||
91 | print '' | ||
92 | print 'SoftGroup CSS Compressor' | ||
93 | print '' | ||
94 | print 'This is a compressor for CSS files.' | ||
95 | print 'As far as I know it doesn\'t break the standards' | ||
96 | print 'compliance of the file. If you can find a way' | ||
97 | print 'to make it break a file please email me.' | ||
98 | print '' | ||
99 | print 'Usage: cmpcss <filename>' | ||
100 | sys.exit() | ||
101 | |||
102 | # The main routine | ||
103 | if ( __name__ == "__main__" ): | ||
104 | if (len(sys.argv) <= 1): | ||
105 | printusage() | ||
106 | |||
107 | # Open the file for reading | ||
108 | try: | ||
109 | theFile = file(sys.argv[1], "r") | ||
110 | except IOError: | ||
111 | print 'I couldn\'t open your file.' | ||
112 | sys.exit() | ||
113 | |||
114 | # Initialize temp so it's not scoped to the for loop | ||
115 | temp = '' | ||
116 | |||
117 | # Loop through the file line by line and clean it | ||
118 | for line in theFile: | ||
119 | temp = temp + cleanline(line) | ||
120 | |||
121 | # Once more, clean the entire file string | ||
122 | print cleanline(temp) | ||
123 | |||
124 | # Cleanup after ourselves | ||
125 | theFile.close() | ||
diff --git a/build_system/get_jsfiles.sed b/build_system/get_jsfiles.sed new file mode 100755 index 0000000..09e7a6d --- /dev/null +++ b/build_system/get_jsfiles.sed | |||
@@ -0,0 +1,25 @@ | |||
1 | #!/bin/sed -f | ||
2 | # | ||
3 | # SED script for parsing out include file names from | ||
4 | # the application.js file. | ||
5 | # | ||
6 | |||
7 | # Remove single line comments first | ||
8 | /^\s+\/\//d | ||
9 | |||
10 | # Remove multi-line comments | ||
11 | /^\s+\/\*.+/d | ||
12 | /\/\*/,/\*\// { | ||
13 | s/.*//g | ||
14 | } | ||
15 | |||
16 | # Replace the require lines with just their contents | ||
17 | s/this\.require\(["']([^"']*)["']\);/\1/ | ||
18 | |||
19 | # Remove all lines that don't end in js | ||
20 | # assumes we are getting all of our JS files | ||
21 | /js$/!d | ||
22 | |||
23 | # Remove the spaces before and after the filename | ||
24 | # assumes there are no spaces in the filename iteself | ||
25 | s/\s*// | ||
diff --git a/build_system/make.gzip b/build_system/make.gzip new file mode 100755 index 0000000..a95ed04 --- /dev/null +++ b/build_system/make.gzip | |||
@@ -0,0 +1,130 @@ | |||
1 | #!/bin/bash | ||
2 | # | ||
3 | # ExxonMobil Designer Site Build Script | ||
4 | # by Mike Crute, EYEMG (mcrute@eyemg.com) | ||
5 | # | ||
6 | # This is a pretty simple build script just export a | ||
7 | # tag or the trunk from SVN and run ./make, running | ||
8 | # make (w/out the ./) will not work. You can then | ||
9 | # take the generated tarball and explode it on the | ||
10 | # production server w/out any further configuration. | ||
11 | # | ||
12 | |||
13 | cd docroot | ||
14 | |||
15 | # Create the build directory and copy over the cgi-bin | ||
16 | mkdir -p build/{docroot,cgi-bin} | ||
17 | cp -R ../cgi-bin/* build/cgi-bin/ | ||
18 | |||
19 | # Create a build date file | ||
20 | echo "Designer Site Built `date +'%Y-%m-%d %H:%M:%S'` EST" > build/docroot/build.date | ||
21 | echo "Code Version: `grep 'releaseVersion' classes/sme.namespace.js | cut -d"'" -f 2 `" >> build/docroot/build.date | ||
22 | echo "Subversion Revision: `svn info | grep 'Revision' | awk '{ print $2 }'`" >> build/docroot/build.date | ||
23 | |||
24 | # Remove stuff from the cgi-bin that does not belong in | ||
25 | # production. | ||
26 | rm build/cgi-bin/*.sql | ||
27 | |||
28 | # Also copy over any files that don't need special processing | ||
29 | cp -R images \ | ||
30 | blank.gif \ | ||
31 | favicon.ico \ | ||
32 | custom_content \ | ||
33 | build/docroot/ | ||
34 | |||
35 | # Prepare the .htaccess file for production | ||
36 | # | ||
37 | # We use a sparse layout in development where each class is | ||
38 | # in its own separate file but in production we use a solid | ||
39 | # layout that is also gzipped so we have to un-comment the | ||
40 | # gzip headers in our htaccess file. | ||
41 | |||
42 | sed ' | ||
43 | /### PRODUCTION ###/,/### END PRODUCTION ###/ { | ||
44 | /^###.*/d | ||
45 | s/#//g | ||
46 | } | ||
47 | ' .htaccess > build/docroot/.htaccess | ||
48 | |||
49 | # Minify the PNG Behavior | ||
50 | sed ' | ||
51 | /\/\*/,/\*\// { | ||
52 | /.*/d; | ||
53 | } | ||
54 | ' pngbehavior.htc > build/docroot/pngbehavior.htc | ||
55 | |||
56 | # Concatenate the Javascript files into a single library file | ||
57 | # | ||
58 | # application.js loads all the javascript files in the development | ||
59 | # environment. When we go to production we need to parse out the | ||
60 | # actual script names being loaded and cat them, in order, into | ||
61 | # the final output application.js file. | ||
62 | |||
63 | # GNU sed uses the -r flag for extended regular expressions, | ||
64 | # Darwin uses the -E flag for regular expressions. If neither | ||
65 | # of these hold true we might as well fail. | ||
66 | if [[ `uname` == 'Linux' ]]; then | ||
67 | MYFILES=`sed -r -f ../build_system/get_jsfiles.sed application.js` | ||
68 | elif [[ `uname` == 'Darwin' ]]; then | ||
69 | MYFILES=`sed -E -f ../build_system/get_jsfiles.sed application.js` | ||
70 | else | ||
71 | print 'No valid sed command could be determined.' | ||
72 | exit 1 | ||
73 | fi | ||
74 | |||
75 | for item in $MYFILES; do | ||
76 | cat $item >> build/docroot/application.js.in | ||
77 | done | ||
78 | |||
79 | # Remove development code from the application code | ||
80 | # and append it to the libraries files | ||
81 | sed '/^;;;.*/d' build/docroot/application.js.in >> build/docroot/application.js.out | ||
82 | sed '/^\/\*;;;.*/d' build/docroot/application.js.out >> build/docroot/application.js | ||
83 | rm build/docroot/application.js.in build/docroot/application.js.out | ||
84 | |||
85 | # Minify index.html by removing leading spaces on each line | ||
86 | # as well as the ID comment, blank lines, and any script tags | ||
87 | # that appear in the <head> of the document. | ||
88 | sed 's/^[[:space:]]*//; /^$/d; s/\n$//; /<!--/,/-->/ { /.*/d; }' index.html > build/docroot/index.html | ||
89 | sed 's/^[[:space:]]*//; /^$/d; s/\n$//; /<!--/,/-->/ { /.*/d; }' logged_out.html > build/docroot/logged_out.html | ||
90 | |||
91 | # Minify the application Javascript using the YUI Compressor | ||
92 | java -jar ../build_system/yui_compressor.jar -o application.o build/docroot/application.js > /dev/null 2>&1 | ||
93 | mv application.o build/docroot/application.js | ||
94 | |||
95 | # Minify the JSON data files | ||
96 | mkdir -p build/docroot/data | ||
97 | for item in `ls data/`; do | ||
98 | java -jar ../build_system/yui_compressor.jar -o build/docroot/data/$item data/$item > /dev/null 2>&1 | ||
99 | done | ||
100 | |||
101 | # Can't minimize JSON, the semicolon at the end breaks everything | ||
102 | cat data/card_tables.js > build/docroot/data/card_tables.js | ||
103 | |||
104 | # Minify the application CSS using our custom compressor | ||
105 | ../build_system/cmpcss application.css > build/docroot/application.css | ||
106 | ../build_system/cmpcss specialcases.css > build/docroot/specialcases.css | ||
107 | |||
108 | # Gzip all that should be gzipped. This really should be done server-side | ||
109 | # but for now we work around it by statically compressing them at build time | ||
110 | # and serving it with the correct headers in the .htaccess file | ||
111 | gzip build/docroot/application.js | ||
112 | gzip build/docroot/application.css | ||
113 | gzip build/docroot/index.html | ||
114 | |||
115 | mv build/docroot/application.js.gz build/docroot/application.js | ||
116 | mv build/docroot/application.css.gz build/docroot/application.css | ||
117 | mv build/docroot/index.html.gz build/docroot/index.html | ||
118 | |||
119 | # Tar it all up in a development dump | ||
120 | mv build aes_designer | ||
121 | cd aes_designer | ||
122 | |||
123 | # Removing the date since its really not needed | ||
124 | tar -cjvf ../../aes_designer.tbz2 * > /dev/null 2>&1 | ||
125 | #tar -cjvf ../../aes_designer-`date +%Y%m%d_%H%M%S`.tbz2 * > /dev/null 2>&1 | ||
126 | |||
127 | |||
128 | cd .. | ||
129 | |||
130 | rm -rf aes_designer/ | ||
diff --git a/build_system/yui_compressor.jar b/build_system/yui_compressor.jar new file mode 100755 index 0000000..4efe293 --- /dev/null +++ b/build_system/yui_compressor.jar | |||
Binary files differ | |||
diff --git a/cgi-bin/card_table.pl b/cgi-bin/card_table.pl new file mode 100755 index 0000000..a4d26f6 --- /dev/null +++ b/cgi-bin/card_table.pl | |||
@@ -0,0 +1,371 @@ | |||
1 | #!/usr/bin/perl | ||
2 | |||
3 | $|=1; | ||
4 | |||
5 | srand; | ||
6 | |||
7 | use strict; | ||
8 | |||
9 | use Apache::Request; | ||
10 | use Apache::Constants qw(REDIRECT); | ||
11 | use Benchmark::Timer; | ||
12 | use HTML::Template; | ||
13 | use Bit::Vector; | ||
14 | use Image::Magick; | ||
15 | |||
16 | use Compose::local_lib; | ||
17 | use Compose::db_connection; | ||
18 | |||
19 | my $r = Apache::Request->new(Apache->request); | ||
20 | $r->send_http_header('text/javascript'); | ||
21 | |||
22 | my $tt = new Benchmark::Timer; | ||
23 | $tt->start('all'); | ||
24 | |||
25 | my $local_lib = new Compose::local_lib(); | ||
26 | |||
27 | my $dbh = new Compose::db_connection('localhost','aes','apache','webconnect'); | ||
28 | |||
29 | my $form; | ||
30 | foreach my $key (sort $r->param) { | ||
31 | $form->{$key} = $local_lib->fix_spaces($r->param($key)); | ||
32 | } | ||
33 | |||
34 | # table=home | ||
35 | |||
36 | ################################################## | ||
37 | # | ||
38 | # | ||
39 | if ($r->method() eq "GET") { | ||
40 | |||
41 | if ($form->{'table'} eq "") { | ||
42 | |||
43 | my $qry = qq(select distinct select_0 from content_data where site_id=1 and form_id=48 and moderation_status=3 and date_0 <= NOW() ); | ||
44 | my %tables = $dbh->queryDB($qry,'select_0'); | ||
45 | |||
46 | print qq( | ||
47 | [ | ||
48 | ); | ||
49 | my @list=(); | ||
50 | foreach my $table (sort { $a cmp $b } keys %tables) { | ||
51 | push @list,"\t\"$table\""; | ||
52 | } | ||
53 | |||
54 | print join(",\n",@list) . qq( | ||
55 | ] | ||
56 | ); | ||
57 | |||
58 | |||
59 | |||
60 | } elsif ($form->{'table'} ne "") { | ||
61 | |||
62 | my $qry = ""; | ||
63 | |||
64 | my $ckb = "checkbox_1"; | ||
65 | if ($form->{'table'} eq "intro") { | ||
66 | $ckb = "checkbox_0"; | ||
67 | } | ||
68 | |||
69 | my $t = new Benchmark::Timer; | ||
70 | |||
71 | $form->{'num_cards'} = 15; # if ($form->{'num_cards'} eq ""); | ||
72 | $form->{'w'} = 900 if ($form->{'w'} eq "" || $form->{'w'} < 900); | ||
73 | $form->{'h'} = 400 if ($form->{'h'} eq "" || $form->{'h'} < 400); | ||
74 | $form->{'max'} = 20 if ($form->{'max'} eq ""); | ||
75 | |||
76 | # get cards | ||
77 | if ($form->{'table'} eq 'home') { | ||
78 | $qry = qq( | ||
79 | SELECT | ||
80 | content_id, | ||
81 | enc_content_id, | ||
82 | textfield_0 AS title, | ||
83 | select_0 AS category, | ||
84 | checkbox_4 AS protected, | ||
85 | image_1_width AS rotated_width, | ||
86 | image_1_height AS rotated_height, | ||
87 | image_2_width AS zdegree_width, | ||
88 | image_2_height AS zdegree_height, | ||
89 | CONCAT(clients.website.site_url,clients.website.publish_docroot,"/",human_dir,"/index.html") AS page_loc, | ||
90 | CONCAT(clients.website.server_docroot, clients.website.publish_docroot,"/",human_dir,"/orig/",image_1) AS publish_loc, | ||
91 | CONCAT(clients.website.site_url,clients.website.publish_docroot,"/",human_dir,"/orig/",image_1) AS rotated_image, | ||
92 | CONCAT(clients.website.site_url,clients.website.publish_docroot,"/",human_dir,"/orig/",image_2) AS zdegree_image | ||
93 | FROM | ||
94 | content_data, | ||
95 | clients.website | ||
96 | WHERE | ||
97 | clients.website.site_id = content_data.site_id AND | ||
98 | content_data.site_id = 1 AND | ||
99 | form_id = 48 AND | ||
100 | moderation_status >= 3 AND | ||
101 | date_0 <= NOW() AND | ||
102 | checkbox_0 = "Yes" AND | ||
103 | $ckb = "Yes" AND | ||
104 | checkbox_2 != "Yes" AND | ||
105 | !(textfield_5 is NULL or textfield_5 = "") | ||
106 | ORDER BY | ||
107 | last_modified_date | ||
108 | LIMIT | ||
109 | $form->{'num_cards'} | ||
110 | ); | ||
111 | } else { | ||
112 | #$qry = qq( select content_id,enc_content_id,textfield_0 as title, select_0 as category, image_1_height as rotated_height, image_2_width as zdegree_width, image_2_height as zdegree_height, CONCAT(clients.website.site_url,clients.website.publish_docroot,"/",human_dir,"/index.html") as page_loc , CONCAT(clients.website.server_docroot, clients.website.publish_docroot,"/",human_dir,"/orig/",image_1) as publish_loc, CONCAT(clients.website.site_url,clients.website.publish_docroot,"/",human_dir,"/orig/",image_1) as rotated_image, CONCAT(clients.website.site_url,clients.website.publish_docroot,"/",human_dir,"/orig/",image_2) as zdegree_image from content_data,clients.website where clients.website.site_id=content_data.site_id and content_data.site_id=1 and form_id=48 and moderation_status >=3 and date_0 <= NOW() and select_0="$form->{'table'}" and $ckb="Yes" and !(textfield_5 is NULL or textfield_5 = "") and checkbox_2 != "Yes" order by last_modified_date limit $form->{'num_cards'}) ; | ||
113 | |||
114 | $qry = qq( | ||
115 | SELECT | ||
116 | content_id, | ||
117 | enc_content_id, | ||
118 | textfield_0 AS title, | ||
119 | select_0 AS category, | ||
120 | checkbox_4 AS protected, | ||
121 | image_1_width AS rotated_width, | ||
122 | image_1_height AS rotated_height, | ||
123 | image_2_width AS zdegree_width, | ||
124 | image_2_height AS zdegree_height, | ||
125 | CONCAT(clients.website.site_url,clients.website.publish_docroot,"/",human_dir,"/index.html") AS page_loc, | ||
126 | CONCAT(clients.website.server_docroot, clients.website.publish_docroot,"/",human_dir,"/orig/",image_1) AS publish_loc, | ||
127 | CONCAT(clients.website.site_url,clients.website.publish_docroot,"/",human_dir,"/orig/",image_1) AS rotated_image, | ||
128 | CONCAT(clients.website.site_url,clients.website.publish_docroot,"/",human_dir,"/orig/",image_2) AS zdegree_image | ||
129 | FROM | ||
130 | content_data, | ||
131 | clients.website | ||
132 | WHERE | ||
133 | clients.website.site_id = content_data.site_id AND | ||
134 | content_data.site_id = 1 AND | ||
135 | form_id = 48 AND | ||
136 | moderation_status >= 3 AND | ||
137 | date_0 <= NOW() AND | ||
138 | select_0 = "$form->{'table'}" AND | ||
139 | $ckb = "Yes" AND | ||
140 | checkbox_2 != "Yes" AND | ||
141 | !(textfield_5 IS NULL OR textfield_5 = "") | ||
142 | ORDER BY | ||
143 | last_modified_date | ||
144 | LIMIT | ||
145 | $form->{'num_cards'} | ||
146 | ); | ||
147 | |||
148 | } | ||
149 | |||
150 | my %res = $dbh->queryRawDB($qry); | ||
151 | |||
152 | my @master_v = new Bit::Vector($form->{'w'},$form->{'h'}); | ||
153 | my ($hei,$wid); | ||
154 | my @rowM=(); | ||
155 | |||
156 | my $back_image; | ||
157 | if ($form->{'gen'} ne "") { | ||
158 | $back_image = Image::Magick->new(size=>"$form->{'w'} x $form->{'h'}"); | ||
159 | $back_image->ReadImage('xc:white'); | ||
160 | } | ||
161 | |||
162 | my ($idx,$x,$y); | ||
163 | |||
164 | print qq( [ \n); | ||
165 | my $iter = 0; | ||
166 | foreach $idx (sort {$a <=> $b} keys %res) { | ||
167 | |||
168 | my $protected = ($res{$idx}{'protected'} eq 'Yes') ? 'true' : 'false'; | ||
169 | |||
170 | ($x,$y,$back_image) = &place_data(\@master_v,$form->{'w'},$form->{'h'},$res{$idx}{'zdegree_image'},$res{$idx}{'rotated_image'},$res{$idx}{'rotated_width'},$res{$idx}{'rotated_height'},$res{$idx}{'publish_loc'},$back_image,$form->{'gen'},$form->{'max'}); | ||
171 | |||
172 | print qq(\t{ | ||
173 | "cid" : "$res{$idx}{'enc_content_id'}", | ||
174 | "title" : "$res{$idx}{'title'}", | ||
175 | "category" : "$res{$idx}{'category'}", | ||
176 | "locked" : $protected, | ||
177 | "x" : "$x", | ||
178 | "y" : "$y", | ||
179 | "chip" : "$res{$idx}{'rotated_image'}" | ||
180 | }); | ||
181 | print ",\n" if (++$iter != keys(%res)); | ||
182 | } | ||
183 | print qq( \n ]); | ||
184 | |||
185 | if ($form->{'gen'} ne "") { | ||
186 | $back_image->Write("/usr/web/designer/docroot/test.png"); | ||
187 | } | ||
188 | |||
189 | } | ||
190 | |||
191 | } | ||
192 | |||
193 | #$tt->stop('all'); | ||
194 | #print $tt->report() ." <br>"; | ||
195 | #print qq(<img src="/test.png">) if ($form->{'gen'}); | ||
196 | |||
197 | ########################################### | ||
198 | # | ||
199 | |||
200 | sub place_data { | ||
201 | |||
202 | my $master_v = shift; | ||
203 | my $page_width = shift; | ||
204 | my $page_height = shift; | ||
205 | my $image_data_zdegree_image = shift; | ||
206 | my $image_data_rotated_image = shift; | ||
207 | my $image_data_rotated_width = shift; | ||
208 | my $image_data_rotated_height = shift; | ||
209 | my $image_data_publish_loc = shift; | ||
210 | my $back_image = shift; | ||
211 | my $gen_image = shift; | ||
212 | my $max_collide = shift; | ||
213 | |||
214 | my $done = 0; | ||
215 | my $attempts = 0; | ||
216 | |||
217 | my $t = new Benchmark::Timer; | ||
218 | |||
219 | my ($max_x,$max_y,$xpos,$ypos,$vector,$sdone,$start,$attempts,$sattempts,$min,$max,$collisions,$xrand,$yrand ); | ||
220 | |||
221 | while ((!$done) && $attempts < 500) { | ||
222 | |||
223 | $xrand = int(rand($page_width-20-$image_data_rotated_width)+10); | ||
224 | $yrand = int(rand($page_height-20-$image_data_rotated_height)+10); | ||
225 | |||
226 | my $max_collide = int($image_data_rotated_width*$image_data_rotated_height*($max_collide/100)); | ||
227 | |||
228 | $max_x = $xrand+$image_data_rotated_width; | ||
229 | $max_y = $yrand+$image_data_rotated_height; | ||
230 | |||
231 | $t->start('3.1'); | ||
232 | |||
233 | $collisions = 0; | ||
234 | |||
235 | for ($ypos = $yrand;$ypos <= $max_y; $ypos++) { | ||
236 | $vector = @{$master_v}->[$ypos]; | ||
237 | |||
238 | $sdone=0; | ||
239 | $start = $xrand; | ||
240 | $sattempts = 0; | ||
241 | |||
242 | while (!$sdone && $sattempts++ < 20) { | ||
243 | ($min,$max) = $vector->Interval_Scan_inc($start); | ||
244 | |||
245 | if ($max+1 >= $page_width) { | ||
246 | $sdone=1; | ||
247 | } | ||
248 | |||
249 | if ($min < $max_x && $max) { | ||
250 | if ($max > $max_x || $max+1 >= $page_width) { | ||
251 | $collisions += $max_x - $min; | ||
252 | $sdone = 1; | ||
253 | } else { | ||
254 | $collisions += $max - $min; | ||
255 | $start = $max+1; | ||
256 | } | ||
257 | |||
258 | } elsif ($min > $max_x || !$max || !$min) { | ||
259 | $sdone=1; | ||
260 | } | ||
261 | } | ||
262 | } | ||
263 | |||
264 | #$t->stop('3.1'); | ||
265 | #print $t->report() . "<br>"; | ||
266 | |||
267 | |||
268 | if ($collisions < $max_collide) { | ||
269 | |||
270 | # place image in master array | ||
271 | |||
272 | my ($xpos,$ypos); | ||
273 | my $max_x = $xrand+$image_data_rotated_width; | ||
274 | my $max_y = $yrand+$image_data_rotated_height; | ||
275 | |||
276 | #$t->start('5.1'); | ||
277 | |||
278 | my $vector; | ||
279 | for ($ypos = $yrand;$ypos <= $max_y; $ypos++) { | ||
280 | $vector = @{$master_v}->[$ypos]; | ||
281 | $vector->Interval_Fill($xrand,$max_x); | ||
282 | } | ||
283 | |||
284 | #$t->stop('5.1'); | ||
285 | #print $t->report() . "<br>"; | ||
286 | |||
287 | $done = 1; | ||
288 | |||
289 | if ($gen_image) { | ||
290 | my $card_image = Image::Magick->new(); | ||
291 | $card_image->Read($image_data_publish_loc); | ||
292 | $back_image->Composite(image=>$card_image, x=>$xrand,y=>$yrand); | ||
293 | } | ||
294 | |||
295 | } else { | ||
296 | |||
297 | $attempts++; | ||
298 | } | ||
299 | } | ||
300 | |||
301 | #print "ATTEMPTS: $attempts <br>"; | ||
302 | |||
303 | if ($attempts >= 499) { | ||
304 | #print "UNABLE TO PLACE IMAGE <br>"; | ||
305 | } | ||
306 | |||
307 | |||
308 | return ($xrand,$yrand,$back_image); | ||
309 | } | ||
310 | |||
311 | ################################################## | ||
312 | # | ||
313 | |||
314 | sub check_collisions { | ||
315 | |||
316 | my $master_v = shift; | ||
317 | my $width = shift; | ||
318 | my $height = shift; | ||
319 | my $xrand = shift; | ||
320 | my $yrand = shift; | ||
321 | my $max_collide = shift; | ||
322 | my $page_width = shift; | ||
323 | |||
324 | |||
325 | my $t = new Benchmark::Timer; | ||
326 | |||
327 | my $max_x = $xrand+$width; | ||
328 | my $max_y = $yrand+$height; | ||
329 | |||
330 | #$t->start('3.1'); | ||
331 | |||
332 | my $btot = 0; | ||
333 | |||
334 | my ($xpos,$ypos,$vector,$sdone,$start,$attempts,$min,$max); | ||
335 | |||
336 | for ($ypos = $yrand;$ypos <= $max_y; $ypos++) { | ||
337 | $vector = @{$master_v}->[$ypos]; | ||
338 | |||
339 | $sdone=0; | ||
340 | $start = $xrand; | ||
341 | $attempts = 0; | ||
342 | |||
343 | while (!$sdone && $attempts++ < 20) { | ||
344 | ($min,$max) = $vector->Interval_Scan_inc($start); | ||
345 | |||
346 | if ($max+1 >= $page_width) { | ||
347 | $sdone=1; | ||
348 | } | ||
349 | |||
350 | if ($min < $max_x && $max) { | ||
351 | if ($max > $max_x || $max+1 >= $page_width) { | ||
352 | $btot += $max_x - $min; | ||
353 | $sdone = 1; | ||
354 | } else { | ||
355 | $btot += $max - $min; | ||
356 | $start = $max+1; | ||
357 | } | ||
358 | |||
359 | } elsif ($min > $max_x || !$max || !$min) { | ||
360 | $sdone=1; | ||
361 | } | ||
362 | } | ||
363 | } | ||
364 | |||
365 | #$t->stop('3.1'); | ||
366 | #print $t->report() . "<br>"; | ||
367 | |||
368 | return $btot; | ||
369 | |||
370 | } | ||
371 | |||
diff --git a/cgi-bin/designer.sql b/cgi-bin/designer.sql new file mode 100755 index 0000000..aef45ce --- /dev/null +++ b/cgi-bin/designer.sql | |||
@@ -0,0 +1,15 @@ | |||
1 | drop database if exists designer; | ||
2 | create database designer; | ||
3 | |||
4 | use designer; | ||
5 | |||
6 | |||
7 | |||
8 | create TABLE sketchbook ( | ||
9 | id int not NULL auto_increment, | ||
10 | user_id int not NULL, | ||
11 | sketchbook_data text, | ||
12 | INDEX user_id(user_id), | ||
13 | PRIMARY KEY(id) | ||
14 | ); | ||
15 | |||
diff --git a/cgi-bin/html/login.html b/cgi-bin/html/login.html new file mode 100755 index 0000000..5fe2254 --- /dev/null +++ b/cgi-bin/html/login.html | |||
@@ -0,0 +1,41 @@ | |||
1 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
2 | <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" > | ||
3 | <head> | ||
4 | <title>Please Log In!</title> | ||
5 | <script type="text/javascript" src="http://materialexperience.santoprene.com/siteflow2/scripts/designer.js"></script> | ||
6 | |||
7 | <style type="text/css"> | ||
8 | @import url('http://www.santoprene.com/siteflow2/styles/designer.css'); | ||
9 | @import url('http://materialexperience.santoprene.com/specialcases.css'); | ||
10 | </style> | ||
11 | </head> | ||
12 | |||
13 | <body> | ||
14 | <h3>Please Log In!</h3> | ||
15 | <h4>You need to be registered and logged in to use certain features of the Material Experience Website.</h4> | ||
16 | |||
17 | <TMPL_VAR NAME="error"> | ||
18 | |||
19 | <p> | ||
20 | If you are not yet registered, <a href="http://www.santoprene.com/cgi-bin/register/account.pl?template=designer_"> | ||
21 | register here!</a> If you have forgotten your password, | ||
22 | <a href="http://www.santoprene.com/cgi-bin/lost_password.pl?tmpl=designer">we can e-mail it to you</a>. | ||
23 | </p> | ||
24 | |||
25 | <form action="/cgi-bin/login.pl" method="post"> | ||
26 | <input type="hidden" name="redir" value="/cgi-bin/sketchbook.pl"> | ||
27 | |||
28 | <p> | ||
29 | <label>Username:</label> | ||
30 | <input type="text" name="user" value="<TMPL_VAR NAME="user">" size="30" /> | ||
31 | </p> | ||
32 | <p> | ||
33 | <label>Password:</label> | ||
34 | <input type="password" name="password" /> | ||
35 | <a href="http://www.santoprene.com/cgi-bin/lost_password.pl?tmpl=designer">Forgot it?</a> | ||
36 | </p> | ||
37 | |||
38 | <p><input type="submit" value="Log In" /></p> | ||
39 | </form> | ||
40 | </body> | ||
41 | </html> | ||
diff --git a/cgi-bin/login.pl b/cgi-bin/login.pl new file mode 100755 index 0000000..81e8bf7 --- /dev/null +++ b/cgi-bin/login.pl | |||
@@ -0,0 +1,209 @@ | |||
1 | #!/usr/bin/perl | ||
2 | |||
3 | $|=1; | ||
4 | |||
5 | use strict; | ||
6 | |||
7 | use Apache::Request; | ||
8 | use Apache::Constants qw(REDIRECT); | ||
9 | use MIME::Base64 qw(encode_base64 decode_base64); | ||
10 | use HTML::Template; | ||
11 | |||
12 | use Compose::local_lib; | ||
13 | use Compose::site_user_lib; | ||
14 | |||
15 | $Apache::DBI::DEBUG=2; | ||
16 | |||
17 | my $r = Apache::Request->new(Apache->request); | ||
18 | #$r->send_http_header('text/html'); | ||
19 | |||
20 | my $dbh = new Compose::db_connection('localhost','aes','apache','webconnect'); | ||
21 | |||
22 | my $client_lib = new Compose::client_lib(); | ||
23 | my $local_lib = new Compose::local_lib($client_lib,0); | ||
24 | |||
25 | my $client_id = 1; | ||
26 | $client_lib->setup_client($client_id); | ||
27 | |||
28 | |||
29 | my $site_user_lib = new Compose::site_user_lib($client_lib); | ||
30 | |||
31 | $client_lib->{'dbh'}{'debug'} = 2; | ||
32 | |||
33 | my ($form,$PASS); | ||
34 | |||
35 | foreach my $key (sort $r->param) { | ||
36 | $form->{$key} = $local_lib->fix_spaces($r->param($key)); | ||
37 | #print "$key: $form->{$key} <br>"; | ||
38 | } | ||
39 | |||
40 | my %cookiejar = Apache::Cookie->new($r)->parse; | ||
41 | my $newcookie = Apache::Cookie->new($r); | ||
42 | |||
43 | ##################################################### | ||
44 | # Get the username and password from the cookie. | ||
45 | |||
46 | unless ($cookiejar{'Site'} || ($form->{'user'} && $form->{'password'})) { | ||
47 | $r->send_http_header('text/html'); | ||
48 | |||
49 | my $template = HTML::Template->new( filename => "html/login.html", path => [ "$client_lib->{'client'}->{'server_docroot'}" ], die_on_bad_params => 0); | ||
50 | |||
51 | $template->param('user' => $form->{'user'}); | ||
52 | |||
53 | print $template->output(); | ||
54 | |||
55 | exit(0); | ||
56 | } | ||
57 | |||
58 | my %cookie_hash; | ||
59 | |||
60 | if ( $cookiejar{'Site'} ) { | ||
61 | |||
62 | my @values = $cookiejar{'Site'}->value; | ||
63 | |||
64 | for (my $i=0;$i<scalar(@values);$i+=2) { | ||
65 | $cookie_hash{$values[$i]} = $values[$i+1]; | ||
66 | } | ||
67 | } else { | ||
68 | $cookiejar{'Site'} = ""; | ||
69 | } | ||
70 | |||
71 | my $errors = ""; | ||
72 | |||
73 | |||
74 | if ($form->{'user'} && $form->{'password'}) { | ||
75 | |||
76 | my $site_user = &get_user_info($form->{'user'},$dbh); | ||
77 | |||
78 | if (lc $site_user->{'user_name'} eq lc $form->{'user'}) { | ||
79 | if ($site_user->{'user_passwd'} eq $form->{'password'}) { | ||
80 | &bake_cookie($r,$client_lib,$newcookie,\%cookie_hash,$form,$site_user,$dbh); | ||
81 | exit(0); | ||
82 | } else { | ||
83 | $errors .= qq(The password you entered is incorrect. Please try again.<br>); | ||
84 | } | ||
85 | |||
86 | } else { | ||
87 | $errors .= qq(The user name $form->{'user'} does not exist.<br>); | ||
88 | } | ||
89 | |||
90 | } elsif ($cookie_hash{'Site'}) { | ||
91 | |||
92 | |||
93 | my ($user, $password) = split /:/, decode_base64($cookie_hash{'Site'}), 2; | ||
94 | |||
95 | if ($user eq "" ) { | ||
96 | $errors .= qq($cookie_hash{'Site'} Cookie could not be read. <br>); | ||
97 | |||
98 | } else { | ||
99 | |||
100 | my $site_user = &get_user_info($user,$dbh); | ||
101 | |||
102 | if (defined $site_user->{'user_name'} && lc $site_user->{'user_name'} eq lc $user ) { | ||
103 | |||
104 | if ($site_user->{'user_passwd'} eq $password) { | ||
105 | &bake_cookie($r,$client_lib,$newcookie,\%cookie_hash,$form,$site_user,$dbh); | ||
106 | exit(0); | ||
107 | } else { | ||
108 | $errors .= qq(The password you entered is incorrect. Please try again.<br>); | ||
109 | } | ||
110 | |||
111 | } else { | ||
112 | $errors .= qq(The user name $form->{'user'} does not exist.<br>) if ($form->{'user'}); | ||
113 | } | ||
114 | } | ||
115 | } | ||
116 | |||
117 | |||
118 | $r->send_http_header('text/html'); | ||
119 | |||
120 | my $template = HTML::Template->new( filename => "html/login.html", path => [ "$client_lib->{'client'}->{'server_docroot'}" ], die_on_bad_params => 0); | ||
121 | |||
122 | $template->param('user' => $form->{'user'}); | ||
123 | $template->param('error' => "$errors"); | ||
124 | |||
125 | print $template->output(); | ||
126 | |||
127 | |||
128 | |||
129 | ################################### | ||
130 | |||
131 | sub bake_cookie { | ||
132 | |||
133 | my $r = shift; | ||
134 | my $client_lib = shift; | ||
135 | my $cookiejar = shift; | ||
136 | my $cookie_hash = shift; | ||
137 | my $form = shift; | ||
138 | my $site_user = shift; | ||
139 | my $dbh = shift; | ||
140 | |||
141 | if ( ($cookie_hash->{uri} =~ /login.pl/) || $cookie_hash->{uri} eq "") { | ||
142 | $cookie_hash->{uri} = "/"; | ||
143 | } | ||
144 | $cookie_hash->{uri} = $form->{'redir'}; | ||
145 | |||
146 | |||
147 | # We have some valid credientials, so set an authorization cookie. | ||
148 | my @values = ( | ||
149 | uri => $cookie_hash->{uri}, | ||
150 | Cookie => encode_base64(join ":", ($form->{'user'},$form->{'password'})), | ||
151 | ); | ||
152 | |||
153 | my $c = $r->connection; | ||
154 | my $ip = $c->remote_ip; | ||
155 | my $ins = qq(insert into logins (id,username,last_name,first_name,login_date,ip_address) values (NULL,"$site_user->{'user_name'}","$site_user->{'last_name'}","$site_user->{'first_name'}",NOW(),"$ip")); | ||
156 | $dbh->updateDB($ins); | ||
157 | |||
158 | |||
159 | $cookiejar->name('Site'); | ||
160 | $cookiejar->value(\@values); | ||
161 | $cookiejar->path('/'); | ||
162 | $cookiejar->domain('.santoprene.com'); | ||
163 | $cookiejar->bake; | ||
164 | |||
165 | |||
166 | $r->status(REDIRECT); | ||
167 | $r->headers_out->set(Location => $cookie_hash->{uri}); | ||
168 | $r->send_http_header; | ||
169 | |||
170 | |||
171 | |||
172 | } | ||
173 | ####################### | ||
174 | |||
175 | sub get_user_info { | ||
176 | |||
177 | my $uid = shift; | ||
178 | my $dbh = shift; | ||
179 | |||
180 | my ($qry,$gqry,%user_info,%group_info); | ||
181 | |||
182 | %user_info=%group_info=(); | ||
183 | |||
184 | ########################### | ||
185 | # Internet User | ||
186 | |||
187 | |||
188 | $qry = qq(select admin_user_info.*, DATE_FORMAT(created_on,'%c/%y') as format_created_on from admin_user_info where user_name="$uid" and ((registrant=1 and verified=1) or registrant=0) ); | ||
189 | |||
190 | %user_info = $dbh->queryRawDB($qry); | ||
191 | |||
192 | my %USER_INFO; | ||
193 | |||
194 | foreach my $k (keys %{$user_info{'0'}}) { | ||
195 | $USER_INFO{$k} = $user_info{'0'}{$k}; | ||
196 | } | ||
197 | |||
198 | $USER_INFO{'FULL_NAME'} = "$USER_INFO{'first_name'} " if ($USER_INFO{'first_name'} ne ""); | ||
199 | $USER_INFO{'FULL_NAME'} .= "$USER_INFO{'last_name'} " if ($USER_INFO{'last_name'} ne ""); | ||
200 | |||
201 | foreach my $group (keys %group_info) { | ||
202 | $USER_INFO{'group_info'}{$group_info{$group}{'group_id'}} = $group_info{$group}; | ||
203 | $USER_INFO{'groups'}{$group} = 1; | ||
204 | } | ||
205 | |||
206 | return \%USER_INFO; | ||
207 | } | ||
208 | |||
209 | |||
diff --git a/cgi-bin/sketchbook.pl b/cgi-bin/sketchbook.pl new file mode 100755 index 0000000..8103bd4 --- /dev/null +++ b/cgi-bin/sketchbook.pl | |||
@@ -0,0 +1,142 @@ | |||
1 | #!/usr/bin/perl | ||
2 | |||
3 | $|=1; | ||
4 | |||
5 | srand; | ||
6 | |||
7 | use strict; | ||
8 | |||
9 | use Apache::Request; | ||
10 | use Apache::Constants qw(REDIRECT); | ||
11 | use Benchmark::Timer; | ||
12 | use HTML::Template; | ||
13 | use MIME::Base64 qw(encode_base64 decode_base64); | ||
14 | use Compose::local_lib; | ||
15 | use Compose::db_connection; | ||
16 | |||
17 | my $r = Apache::Request->new(Apache->request); | ||
18 | |||
19 | my $local_lib = new Compose::local_lib(); | ||
20 | |||
21 | my $dbh_aes = new Compose::db_connection('localhost','aes','apache','webconnect'); | ||
22 | my $dbh = new Compose::db_connection('localhost','designer','apache','webconnect'); | ||
23 | |||
24 | my $form; | ||
25 | foreach my $key (sort $r->param) { | ||
26 | $form->{$key} = $local_lib->fix_spaces($r->param($key)); | ||
27 | } | ||
28 | |||
29 | my %cookiejar = Apache::Cookie->new($r)->parse; | ||
30 | my $newcookie = Apache::Cookie->new($r); | ||
31 | my ($user, $password, %user_info, $qry, %user_info, %cookie_hash); | ||
32 | |||
33 | ################################################## | ||
34 | # | ||
35 | unless ($cookiejar{'Site'}) { | ||
36 | print "Content-type: text/html\n"; | ||
37 | print "Status: 403\n"; | ||
38 | exit(0); | ||
39 | ################################################## | ||
40 | # | ||
41 | } elsif ( $cookiejar{'Site'} ) { | ||
42 | |||
43 | my @values = $cookiejar{'Site'}->value; | ||
44 | |||
45 | for (my $i=0;$i<scalar(@values);$i+=2) { | ||
46 | #print qq($values[$i] : $values[$i+1] <br>); | ||
47 | $cookie_hash{$values[$i]} = $values[$i+1]; | ||
48 | } | ||
49 | |||
50 | ($user, $password) = split /:/, decode_base64($cookie_hash{'Cookie'}), 2; | ||
51 | |||
52 | $qry = qq(select * from admin_user_info where user_name="$user"); | ||
53 | |||
54 | %user_info = $dbh_aes->queryRawDB($qry); | ||
55 | |||
56 | if ($user_info{'0'}{'id'} eq "") { | ||
57 | print "Content-type: text/html\n"; | ||
58 | print "Status: 403\n"; | ||
59 | exit(0); | ||
60 | } | ||
61 | } | ||
62 | |||
63 | |||
64 | |||
65 | ################################################## | ||
66 | # | ||
67 | if ($r->method() eq "GET") { | ||
68 | |||
69 | $qry = qq(select * from sketchbook where user_id="$user_info{'0'}{'id'}"); | ||
70 | |||
71 | my %data = $dbh->queryRawDB($qry); | ||
72 | |||
73 | if ($data{'0'}{'sketchbook_data'} eq "") { | ||
74 | if ($form->{'interactive'} ne "false") { | ||
75 | $r->send_http_header('text/html'); | ||
76 | print qq{ | ||
77 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
78 | <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> | ||
79 | <head> | ||
80 | <title>Logged In</title> | ||
81 | <style type="text/css"> | ||
82 | \@import url('http://www.santoprene.com/siteflow2/styles/designer.css'); | ||
83 | \@import url('http://materialexperience.santoprene.com/specialcases.css'); | ||
84 | </style> | ||
85 | </head> | ||
86 | |||
87 | <body> | ||
88 | <h1>Logged In</h1> | ||
89 | <p>Thanks for logging in. You can close this card now.</p> | ||
90 | </body> | ||
91 | </html><div style="display: none"> | ||
92 | }; | ||
93 | } else { | ||
94 | print "Status: 404\n"; | ||
95 | print "Content-type: text/html\n"; | ||
96 | } | ||
97 | |||
98 | exit(0); | ||
99 | } else { | ||
100 | if ($form->{'interactive'} ne "false") { | ||
101 | $r->send_http_header('text/html'); | ||
102 | print qq{ | ||
103 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
104 | <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> | ||
105 | <head> | ||
106 | <title>Logged In</title> | ||
107 | <style type="text/css"> | ||
108 | \@import url('http://www.santoprene.com/siteflow2/styles/designer.css'); | ||
109 | \@import url('http://materialexperience.santoprene.com/specialcases.css'); | ||
110 | </style> | ||
111 | </head> | ||
112 | |||
113 | <body> | ||
114 | <h1>Logged In</h1> | ||
115 | <p>Thanks for logging in. You can close this card now.</p> | ||
116 | </body> | ||
117 | </html><div style="display: none"> | ||
118 | }; | ||
119 | } else { | ||
120 | $r->send_http_header('text/javascript'); | ||
121 | print "$data{'0'}{'sketchbook_data'}\n"; | ||
122 | } | ||
123 | } | ||
124 | |||
125 | ################################################## | ||
126 | # | ||
127 | } else { | ||
128 | |||
129 | if ($form->{'sketchbook_data'} ne "") { | ||
130 | my $upd = qq(delete from sketchbook where user_id="$user_info{'0'}{'id'}"); | ||
131 | $dbh->updateDB($upd); | ||
132 | |||
133 | $form->{'sketchbook_data'} =~ s/"/\\"/g; | ||
134 | |||
135 | my $upd = qq(insert into sketchbook (sketchbook_data,user_id) values ("$form->{'sketchbook_data'}","$user_info{'0'}{'id'}")); | ||
136 | my %data = $dbh->queryRawDB($upd); | ||
137 | } | ||
138 | |||
139 | print "Content-type: text/html\n\n"; | ||
140 | |||
141 | } | ||
142 | |||
diff --git a/cgi-bin/sketchbook_resolver.pl b/cgi-bin/sketchbook_resolver.pl new file mode 100755 index 0000000..cbf64be --- /dev/null +++ b/cgi-bin/sketchbook_resolver.pl | |||
@@ -0,0 +1,49 @@ | |||
1 | #!/usr/bin/perl | ||
2 | |||
3 | $|=1; | ||
4 | |||
5 | use strict; | ||
6 | use Apache::Request; | ||
7 | use Apache::Constants qw(REDIRECT); | ||
8 | use Compose::local_lib; | ||
9 | use Compose::db_connection; | ||
10 | |||
11 | my $r = Apache::Request->new(Apache->request); | ||
12 | my $local_lib = new Compose::local_lib(); | ||
13 | my $dbh = new Compose::db_connection('localhost','aes','apache','webconnect'); | ||
14 | my $item = $local_lib->fix_spaces($r->param('card')); | ||
15 | |||
16 | $r->send_http_header('text/javascript'); | ||
17 | |||
18 | if ($r->method() eq "GET") { | ||
19 | my %res = $dbh->queryRawDB(qq( | ||
20 | SELECT | ||
21 | enc_content_id, | ||
22 | textfield_0 AS title, | ||
23 | select_0 AS category, | ||
24 | CONCAT(clients.website.site_url,clients.website.publish_docroot,"/",human_dir,"/index.html") AS page_loc, | ||
25 | CONCAT(clients.website.site_url,clients.website.publish_docroot,"/",human_dir,"/orig/",image_1) AS rotated_image | ||
26 | FROM | ||
27 | content_data, | ||
28 | clients.website | ||
29 | WHERE | ||
30 | clients.website.site_id = content_data.site_id AND | ||
31 | content_data.site_id = 1 AND | ||
32 | form_id = 48 AND | ||
33 | moderation_status >= 3 AND | ||
34 | date_0 <= NOW() AND | ||
35 | enc_content_id = "$item" AND | ||
36 | !(textfield_5 is NULL or textfield_5 = "") AND | ||
37 | checkbox_2 != "Yes" | ||
38 | )); | ||
39 | |||
40 | print qq( | ||
41 | \({ | ||
42 | "cid" : "$res{0}{'enc_content_id'}", | ||
43 | "title" : "$res{0}{'title'}", | ||
44 | "category" : "$res{0}{'category'}", | ||
45 | "url" : "$res{0}{'page_loc'}", | ||
46 | "chip" : "$res{0}{'rotated_image'}" | ||
47 | }\) | ||
48 | ); | ||
49 | } \ No newline at end of file | ||
diff --git a/cgi-bin/sop_preview_proxy.pl b/cgi-bin/sop_preview_proxy.pl new file mode 100755 index 0000000..0694e11 --- /dev/null +++ b/cgi-bin/sop_preview_proxy.pl | |||
@@ -0,0 +1,28 @@ | |||
1 | #!/usr/bin/perl | ||
2 | |||
3 | # | ||
4 | # Perl Based Same Origin Proxy | ||
5 | # | ||
6 | |||
7 | $|=1; | ||
8 | |||
9 | srand; | ||
10 | use strict; | ||
11 | |||
12 | use Apache::Request; | ||
13 | use LWP::Simple; | ||
14 | use LWP::UserAgent; | ||
15 | |||
16 | my $r = Apache::Request->new(Apache->request); | ||
17 | $r->send_http_header('text/javascript'); | ||
18 | |||
19 | my @url = split(/site=/,$r->parsed_uri()->unparse()); | ||
20 | |||
21 | my $browser = LWP::UserAgent->new; | ||
22 | $browser->credentials( | ||
23 | 'admin.santoprene.com:80', | ||
24 | 'Admin', | ||
25 | 'DESIGNER_SOP_USER' => 'dsop4edit' | ||
26 | ); | ||
27 | |||
28 | print $browser->get($url[1])->content; | ||
diff --git a/cgi-bin/sop_proxy.pl b/cgi-bin/sop_proxy.pl new file mode 100755 index 0000000..4a3b370 --- /dev/null +++ b/cgi-bin/sop_proxy.pl | |||
@@ -0,0 +1,18 @@ | |||
1 | #!/usr/bin/perl | ||
2 | |||
3 | # | ||
4 | # Perl Based Same Origin Proxy | ||
5 | # | ||
6 | |||
7 | $|=1; | ||
8 | |||
9 | srand; | ||
10 | use strict; | ||
11 | |||
12 | use Apache::Request; | ||
13 | use LWP::Simple; | ||
14 | |||
15 | my $r = Apache::Request->new(Apache->request); | ||
16 | $r->send_http_header('text/javascript'); | ||
17 | |||
18 | print get $r->param('site'); | ||
@@ -0,0 +1,71 @@ | |||
1 | #!/bin/bash | ||
2 | # | ||
3 | # ExxonMobil Designer Site Deploy Script | ||
4 | # by Mike Crute, EYEMG (mcrute@eyemg.com) | ||
5 | # | ||
6 | # This is the deployment script for the AES designer site | ||
7 | # it exists to script the last piece of building and deploying | ||
8 | # the designer site. | ||
9 | # | ||
10 | |||
11 | if [ `hostname` == 'calvin.eyemg.com' ]; then | ||
12 | cd /usr/web/designer | ||
13 | rm -rf cgi-bin/ docroot/ | ||
14 | tar -xvjf aes_designer.tbz2 | ||
15 | echo "Code Deployed: `date +'%Y-%m-%d %H:%M:%S'` EST" >> docroot/build.date | ||
16 | rm deploy | ||
17 | else | ||
18 | RED="\033[0;31m" | ||
19 | GREEN="\033[0;32m" | ||
20 | BROWN="\033[0;33m" | ||
21 | CYAN="\033[0;36m" | ||
22 | COLOR_CLEAR="\033[00m" | ||
23 | |||
24 | clear | ||
25 | echo -e "${BROWN}***************************************************" | ||
26 | echo -e "${BROWN}* *" | ||
27 | echo -e "${BROWN}*${COLOR_CLEAR} ${CYAN}AES Designer Site Deployment Script${COLOR_CLEAR} ${BROWN}*" | ||
28 | echo -e "${BROWN}* *" | ||
29 | echo -e "${BROWN}***************************************************" | ||
30 | echo -e "$COLOR_CLEAR" | ||
31 | echo -n "Your SSH Username: " | ||
32 | read myuser | ||
33 | echo "" | ||
34 | |||
35 | if [[ $myuser == '' ]]; then | ||
36 | echo -e "\n${RED}FAILED:${COLOR_CLEAR} Enter a username." | ||
37 | exit 1 | ||
38 | fi | ||
39 | |||
40 | ./make | ||
41 | |||
42 | if [[ $? != 0 ]]; then | ||
43 | echo -e "\n${RED}FAILED:${COLOR_CLEAR} Make failed, wisely refusing to continue." | ||
44 | exit 1 | ||
45 | fi | ||
46 | |||
47 | scp ./aes_designer.tbz2 $myuser@calvin.eyemg.com:/usr/web/designer | ||
48 | |||
49 | if [[ $? != 0 ]]; then | ||
50 | echo -e "\n${RED}FAILED:${COLOR_CLEAR} Failed to deploy code tarball." | ||
51 | exit 1 | ||
52 | fi | ||
53 | |||
54 | scp ./deploy $myuser@calvin.eyemg.com:/usr/web/designer | ||
55 | |||
56 | if [[ $? != 0 ]]; then | ||
57 | echo -e "\n${RED}FAILED:${COLOR_CLEAR} Failed to deploy install script." | ||
58 | exit 1 | ||
59 | fi | ||
60 | |||
61 | ssh $myuser@calvin.eyemg.com /usr/web/designer/deploy > /dev/null 2>&1 | ||
62 | |||
63 | if [[ $? != 0 ]]; then | ||
64 | echo -e "\n${RED}FAILED:${COLOR_CLEAR} Remote command execution failed." | ||
65 | exit 1 | ||
66 | fi | ||
67 | |||
68 | rm aes_designer.tbz2 | ||
69 | |||
70 | echo -e "\n ${GREEN}SUCCESS:${COLOR_CLEAR} Go check and make sure!" | ||
71 | fi | ||
diff --git a/docroot/.htaccess b/docroot/.htaccess new file mode 100755 index 0000000..70fc492 --- /dev/null +++ b/docroot/.htaccess | |||
@@ -0,0 +1,46 @@ | |||
1 | # | ||
2 | # Material Experience - .htaccess File | ||
3 | # | ||
4 | # EYEMG - Interactive Media Group | ||
5 | # Created by Mike Crute (mcrute@eyemg.com) | ||
6 | # Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | # | ||
8 | # Workarounds for missing mod_gzip and caching. This file | ||
9 | # is pre-processed before the site goes into production | ||
10 | # since we don't really gzip anything in development. | ||
11 | # | ||
12 | # WARNING!: Seconds of code marked with ### PRODUCTION### | ||
13 | # are blocks un-commented by the build system, you can | ||
14 | # add or remove them just be careful what you ask for. | ||
15 | # | ||
16 | |||
17 | # Cache Control Headers | ||
18 | ### PRODUCTION ### | ||
19 | #ExpiresActive on | ||
20 | #ExpiresDefault "access plus 1 week" | ||
21 | ### END PRODUCTION ### | ||
22 | |||
23 | # Disable the ETag Header (per YSlow) | ||
24 | # | ||
25 | # I know that this probably doesn't mean anything for us | ||
26 | # because we don't serve AES with a cluster but just for | ||
27 | # the sake of getting straight As in YSlow we'll remove | ||
28 | # them. | ||
29 | FileEtag none | ||
30 | |||
31 | ### PRODUCTION ### | ||
32 | #<Files application.js> | ||
33 | # Header set Content-Encoding "gzip" | ||
34 | #</Files> | ||
35 | |||
36 | #<Files application.css> | ||
37 | # Header set Content-Encoding "gzip" | ||
38 | #</Files> | ||
39 | ### END PRODUCTION ### | ||
40 | |||
41 | <Files index.html> | ||
42 | Header set Content-Type "text/html;charset=utf-8" | ||
43 | ### PRODUCTION ### | ||
44 | # Header set Content-Encoding "gzip" | ||
45 | ### END PRODUCTION ### | ||
46 | </Files> | ||
diff --git a/docroot/account_deleted.html b/docroot/account_deleted.html new file mode 100755 index 0000000..04af7cc --- /dev/null +++ b/docroot/account_deleted.html | |||
@@ -0,0 +1,23 @@ | |||
1 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
2 | <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" > | ||
3 | <head> | ||
4 | <title>Please Log In!</title> | ||
5 | <script type="text/javascript" src="http://materialexperience.santoprene.com/siteflow2/scripts/designer.js"></script> | ||
6 | |||
7 | <style type="text/css"> | ||
8 | @import url('http://www.santoprene.com/siteflow2/styles/designer.css'); | ||
9 | @import url('http://materialexperience.santoprene.com/specialcases.css'); | ||
10 | </style> | ||
11 | </head> | ||
12 | |||
13 | <body> | ||
14 | <h3>Your account has been deleted!</h3> | ||
15 | |||
16 | <p>Your account information has been deleted and you are now logged out of Material Experience Website. <br /> | ||
17 | You will have to register and log in again if you would like to gain access to certain areas of this Web site.</p> | ||
18 | |||
19 | <p>You can close this card to continue.</p> | ||
20 | |||
21 | |||
22 | </body> | ||
23 | </html> | ||
diff --git a/docroot/application.css b/docroot/application.css new file mode 100755 index 0000000..cd0c540 --- /dev/null +++ b/docroot/application.css | |||
@@ -0,0 +1,463 @@ | |||
1 | /* | ||
2 | * Material Experience - Site Stylesheet | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | * | ||
8 | * Site stylesheet the covers most of the look and feel of the site. | ||
9 | * Some of the CSS is also in the application logic so if you can't | ||
10 | * find what your looking for here go there. | ||
11 | */ | ||
12 | |||
13 | /* | ||
14 | * General Application Styles | ||
15 | */ | ||
16 | html | ||
17 | { | ||
18 | overflow: hidden; | ||
19 | } | ||
20 | |||
21 | body | ||
22 | { | ||
23 | font: 10px Verdana,sans-serif; | ||
24 | margin: 0px; | ||
25 | overflow: hidden; | ||
26 | } | ||
27 | |||
28 | a | ||
29 | { | ||
30 | color: blue; | ||
31 | text-decoration: underline; | ||
32 | } | ||
33 | |||
34 | a img | ||
35 | { | ||
36 | border: 0px; | ||
37 | } | ||
38 | |||
39 | h1 | ||
40 | { | ||
41 | font-size: 1.7em; | ||
42 | } | ||
43 | |||
44 | h2 | ||
45 | { | ||
46 | font-size: 1.6em; | ||
47 | } | ||
48 | |||
49 | img | ||
50 | { | ||
51 | /* The infamous PNG fix */ | ||
52 | behavior: url('pngbehavior.htc'); | ||
53 | } | ||
54 | |||
55 | /* | ||
56 | * Application Header Styles | ||
57 | */ | ||
58 | img#tagline | ||
59 | { | ||
60 | position: absolute; | ||
61 | top: 0px; | ||
62 | right: 0px; | ||
63 | } | ||
64 | |||
65 | div#header, div#footer | ||
66 | { | ||
67 | background: white; | ||
68 | height: 60px; | ||
69 | width: 100%; | ||
70 | position: relative; | ||
71 | z-index: 99; | ||
72 | } | ||
73 | |||
74 | div#header | ||
75 | { | ||
76 | height: 80px; | ||
77 | } | ||
78 | |||
79 | p#version | ||
80 | { | ||
81 | position: absolute; | ||
82 | bottom: 0px; | ||
83 | right: 7px; | ||
84 | z-index: 100; | ||
85 | color: red; | ||
86 | } | ||
87 | |||
88 | img#logo | ||
89 | { | ||
90 | position: absolute; | ||
91 | z-index: 9999999999; | ||
92 | cursor: pointer; | ||
93 | padding: 10px 0px 0px 10px; | ||
94 | } | ||
95 | |||
96 | /* | ||
97 | * Table and Chip Element Styles | ||
98 | */ | ||
99 | div.appDecorator | ||
100 | { | ||
101 | height: 21px; | ||
102 | width: 6px; | ||
103 | } | ||
104 | |||
105 | div.table | ||
106 | { | ||
107 | position: absolute; | ||
108 | top: 60px; | ||
109 | left: 0px; | ||
110 | width: 100%; | ||
111 | } | ||
112 | |||
113 | img.chip | ||
114 | { | ||
115 | position: absolute; | ||
116 | cursor: pointer; | ||
117 | } | ||
118 | |||
119 | /* | ||
120 | * Application Footer Styles | ||
121 | */ | ||
122 | p#copyright, div#footer p#copyright a | ||
123 | { | ||
124 | color: rgb(190,190,190); | ||
125 | font-size: 10px; | ||
126 | margin-right: 0px; | ||
127 | text-transform: none; | ||
128 | } | ||
129 | |||
130 | div#footer p#copyright a | ||
131 | { | ||
132 | text-decoration: underline; | ||
133 | } | ||
134 | |||
135 | p#copyright | ||
136 | { | ||
137 | width: 100%; | ||
138 | display: block; | ||
139 | position: absolute; | ||
140 | bottom: 0px; | ||
141 | left: 0px; | ||
142 | text-align: center; | ||
143 | z-index: 100; | ||
144 | } | ||
145 | |||
146 | div#footer | ||
147 | { | ||
148 | position: absolute; | ||
149 | bottom: -1px; | ||
150 | height: 40px; | ||
151 | text-align: center; | ||
152 | padding: 5px 0px 10px 0px; | ||
153 | } | ||
154 | |||
155 | div#footer img | ||
156 | { | ||
157 | position: relative; | ||
158 | top: 6px; | ||
159 | margin-right: 5px; | ||
160 | } | ||
161 | |||
162 | div#footer a | ||
163 | { | ||
164 | font-size: 1.2em; | ||
165 | text-transform: uppercase; | ||
166 | text-decoration: none; | ||
167 | margin-right: 25px; | ||
168 | } | ||
169 | |||
170 | /* | ||
171 | * History Menu Styles | ||
172 | */ | ||
173 | div.history | ||
174 | { | ||
175 | position: absolute; | ||
176 | right: 20px; | ||
177 | top: 20px; | ||
178 | padding: 3px 10px; | ||
179 | cursor: pointer; | ||
180 | background: none; | ||
181 | text-align: right; | ||
182 | } | ||
183 | |||
184 | ul#history:hover, ul#history.hovered | ||
185 | { | ||
186 | display: block; | ||
187 | } | ||
188 | |||
189 | ul#history | ||
190 | { | ||
191 | position: absolute; | ||
192 | z-index: 100; | ||
193 | display: none; | ||
194 | margin: 0px; | ||
195 | padding: 10px 0px 0px 0px; | ||
196 | color: rgb(255, 255, 255); | ||
197 | text-decoration: none; | ||
198 | width: 200px; | ||
199 | } | ||
200 | |||
201 | ul#history li | ||
202 | { | ||
203 | background: rgb(145, 145, 145); | ||
204 | list-style: none; | ||
205 | padding: 5px 10px; | ||
206 | margin: 0px; | ||
207 | border-bottom: 1px solid white; | ||
208 | text-align: left; | ||
209 | color: white; | ||
210 | text-decoration: none; | ||
211 | } | ||
212 | |||
213 | ul#history li a | ||
214 | { | ||
215 | color: white; | ||
216 | text-decoration: none; | ||
217 | border: 0px; | ||
218 | display: block; | ||
219 | width: 100%; | ||
220 | height: 100%; | ||
221 | } | ||
222 | |||
223 | ul#history li:last-child | ||
224 | { | ||
225 | border-bottom: 0px; | ||
226 | } | ||
227 | |||
228 | /* | ||
229 | * Rounded Corner Styles | ||
230 | */ | ||
231 | .round { display: block; } | ||
232 | .round * { display:block; height: 1px; overflow: hidden; } | ||
233 | .rcSlice_1 { margin: 0 10px; } | ||
234 | .rcSlice_2 { margin: 0 8px; } | ||
235 | .rcSlice_3 { margin: 0 6px; } | ||
236 | .rcSlice_4 { margin: 0 5px; } | ||
237 | .rcSlice_5 { margin: 0 4px; } | ||
238 | .rcSlice_6 { margin: 0 3px; } | ||
239 | .rcSlice_7 { margin: 0 3px; } | ||
240 | .rcSlice_8 { margin: 0 2px; height: 1px; } | ||
241 | .rcSlice_9 { margin: 0 2px; height: 2px; } | ||
242 | .rcSlice_10 { margin: 0 1px; height: 3px; } | ||
243 | |||
244 | /* | ||
245 | * Card Frame Styles (Card Class) | ||
246 | */ | ||
247 | img.commandbtn | ||
248 | { | ||
249 | height: 11px; | ||
250 | width: 11px; | ||
251 | display: inline; | ||
252 | margin: 0px 2px; | ||
253 | cursor: pointer; | ||
254 | } | ||
255 | |||
256 | span.commandlabel | ||
257 | { | ||
258 | height: auto; | ||
259 | z-index: 9; | ||
260 | display: inline; | ||
261 | position: relative; | ||
262 | top: -2px; | ||
263 | font-size: 1.1em; | ||
264 | } | ||
265 | |||
266 | span.commandlabel * | ||
267 | { | ||
268 | height: auto; | ||
269 | display: inline; | ||
270 | font-size: 1.1em; | ||
271 | } | ||
272 | |||
273 | span.cardTitle | ||
274 | { | ||
275 | font-size: 1.1em; | ||
276 | } | ||
277 | |||
278 | .cardTitle | ||
279 | { | ||
280 | position: absolute; | ||
281 | color: white; | ||
282 | z-index: 1; | ||
283 | height: auto; | ||
284 | top: 4px; | ||
285 | left: 15px; | ||
286 | } | ||
287 | |||
288 | .buttonbox | ||
289 | { | ||
290 | position: absolute; | ||
291 | right: 15px; | ||
292 | top: 6px; | ||
293 | z-index: 100; | ||
294 | height: auto; | ||
295 | color: white; | ||
296 | font-size: 0.8em; | ||
297 | } | ||
298 | |||
299 | /* | ||
300 | * Card Resource List Styles (Card Layout) | ||
301 | */ | ||
302 | .resourceList | ||
303 | { | ||
304 | margin: 0px; | ||
305 | padding: 0px; | ||
306 | } | ||
307 | |||
308 | .resourceList li | ||
309 | { | ||
310 | list-style: none; | ||
311 | padding-bottom: 7px; | ||
312 | } | ||
313 | |||
314 | /* | ||
315 | * Card Thumbnail Styles (Card Layout) | ||
316 | */ | ||
317 | .mediaThumb | ||
318 | { | ||
319 | width: 50px; | ||
320 | height: 50px; | ||
321 | padding: 10px 10px 0px 0px; | ||
322 | cursor: pointer; | ||
323 | } | ||
324 | |||
325 | .thumbStrip | ||
326 | { | ||
327 | text-align: center; | ||
328 | } | ||
329 | |||
330 | .thumbStrip img | ||
331 | { | ||
332 | width: 50px; | ||
333 | height: 50px; | ||
334 | cursor: pointer; | ||
335 | } | ||
336 | |||
337 | /* | ||
338 | * Sketchbook Trash Can Styles | ||
339 | */ | ||
340 | #sbTrash | ||
341 | { | ||
342 | position: absolute; | ||
343 | bottom: 20px; | ||
344 | right: 10px; | ||
345 | } | ||
346 | |||
347 | .trashHi | ||
348 | { | ||
349 | background-color: lightyellow; | ||
350 | } | ||
351 | |||
352 | /* | ||
353 | * Skip Link Styles (Intro Card) | ||
354 | */ | ||
355 | .skiplink | ||
356 | { | ||
357 | color: rgb(145, 145, 145); | ||
358 | text-decoration: none; | ||
359 | font-size: 1.1em; | ||
360 | position: relative; | ||
361 | z-index: 99; | ||
362 | } | ||
363 | |||
364 | .skipimg | ||
365 | { | ||
366 | vertical-align: bottom; | ||
367 | margin-left: 3px; | ||
368 | } | ||
369 | |||
370 | .skipbox | ||
371 | { | ||
372 | text-align: center; | ||
373 | margin-top: -50px; | ||
374 | } | ||
375 | |||
376 | /* | ||
377 | * Custom Content Styles | ||
378 | */ | ||
379 | ul#navigation | ||
380 | { | ||
381 | margin: 0px; | ||
382 | padding: 0px; | ||
383 | } | ||
384 | |||
385 | ul#navigation li | ||
386 | { | ||
387 | margin: 5px 30px 0px 0px; | ||
388 | list-style: none; | ||
389 | border: 1px solid white; | ||
390 | } | ||
391 | |||
392 | ul#navigation li a | ||
393 | { | ||
394 | display: block; | ||
395 | text-decoration: none; | ||
396 | padding: 2px; | ||
397 | border: 3px solid black; | ||
398 | } | ||
399 | |||
400 | ul#navigation li ul li a | ||
401 | { | ||
402 | margin: 0px -60px 0px -30px; | ||
403 | } | ||
404 | |||
405 | .selected | ||
406 | { | ||
407 | background: black; | ||
408 | color: white; | ||
409 | padding: 5px; | ||
410 | } | ||
411 | /* | ||
412 | .narrowCol, .wideCol | ||
413 | { | ||
414 | float: left; | ||
415 | height: 460px; | ||
416 | overflow: hidden; | ||
417 | } | ||
418 | |||
419 | .narrowCol | ||
420 | { | ||
421 | width: 290px; | ||
422 | margin-right: 20px; | ||
423 | } | ||
424 | |||
425 | .wideCol | ||
426 | { | ||
427 | width: 569px; | ||
428 | } | ||
429 | */ | ||
430 | |||
431 | /* | ||
432 | * Custom Content Color Styles | ||
433 | */ | ||
434 | ul#navigation li a | ||
435 | { | ||
436 | color: white; | ||
437 | background: rgb(100, 100, 100); | ||
438 | border: 3px solid rgb(100, 100, 100); | ||
439 | } | ||
440 | |||
441 | ul#navigation li ul li a | ||
442 | { | ||
443 | background: white; | ||
444 | color: rgb(100, 100, 100); | ||
445 | } | ||
446 | |||
447 | .selected | ||
448 | { | ||
449 | background: #ccc; | ||
450 | color: #000; | ||
451 | padding: 0px; | ||
452 | } | ||
453 | |||
454 | span.glossterm | ||
455 | { | ||
456 | text-transform: uppercase; | ||
457 | } | ||
458 | |||
459 | span.anchor | ||
460 | { | ||
461 | text-transform: uppercase; | ||
462 | font-weight: bold; | ||
463 | } | ||
diff --git a/docroot/application.js b/docroot/application.js new file mode 100755 index 0000000..22bf06e --- /dev/null +++ b/docroot/application.js | |||
@@ -0,0 +1,67 @@ | |||
1 | /* | ||
2 | * Material Experience - Development Loader | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | * | ||
8 | * Loads the designer site in the development environment, | ||
9 | * this facilitates us having multiple class files. This | ||
10 | * file is pre-processed by the build system to pull | ||
11 | * out include files for building into the final site. | ||
12 | * | ||
13 | * Thus, the require statements are magic :-) | ||
14 | */ | ||
15 | |||
16 | var DesignerSite = | ||
17 | { | ||
18 | Version: "$Revision$".match(/[0-9]+/), | ||
19 | |||
20 | require: function(libraryName) | ||
21 | { | ||
22 | document.write('<script type="text/javascript" src="' + libraryName + '"></script>'); | ||
23 | }, | ||
24 | |||
25 | load: function() | ||
26 | { | ||
27 | // Prototype Stuff | ||
28 | this.require("lib/prototype.js"); | ||
29 | |||
30 | // Script.aculo.us Stuff | ||
31 | this.require("lib/scriptaculous/scriptaculous.js"); | ||
32 | this.require("lib/scriptaculous/effects.js"); | ||
33 | this.require("lib/scriptaculous/builder.js"); | ||
34 | this.require("lib/scriptaculous/dragdrop.js"); | ||
35 | this.require("lib/scriptaculous/slider.js"); | ||
36 | |||
37 | // Other 3rd Party Libraries | ||
38 | this.require("lib/swfobject/swfobject.js"); | ||
39 | this.require("classes/decoder.module.js"); | ||
40 | |||
41 | // Application Code | ||
42 | this.require("classes/utility.js"); | ||
43 | this.require("classes/chip.class.js"); | ||
44 | this.require("classes/cookie.class.js"); | ||
45 | this.require("classes/card.class.js"); | ||
46 | this.require("classes/bezel.class.js"); | ||
47 | this.require("classes/overlay.class.js"); | ||
48 | this.require("classes/roundcorners.class.js"); | ||
49 | this.require("classes/table.class.js"); | ||
50 | this.require("classes/history.class.js"); | ||
51 | this.require("classes/sketchbook.class.js"); | ||
52 | this.require("classes/application.js"); | ||
53 | |||
54 | // Layout Engines | ||
55 | this.require("classes/layout.class.js"); | ||
56 | this.require("classes/layouts/layout.error.class.js"); | ||
57 | this.require("classes/layouts/layout.primary.class.js"); | ||
58 | this.require("classes/layouts/layout.special.class.js"); | ||
59 | this.require("classes/layouts/layout.custom.class.js"); | ||
60 | |||
61 | // Namespaces and Data | ||
62 | this.require("data/strings.en.js"); | ||
63 | this.require("classes/sme.namespace.js"); | ||
64 | } | ||
65 | }; | ||
66 | |||
67 | DesignerSite.load(); \ No newline at end of file | ||
diff --git a/docroot/blank.gif b/docroot/blank.gif new file mode 100755 index 0000000..057b51b --- /dev/null +++ b/docroot/blank.gif | |||
@@ -0,0 +1,5 @@ | |||
1 | XSym | ||
2 | 0016 | ||
3 | 88e2976783fa96f9cad2c9a96c39fa58 | ||
4 | images/blank.gif | ||
5 | \ No newline at end of file | ||
diff --git a/docroot/classes/application.js b/docroot/classes/application.js new file mode 100755 index 0000000..d8776d8 --- /dev/null +++ b/docroot/classes/application.js | |||
@@ -0,0 +1,227 @@ | |||
1 | /* | ||
2 | * Material Experience - Main Application Code | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | * | ||
8 | * Core application code that is responsible for starting up the application | ||
9 | * and initializing the core objects. | ||
10 | */ | ||
11 | |||
12 | /* | ||
13 | * Register global actions for AJAX responders. | ||
14 | */ | ||
15 | Ajax.Responders.register( | ||
16 | { | ||
17 | /* | ||
18 | * When an AJAX connection is created show the bezel that says | ||
19 | * "loading data". | ||
20 | */ | ||
21 | onCreate: function() | ||
22 | { | ||
23 | if (!SME.AJAXBezel) | ||
24 | { | ||
25 | SME.AJAXBezel = new Bezel({ displayTime: 0, destroy: false }).show(Strings.loadingAnim); | ||
26 | } | ||
27 | else | ||
28 | { | ||
29 | SME.AJAXBezel.show(Strings.loadingAnim); | ||
30 | } | ||
31 | }, | ||
32 | |||
33 | /* | ||
34 | * Each time a requester completes we check to see if it was the last | ||
35 | * one, if it is then we take down the loading bezel. | ||
36 | */ | ||
37 | onComplete: function() | ||
38 | { | ||
39 | if (Ajax.activeRequestCount == 0) | ||
40 | { | ||
41 | SME.AJAXBezel.hide(); | ||
42 | } | ||
43 | }, | ||
44 | |||
45 | /* | ||
46 | * When something goes wrong with loading data we die. | ||
47 | */ | ||
48 | onException: function(transport, exception) | ||
49 | { | ||
50 | if (SME.debug) | ||
51 | { | ||
52 | console.error(exception); | ||
53 | } | ||
54 | |||
55 | SME.AJAXBezel.hide(); | ||
56 | new Bezel({ displayTime: 0 }).show(Strings.ajaxError); | ||
57 | } | ||
58 | }); | ||
59 | |||
60 | /* | ||
61 | * Load the data for the card tables. | ||
62 | */ | ||
63 | function loadTables() | ||
64 | { | ||
65 | new Ajax.Request(SME.url.tableList, | ||
66 | { | ||
67 | method: "get", | ||
68 | |||
69 | onSuccess: function(transport) | ||
70 | { | ||
71 | transport.responseText.evalJSON().each(function(data) | ||
72 | { | ||
73 | var windowDims = window.getDimensions(); | ||
74 | |||
75 | var table = new CardTable( | ||
76 | { | ||
77 | color: data.color, | ||
78 | name: data.name, | ||
79 | id: data.tid, | ||
80 | decorate: data.decorate | ||
81 | }); | ||
82 | |||
83 | // Push the table onto the global table cache (see the SME namespace | ||
84 | // for more information about the global table cache) | ||
85 | SME.tables.push(table); | ||
86 | |||
87 | // Subtracting the max chip width and height ensures that cards don't | ||
88 | // fall too far off the tables | ||
89 | table.loadChipData(SME.url.cardTables, | ||
90 | { | ||
91 | table: data.tid, | ||
92 | w: windowDims.width - (SME.sizes.chipMax.width / 2), | ||
93 | h: windowDims.height - (SME.sizes.chipMax.height / 2) | ||
94 | }, false); | ||
95 | }); | ||
96 | |||
97 | // Initialize the sketchbook | ||
98 | SME.sketchbook = new Sketchbook(); | ||
99 | |||
100 | // By default show the home table. When the history manager loads for the | ||
101 | // first time (after this step) it will load the right table from the URL | ||
102 | // if applicable. This just ensures that a table is always displayed. | ||
103 | CardTable.showTable("home"); | ||
104 | |||
105 | // Show the tool box in the upper right | ||
106 | showToolBox(); | ||
107 | } | ||
108 | }); | ||
109 | |||
110 | // Start up the history manager | ||
111 | SME.history = new HistoryManager().pollEvents(); | ||
112 | } | ||
113 | |||
114 | /* | ||
115 | * Show an intro card. This function will gracefully pass if there are no | ||
116 | * intro cards to be shown. | ||
117 | */ | ||
118 | function showIntro() | ||
119 | { | ||
120 | new Ajax.Request(SME.url.introCards, | ||
121 | { | ||
122 | method: "get", | ||
123 | |||
124 | onSuccess: function(transport) | ||
125 | { | ||
126 | var data = transport.responseText.cleanJSON(); | ||
127 | |||
128 | // If there is no intro card then just pass | ||
129 | if (data == "" || data == "\n") | ||
130 | { | ||
131 | return loadTables(); | ||
132 | } | ||
133 | else | ||
134 | { | ||
135 | data = data.evalJSON(); | ||
136 | |||
137 | // By default just show the first card in the | ||
138 | // feed | ||
139 | var myCard = data[0]; | ||
140 | |||
141 | // If more than one card then pick one at random | ||
142 | // to display (per client requirements). | ||
143 | if (data.length > 1) | ||
144 | { | ||
145 | myCard = data[Math.floor(1 + (data.length - 1) * Math.random())]; | ||
146 | } | ||
147 | } | ||
148 | |||
149 | var card = new Card( | ||
150 | { | ||
151 | color: SME.colors.grey, | ||
152 | title: '', | ||
153 | addExtraButtons: false, | ||
154 | contID: myCard, | ||
155 | |||
156 | onFadeComplete: function() | ||
157 | { | ||
158 | loadTables(); | ||
159 | } | ||
160 | }).show(); | ||
161 | } | ||
162 | }); | ||
163 | } | ||
164 | |||
165 | /* | ||
166 | * Show the toolbox in the upper right side of the screen. | ||
167 | */ | ||
168 | function showToolBox() | ||
169 | { | ||
170 | new Effect.Appear($$("div#header div.history")[0]); | ||
171 | |||
172 | // On mouseover of the history link show the dropdown | ||
173 | $$("div.history a.history")[0].observe("mouseover", function() | ||
174 | { | ||
175 | SME.history.getDropDown(); | ||
176 | }); | ||
177 | |||
178 | // Show the login screen when the login link is clicked | ||
179 | $("loginLink").observe("click", Sketchbook.showLoginScreen); | ||
180 | |||
181 | // Check the login when they first hit the page, saves people logging | ||
182 | // in again | ||
183 | Sketchbook.checkLogin(); | ||
184 | } | ||
185 | |||
186 | /* | ||
187 | * Main program function, this starts up the interface and does various | ||
188 | * little fixups of interface elements. | ||
189 | */ | ||
190 | function main() | ||
191 | { | ||
192 | if (SME.debug) | ||
193 | { | ||
194 | new Bezel({ displayTime: 5 }).show("Full Debug Mode is Enabled"); | ||
195 | } | ||
196 | |||
197 | // Check the resolution at load and when the screen size changes | ||
198 | window.checkResolution(); | ||
199 | Event.observe(window, "resize", window.checkResolution); | ||
200 | |||
201 | // Show the intro card or load the tables | ||
202 | if (SME.skipIntro || window.location.hash.length > 1) | ||
203 | { | ||
204 | loadTables(); | ||
205 | } | ||
206 | else | ||
207 | { | ||
208 | showIntro(); | ||
209 | } | ||
210 | |||
211 | // Per Bryan this should be a single year if 2007 otherwise it should | ||
212 | // be a date range starting on the year that the site was released. | ||
213 | if (new Date().getFullYear() > 2007) | ||
214 | { | ||
215 | $("copyright").innerHTML = $("copyright").innerHTML.replace(/####/, "2007-" + | ||
216 | new Date().getFullYear()); | ||
217 | } | ||
218 | else | ||
219 | { | ||
220 | $("copyright").innerHTML = $("copyright").innerHTML.replace(/####/, "2007"); | ||
221 | } | ||
222 | } | ||
223 | |||
224 | /* | ||
225 | * Start the program up when the window loads. | ||
226 | */ | ||
227 | Event.observe(window, "load", main); \ No newline at end of file | ||
diff --git a/docroot/classes/bezel.class.js b/docroot/classes/bezel.class.js new file mode 100755 index 0000000..d29ed4f --- /dev/null +++ b/docroot/classes/bezel.class.js | |||
@@ -0,0 +1,154 @@ | |||
1 | /* | ||
2 | * Material Experience - Bezel Class | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | * | ||
8 | * Notification bezel class mimics the notification bezel in BBEdit | ||
9 | * (and to a lesser extent, OS X). | ||
10 | */ | ||
11 | |||
12 | var Bezel = Class.create(); | ||
13 | Object.extend(Bezel.prototype, | ||
14 | { | ||
15 | /* | ||
16 | * Creates the bezel HTML elements and adds them to the document body. | ||
17 | */ | ||
18 | initialize: function() | ||
19 | { | ||
20 | this.options = Object.extend( | ||
21 | { | ||
22 | background: "black", // Background color of the bezel | ||
23 | opacity: 0.8, // % Opacity of the bezel | ||
24 | fontSize: "2em", // Font Size | ||
25 | fadeTime: 2, // Fade Time | ||
26 | destroy: true, // Destroy bezel on fade out | ||
27 | displayTime: 1, // How long to display the bezel - 0 is "sticky" | ||
28 | onShow: Prototype.emptyFunction, // After show callback | ||
29 | afterHide: Prototype.emptyFunction // After hide callback | ||
30 | }, arguments[0] || {}); | ||
31 | |||
32 | this.visible = false; | ||
33 | |||
34 | var Rounder = new RoundedCorners(this.options.background); | ||
35 | |||
36 | this.bezel = Element.extend(document.createElement("div")); | ||
37 | this.message = Element.extend(document.createElement("div")); | ||
38 | |||
39 | this.bezel.appendChild(Rounder.get(Rounder.directions.top)); | ||
40 | this.bezel.appendChild(this.message); | ||
41 | this.bezel.appendChild(Rounder.get(Rounder.directions.bottom)); | ||
42 | |||
43 | document.body.appendChild(this.bezel); | ||
44 | |||
45 | this.bezel.setStyle( | ||
46 | { | ||
47 | opacity: this.options.opacity, | ||
48 | zIndex: 9999999, | ||
49 | position: "absolute", | ||
50 | display: "none", | ||
51 | width: "auto", | ||
52 | cursor: "pointer" | ||
53 | }); | ||
54 | |||
55 | this.message.setStyle( | ||
56 | { | ||
57 | background: this.options.background, | ||
58 | fontSize: this.options.fontSize, | ||
59 | color: "white", | ||
60 | padding: "0px 1em", | ||
61 | textAlign: "center" | ||
62 | }); | ||
63 | |||
64 | // IE does not properly size the bezel so we must constrain it | ||
65 | if (Prototype.Browser.IE) | ||
66 | { | ||
67 | this.bezel.setStyle({ width: "50%" }); | ||
68 | } | ||
69 | |||
70 | this.bezel.onclick = function() | ||
71 | { | ||
72 | this.hide(); | ||
73 | }.bind(this); | ||
74 | }, | ||
75 | |||
76 | /* | ||
77 | * Displays the notification bezel with the requested message. | ||
78 | */ | ||
79 | show: function(message) | ||
80 | { | ||
81 | // Sets the bezel message | ||
82 | this.message.innerHTML = message; | ||
83 | this.visible = true; | ||
84 | |||
85 | // Center the bezel | ||
86 | var mysize = this.bezel.getDimensions(); | ||
87 | mysize = window.calcCordsToCenter(mysize.height, mysize.width); | ||
88 | |||
89 | // Set the position of the bezel | ||
90 | this.bezel.setStyle( | ||
91 | { | ||
92 | top: mysize.top + "px", | ||
93 | left: mysize.left + "px" | ||
94 | }); | ||
95 | |||
96 | this.bezel.show(); | ||
97 | |||
98 | if (typeof this.options.onShow == "function") | ||
99 | { | ||
100 | this.options.onShow(); | ||
101 | } | ||
102 | |||
103 | if (this.options.displayTime > 0) | ||
104 | { | ||
105 | new PeriodicalExecuter(function(executer) | ||
106 | { | ||
107 | this.hide(); | ||
108 | executer.stop(); | ||
109 | }.bind(this), this.options.displayTime); | ||
110 | } | ||
111 | |||
112 | return this; | ||
113 | }, | ||
114 | |||
115 | /* | ||
116 | * Removes the bezel from the DOM. | ||
117 | */ | ||
118 | destroy: function() | ||
119 | { | ||
120 | try | ||
121 | { | ||
122 | document.body.removeChild(this.bezel); | ||
123 | } | ||
124 | catch (e) | ||
125 | { | ||
126 | // Ignore errors | ||
127 | } | ||
128 | }, | ||
129 | |||
130 | /* | ||
131 | * Fades out the notification bezel. If this is not a sticky bezel | ||
132 | * (displayTime > 0) then this function will be called automatically | ||
133 | * by the show routine. | ||
134 | */ | ||
135 | hide: function() | ||
136 | { | ||
137 | new Effect.Fade(this.bezel, | ||
138 | { | ||
139 | duration: this.options.fadeTime, | ||
140 | |||
141 | afterFinish: function() | ||
142 | { | ||
143 | this.visible = false; | ||
144 | |||
145 | if (this.options.destroy) | ||
146 | { | ||
147 | this.destroy(); | ||
148 | } | ||
149 | |||
150 | this.options.afterHide(this); | ||
151 | }.bind(this) | ||
152 | }); | ||
153 | } | ||
154 | }); \ No newline at end of file | ||
diff --git a/docroot/classes/card.class.js b/docroot/classes/card.class.js new file mode 100755 index 0000000..e135259 --- /dev/null +++ b/docroot/classes/card.class.js | |||
@@ -0,0 +1,378 @@ | |||
1 | /* | ||
2 | * Material Experience - Card Class | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | */ | ||
8 | |||
9 | var Card = Class.create(); | ||
10 | Object.extend(Card.prototype, | ||
11 | { | ||
12 | /* | ||
13 | * Creates the card and appends it to the document. This does | ||
14 | * not set the content. | ||
15 | */ | ||
16 | initialize: function() | ||
17 | { | ||
18 | this.options = Object.extend( | ||
19 | { | ||
20 | title: 'New Card', // Card Title | ||
21 | id: 'mycard', // Card ID | ||
22 | preview: false, // This is a preview card | ||
23 | addExtraButtons: true, // Add the extra buttons to non-system cards | ||
24 | contID: null, // Content ID of the card's contents | ||
25 | color: SME.colors.blue, // Card Color | ||
26 | width: SME.sizes.card.width, // Card Width | ||
27 | height: SME.sizes.card.height, // Card Height | ||
28 | onClose: Prototype.emptyFunction, // Card Close Callback | ||
29 | onFadeComplete: Prototype.emptyFunction, // Card Fade Completion Callback | ||
30 | onFadeOutFinish: Prototype.emptyFunction // Card Fade Out Completion Callback | ||
31 | }, arguments[0] || {}); | ||
32 | |||
33 | // Keep track of our card chip | ||
34 | this.chip = this.options.chip; | ||
35 | |||
36 | // Buttons on the card frame | ||
37 | this.buttons = []; | ||
38 | |||
39 | // Create the document overlay but don't yet show it | ||
40 | this.overlay = new Overlay( | ||
41 | { | ||
42 | onClick: function() | ||
43 | { | ||
44 | this.hide(); | ||
45 | }.bind(this) | ||
46 | }); | ||
47 | |||
48 | this._createCard(); | ||
49 | |||
50 | if (this.options.addExtraButtons) | ||
51 | { | ||
52 | this._addExtraButtons(); | ||
53 | } | ||
54 | |||
55 | // Always add a close button | ||
56 | this.addCommandButton(Strings.closeCard,'images/close.gif', function() | ||
57 | { | ||
58 | this.hide(); | ||
59 | }.bind(this)); | ||
60 | |||
61 | this.setTitle(this.options.title); | ||
62 | }, | ||
63 | |||
64 | /* | ||
65 | * Create the card DOM nodes. | ||
66 | */ | ||
67 | _createCard: function() | ||
68 | { | ||
69 | // Get the rounded corner generator and the coordinates of the page center | ||
70 | var Rounder = new RoundedCorners(this.options.color); | ||
71 | var cardPos = window.calcCordsToCenter(this.options.height, this.options.width); | ||
72 | var slicebox = Rounder.get(Rounder.directions.top); | ||
73 | |||
74 | // Intro card will flash across the screen while card builds but | ||
75 | // before fading up if display is not set to none | ||
76 | this.card = Builder.node('div', | ||
77 | { | ||
78 | id: this.options.id, | ||
79 | style: 'display: none;', | ||
80 | className: 'card' | ||
81 | }); | ||
82 | |||
83 | // Create the card header with title and button box | ||
84 | slicebox.appendChild(this.title = Builder.node('span', { className: 'cardTitle' })); | ||
85 | slicebox.appendChild(this.buttonbox = Builder.node('div', { className: 'buttonbox' }, | ||
86 | this.buttonLabel = Builder.node('span', { className: 'commandlabel' }) | ||
87 | )); | ||
88 | |||
89 | // Append the header and the card content area | ||
90 | this.card.appendChild(slicebox); | ||
91 | this.card.appendChild(Builder.node('div', { style: 'background: white; opacity: 100%;' }, | ||
92 | this.cframe = Builder.node('div') | ||
93 | )); | ||
94 | |||
95 | // Append the bottom of the card frame and add the card to the document | ||
96 | this.card.appendChild(Rounder.get(Rounder.directions.bottom)); | ||
97 | document.body.appendChild(this.card); | ||
98 | |||
99 | // Card frame styles | ||
100 | Element.extend(this.cframe).setStyle( | ||
101 | { | ||
102 | width: this.options.width - 11 + 'px', | ||
103 | height: this.options.height - 40 + 'px', | ||
104 | borderLeft: '5px solid ' + this.options.color, | ||
105 | borderRight: '5px solid ' + this.options.color, | ||
106 | borderTop: '10px solid ' + this.options.color, | ||
107 | position: 'relative', | ||
108 | overflow: 'hidden' | ||
109 | }); | ||
110 | |||
111 | // Card styles | ||
112 | Element.extend(this.card).setStyle( | ||
113 | { | ||
114 | height: this.options.height + 'px', | ||
115 | width: this.options.width + 'px', | ||
116 | top: cardPos.top + 'px', | ||
117 | left: cardPos.left + 'px', | ||
118 | zIndex: 9001, | ||
119 | opacity: '100%', | ||
120 | display: 'none', | ||
121 | position: 'absolute' | ||
122 | }); | ||
123 | |||
124 | // Make the card draggable | ||
125 | this.drag = new Draggable(this.card, | ||
126 | { | ||
127 | handle: 'round', | ||
128 | zindex: 9001, | ||
129 | starteffect: null, | ||
130 | endeffect: null | ||
131 | }); | ||
132 | }, | ||
133 | |||
134 | /* | ||
135 | * Add a command button to the top right of the card. | ||
136 | */ | ||
137 | addCommandButton: function(title, icon, action) | ||
138 | { | ||
139 | var newButton = Builder.node('img', { className: 'commandbtn', src: icon }); | ||
140 | |||
141 | // Show the label on mouseover | ||
142 | Event.observe(newButton, 'mouseover', function() | ||
143 | { | ||
144 | this.buttonLabel.innerHTML = title; | ||
145 | }.bindAsEventListener(this)); | ||
146 | |||
147 | // Hide the label on mouse out | ||
148 | Event.observe(newButton, 'mouseout', function() | ||
149 | { | ||
150 | this.buttonLabel.innerHTML = ''; | ||
151 | }.bindAsEventListener(this)); | ||
152 | |||
153 | // Take the action specified when the button is clicked | ||
154 | Event.observe(newButton, 'click', function(event) | ||
155 | { | ||
156 | action(event); | ||
157 | }); | ||
158 | |||
159 | // Add the button to the button box and cache it. | ||
160 | this.buttons[title] = this.buttonbox.appendChild(newButton); | ||
161 | }, | ||
162 | |||
163 | /* | ||
164 | * Removes a command button from the card. | ||
165 | */ | ||
166 | removeCommandButton: function(button) | ||
167 | { | ||
168 | this.buttonLabel.innerHTML = ''; | ||
169 | this.buttonbox.removeChild(this.buttons[button]); | ||
170 | }, | ||
171 | |||
172 | /* | ||
173 | * Sets the title of the card. | ||
174 | */ | ||
175 | setTitle: function(title) | ||
176 | { | ||
177 | this.options.title = title; | ||
178 | this.title.innerHTML = title; | ||
179 | }, | ||
180 | |||
181 | /* | ||
182 | * Disables the drag on the card and removes the card | ||
183 | * from the DOM. | ||
184 | */ | ||
185 | destroy: function() | ||
186 | { | ||
187 | // Kill the drag to prevent probable memory leaks in IE | ||
188 | this.drag.destroy(); | ||
189 | |||
190 | // Remove the card from the DOM | ||
191 | document.body.removeChild(this.card); | ||
192 | |||
193 | // Destroy the overlay | ||
194 | this.overlay.destroy(); | ||
195 | }, | ||
196 | |||
197 | /* | ||
198 | * Display the card. | ||
199 | */ | ||
200 | show: function() | ||
201 | { | ||
202 | // Track the currently active card for the history manager | ||
203 | // FLAWED LOGIC: We can have multiple cards visible at the same | ||
204 | // time. | ||
205 | SME.currentCard = this; | ||
206 | |||
207 | // Get card positioning data | ||
208 | var cardPos = window.calcCordsToCenter(this.options.height, this.options.width); | ||
209 | var windSize = window.getDimensions(); | ||
210 | |||
211 | // Position the card | ||
212 | this.card.setStyle( | ||
213 | { | ||
214 | top: cardPos.top + 'px', | ||
215 | left: cardPos.left + 'px' | ||
216 | }); | ||
217 | |||
218 | this._autoSetLayout(); | ||
219 | |||
220 | // Show the overlay first so it will fade in with the card | ||
221 | this.overlay.show(); | ||
222 | |||
223 | // Fade the card in and run its fadeComplete function. | ||
224 | new Effect.Appear(this.card, | ||
225 | { | ||
226 | afterFinish: function() | ||
227 | { | ||
228 | this.options.onFadeComplete(); | ||
229 | }.bind(this) | ||
230 | }); | ||
231 | }, | ||
232 | |||
233 | /* | ||
234 | * Automatically set the layout based on the content of a JSON feed | ||
235 | * if it is supplied. | ||
236 | */ | ||
237 | _autoSetLayout: function() | ||
238 | { | ||
239 | // Fetch data only if this is not an intro card and a content | ||
240 | // id has been specified. | ||
241 | if (!this.options.contID) | ||
242 | { | ||
243 | return; | ||
244 | } | ||
245 | |||
246 | // Pull the correct data if this is a preview or a real | ||
247 | // card. | ||
248 | if (this.options.preview) | ||
249 | { | ||
250 | var theUrl = SME.url.cardPreview.evaluate({card: this.options.contID}) | ||
251 | } | ||
252 | else | ||
253 | { | ||
254 | var theUrl = SME.url.cards.evaluate({card: this.options.contID}); | ||
255 | } | ||
256 | |||
257 | new Ajax.Request(theUrl, | ||
258 | { | ||
259 | method: 'get', | ||
260 | |||
261 | onSuccess: function(transport) | ||
262 | { | ||
263 | try | ||
264 | { | ||
265 | var data = transport.responseText.cleanJSON().evalJSON(); | ||
266 | this.setLayout(SME.engineMapping[data.template], data); | ||
267 | } | ||
268 | catch (exception) | ||
269 | { | ||
270 | if (SME.debug) | ||
271 | { | ||
272 | console.error(exception); | ||
273 | } | ||
274 | |||
275 | this.setLayout(Card.Layout.Errors, ''); | ||
276 | } | ||
277 | }.bind(this) | ||
278 | }); | ||
279 | }, | ||
280 | |||
281 | /* | ||
282 | * Add extra buttons to a card. I moved this to its own private | ||
283 | * function because some cards, like system cards, don't need all | ||
284 | * those extra buttons. | ||
285 | */ | ||
286 | _addExtraButtons: function() | ||
287 | { | ||
288 | this.addCommandButton(Strings.addToSketchbook,'images/plus.gif', function() | ||
289 | { | ||
290 | SME.sketchbook.addChip(this.options.contID); | ||
291 | }.bind(this)); | ||
292 | |||
293 | this.addCommandButton(Strings.sendToFriend,'images/email.gif', function() | ||
294 | { | ||
295 | if ($('cardFlash')) | ||
296 | { | ||
297 | this.flashP = $('cardFlash').up(); | ||
298 | this.flash = $('cardFlash').remove(); | ||
299 | } | ||
300 | |||
301 | var card = new Card( | ||
302 | { | ||
303 | color: SME.colors.grey, | ||
304 | title: Strings.sendToFriend, | ||
305 | addExtraButtons: false, | ||
306 | |||
307 | onFadeComplete: function() | ||
308 | { | ||
309 | if (this.flash) | ||
310 | { | ||
311 | this.flashP.appendChild(this.flash); | ||
312 | this.flash = null; | ||
313 | } | ||
314 | }.bind(this) | ||
315 | }); | ||
316 | |||
317 | card.setLayout(Card.Layout.Special, | ||
318 | { | ||
319 | url: SME.url.sendToFriend.evaluate( | ||
320 | { | ||
321 | durl: 'card' + encodeURI(this.options.contID), | ||
322 | title: encodeURI(this.options.title) | ||
323 | }) | ||
324 | }); | ||
325 | |||
326 | card.show(); | ||
327 | }.bind(this)); | ||
328 | |||
329 | this.addCommandButton(Strings.printCard, 'images/print.gif', function() | ||
330 | { | ||
331 | this.layoutEngine.print(); | ||
332 | }.bind(this)); | ||
333 | |||
334 | // Card history ON the card was poorly designed and implemented | ||
335 | // (yeah, I know) so I'm just removing it for now till I get | ||
336 | // some time to re-write it. | ||
337 | // | ||
338 | // TODO: Re-write this | ||
339 | this.addCommandButton(Strings.myHistory,'images/history.gif', function() | ||
340 | { | ||
341 | var center = window.calcCordsToCenter(SME.sizes.card.height, SME.sizes.card.width); | ||
342 | SME.history.getDropDown(center.left + 30, center.top + 13); | ||
343 | }); | ||
344 | }, | ||
345 | |||
346 | /* | ||
347 | * Hides the current card but does not remove it from the DOM. | ||
348 | */ | ||
349 | hide: function() | ||
350 | { | ||
351 | // There is no current card so unset it | ||
352 | // FLAWED LOGIC: We can have multiple cards. | ||
353 | SME.currentCard = null; | ||
354 | |||
355 | // Fade out and subsequently destroy the card | ||
356 | new Effect.Fade(this.card, | ||
357 | { | ||
358 | afterFinish: function() | ||
359 | { | ||
360 | this.options.onFadeOutFinish(); | ||
361 | this.destroy(); | ||
362 | }.bind(this) | ||
363 | }); | ||
364 | |||
365 | // FLAWED LOGIC: Multiple cards, see above. | ||
366 | SME.history.clearHash(); | ||
367 | this.overlay.hide(); | ||
368 | this.options.onClose(); | ||
369 | }, | ||
370 | |||
371 | /* | ||
372 | * Sets the layout of the card using a layout engine. | ||
373 | */ | ||
374 | setLayout: function(layoutE, data) | ||
375 | { | ||
376 | this.layoutEngine = new layoutE(this.cframe, data, this).layout(); | ||
377 | } | ||
378 | }); | ||
diff --git a/docroot/classes/chip.class.js b/docroot/classes/chip.class.js new file mode 100755 index 0000000..61b7bdb --- /dev/null +++ b/docroot/classes/chip.class.js | |||
@@ -0,0 +1,269 @@ | |||
1 | /* | ||
2 | * Material Experience - Card Chip Class | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | */ | ||
8 | |||
9 | var CardChip = Class.create(); | ||
10 | Object.extend(CardChip.prototype, | ||
11 | { | ||
12 | /* | ||
13 | * Initializes the chip class. | ||
14 | */ | ||
15 | initialize: function() | ||
16 | { | ||
17 | this.options = Object.extend( | ||
18 | { | ||
19 | x: 0, // X coordinate of the chip | ||
20 | y: 0, // Y coordinate of the chip | ||
21 | locked: false, // Require login to view card | ||
22 | category: null, // Chip target category | ||
23 | contID: null, // Chip target content ID | ||
24 | title: null, // Chip target title | ||
25 | image: null, // Chip image | ||
26 | className: 'chip', // Chip CSS Class Name | ||
27 | animate: true // Animate chip onto table | ||
28 | }, arguments[0] || {}); | ||
29 | |||
30 | this.dragged = false; | ||
31 | |||
32 | this._createChip(); | ||
33 | |||
34 | return this; | ||
35 | }, | ||
36 | |||
37 | /* | ||
38 | * Create the chip DOM nodes and attach events and drags as appropriate. | ||
39 | */ | ||
40 | _createChip: function() | ||
41 | { | ||
42 | this.chip = Builder.node('img', | ||
43 | { | ||
44 | src: this.options.image, | ||
45 | className: this.options.className, | ||
46 | style: 'top: ' + this.options.y + 'px; left: ' + this.options.x + 'px', | ||
47 | title: this.options.title + ((this.options.locked) ? ' (protected)' : ''), | ||
48 | alt: this.options.title + ((this.options.locked) ? ' (protected)' : '') | ||
49 | }); | ||
50 | |||
51 | // Each DOM node should have a link back to this class so we can | ||
52 | // later look at a DOM node which holds no real data about the | ||
53 | // object and come back here to get the data we need. | ||
54 | this.chip.classLink = this; | ||
55 | |||
56 | new Draggable(this.chip, | ||
57 | { | ||
58 | starteffect: null, | ||
59 | endeffect: null, | ||
60 | |||
61 | change: function() | ||
62 | { | ||
63 | // Set the dragged flag (see onClick for more information) | ||
64 | this.dragged = true; | ||
65 | }.bind(this), | ||
66 | |||
67 | onEnd: function(e) | ||
68 | { | ||
69 | // Ensures that cards stay on top of the stack after they are | ||
70 | // dropped. This is accomplished by determining the maximum | ||
71 | // z-index of the cards and applying max + 1 to the current | ||
72 | // chip. | ||
73 | // | ||
74 | // Note that we are modifying a variable used internally by | ||
75 | // Scriptaculous that is NOT part of the public API. The | ||
76 | // originalZ variable is used by Scriptaculous to record the | ||
77 | // z-index that it should return the draggable to, by | ||
78 | // incrementing this we effectively move the card up in the | ||
79 | // stack. | ||
80 | // | ||
81 | // Note that scriptaculous adds a z-index of 1000 when the drag | ||
82 | // starts and does not remove it till AFTER this function executes | ||
83 | // so we have to take off that extra 1000 to get the real z-index. | ||
84 | e.originalZ = $$('div#' + (SME.currentTable || 'home') + ' img.chip').max(function(x) | ||
85 | { | ||
86 | var z = parseInt(x.style.zIndex) || 0; | ||
87 | return (z >= 1000) ? z - 1000 : z; | ||
88 | }) + 1; | ||
89 | |||
90 | // Track the X and Y coordinates of the chip | ||
91 | e.element.classLink.options.x = e.element.style.left.split('px')[0]; | ||
92 | e.element.classLink.options.y = e.element.style.top.split('px')[0]; | ||
93 | } | ||
94 | }); | ||
95 | |||
96 | Event.observe(this.chip, 'click', function() | ||
97 | { | ||
98 | this.onClick(); | ||
99 | }.bindAsEventListener(this)); | ||
100 | |||
101 | if (this.options.animate) | ||
102 | { | ||
103 | this._generateAnimation(); | ||
104 | } | ||
105 | }, | ||
106 | |||
107 | /* | ||
108 | * Handle clicks on the chip. | ||
109 | */ | ||
110 | onClick: function() | ||
111 | { | ||
112 | // If we recently dragged this card then do nothing. This | ||
113 | // facilitates single click DND as well as single click | ||
114 | // activation. | ||
115 | if (this.dragged == true) | ||
116 | { | ||
117 | this.dragged = false; | ||
118 | return; | ||
119 | } | ||
120 | |||
121 | // If this is a locked chip and we aren't logged in then show | ||
122 | // a login screen | ||
123 | if (this.options.locked && !Sketchbook.loggedIn) | ||
124 | { | ||
125 | return Sketchbook.showLoginScreen(); | ||
126 | } | ||
127 | |||
128 | // Disable the puff in IE because the PNG fix breaks it | ||
129 | if (!Prototype.Browser.IE) | ||
130 | { | ||
131 | new Effect.Puff(this.chip, | ||
132 | { | ||
133 | afterFinish: function() | ||
134 | { | ||
135 | new Effect.Appear(this.chip); | ||
136 | }.bind(this) | ||
137 | }); | ||
138 | } | ||
139 | |||
140 | SME.history.registerEvent('card', this.options.title, this.options.contID); | ||
141 | |||
142 | var t = new Card( | ||
143 | { | ||
144 | color: CardTable.tableColor(this.options.category), | ||
145 | contID: this.options.contID, | ||
146 | title: this.options.title, | ||
147 | chip: this | ||
148 | }); | ||
149 | |||
150 | t.show(); | ||
151 | }, | ||
152 | |||
153 | /* | ||
154 | * Actually move the chip onto the table. This function expects the | ||
155 | * original and new coordinates to be pre-generated and stored by | ||
156 | * another function. | ||
157 | */ | ||
158 | animate: function() | ||
159 | { | ||
160 | new Effect.Morph(this.chip, | ||
161 | { | ||
162 | style: | ||
163 | { | ||
164 | top: this.newY + 'px', | ||
165 | left: this.newX + 'px' | ||
166 | } | ||
167 | }); | ||
168 | }, | ||
169 | |||
170 | /* | ||
171 | * Return data about the chip in an object that matches of the format | ||
172 | * of the card table JSON feed. | ||
173 | */ | ||
174 | _serialize: function() | ||
175 | { | ||
176 | return { | ||
177 | cid : this.options.contID, | ||
178 | title : this.options.title, | ||
179 | category : this.options.category, | ||
180 | locked : this.options.locked, | ||
181 | x : this.options.x, | ||
182 | y : this.options.y, | ||
183 | chip : this.options.image | ||
184 | }; | ||
185 | }, | ||
186 | |||
187 | /* | ||
188 | * Generate random coordinates for and place the chip off the edge of | ||
189 | * the table to be animated in later on by a different function. | ||
190 | */ | ||
191 | _generateAnimation: function() | ||
192 | { | ||
193 | var myRand = Math.floor(1 + (5 - 1) * Math.random()); | ||
194 | var windSize = window.getDimensions(); | ||
195 | |||
196 | this.newX = this.options.x; | ||
197 | this.newY = this.options.y; | ||
198 | |||
199 | this.chip.setStyle({ position: 'absolute' }); | ||
200 | |||
201 | // Determine from which direction the cards will animate | ||
202 | switch (myRand) | ||
203 | { | ||
204 | case 1: // Top | ||
205 | this.chip.setStyle( | ||
206 | { | ||
207 | top: -(SME.sizes.chipMax.height) + 'px', | ||
208 | left: (windSize.width / 2) + 'px' | ||
209 | }); | ||
210 | break; | ||
211 | |||
212 | case 2: // Right | ||
213 | this.chip.setStyle( | ||
214 | { | ||
215 | top: (windSize.height / 2) + 'px', | ||
216 | left: (SME.sizes.chipMax.width) + windSize.width + 'px' | ||
217 | }); | ||
218 | break; | ||
219 | |||
220 | case 3: // Bottom | ||
221 | this.chip.setStyle( | ||
222 | { | ||
223 | top: (SME.sizes.chipMax.height) + windSize.height + 'px', | ||
224 | left: (windSize.width / 2) + 'px' | ||
225 | }); | ||
226 | break; | ||
227 | |||
228 | default: // Left | ||
229 | this.chip.setStyle( | ||
230 | { | ||
231 | top: (windSize.height / 2) + 'px', | ||
232 | left: -(SME.sizes.chipMax.width + 40) + 'px' | ||
233 | }); | ||
234 | break; | ||
235 | } | ||
236 | }, | ||
237 | |||
238 | /* | ||
239 | * Add a chip DOM node to a table. | ||
240 | */ | ||
241 | addTo: function(table) | ||
242 | { | ||
243 | table.appendChild(this.chip); | ||
244 | }, | ||
245 | |||
246 | /* | ||
247 | * Remove the chip DOM node from the document. | ||
248 | */ | ||
249 | destroy: function() | ||
250 | { | ||
251 | document.removeChild(this.chip); | ||
252 | }, | ||
253 | |||
254 | /* | ||
255 | * Show the chip. | ||
256 | */ | ||
257 | show: function() | ||
258 | { | ||
259 | this.chip.show(); | ||
260 | }, | ||
261 | |||
262 | /* | ||
263 | * Hide the chip. | ||
264 | */ | ||
265 | hide: function() | ||
266 | { | ||
267 | this.chip.hide(); | ||
268 | } | ||
269 | }); \ No newline at end of file | ||
diff --git a/docroot/classes/cookie.class.js b/docroot/classes/cookie.class.js new file mode 100755 index 0000000..adf28f2 --- /dev/null +++ b/docroot/classes/cookie.class.js | |||
@@ -0,0 +1,70 @@ | |||
1 | /* | ||
2 | * Material Experience - Cookie Handling Class | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) on 10/2/07 | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 10/3/07 | ||
7 | * | ||
8 | * Class to handle cookie CRUD. Code adapted from: | ||
9 | * http://www.quirksmode.org/js/cookies.html | ||
10 | */ | ||
11 | |||
12 | var Cookie = Object.extend(Class.create(), | ||
13 | { | ||
14 | /* | ||
15 | * Creates a cookie. | ||
16 | */ | ||
17 | create: function(name, value, days) | ||
18 | { | ||
19 | if (days) | ||
20 | { | ||
21 | var date = new Date().setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); | ||
22 | var expires = "; expires=" + date.toGMTString(); | ||
23 | } | ||
24 | |||
25 | document.cookie = name + "=" + value + (expires ? expires : '') + "; path=/"; | ||
26 | }, | ||
27 | |||
28 | /* | ||
29 | * Setting the expiration date of the cookie to -1 effectively deletes | ||
30 | * it. | ||
31 | */ | ||
32 | erase: function(name) | ||
33 | { | ||
34 | createCookie(name,"",-1); | ||
35 | }, | ||
36 | |||
37 | /* | ||
38 | * Reads a cookie and returns the value. | ||
39 | */ | ||
40 | read: function(name) | ||
41 | { | ||
42 | var nameEQ = name + "="; | ||
43 | var ca = document.cookie.split(';'); | ||
44 | |||
45 | for ( var i = 0; i < ca.length; i++ ) | ||
46 | { | ||
47 | var c = ca[i]; | ||
48 | |||
49 | while (c.charAt(0) == ' ') | ||
50 | { | ||
51 | c = c.substring(1,c.length); | ||
52 | } | ||
53 | |||
54 | if (c.indexOf(nameEQ) == 0) | ||
55 | { | ||
56 | return c.substring(nameEQ.length,c.length); | ||
57 | } | ||
58 | } | ||
59 | |||
60 | return null; | ||
61 | } | ||
62 | }); | ||
63 | |||
64 | /* | ||
65 | * Shorthand for Cookie.read | ||
66 | */ | ||
67 | function $C(name) | ||
68 | { | ||
69 | return Cookie.read(name); | ||
70 | } \ No newline at end of file | ||
diff --git a/docroot/classes/decoder.module.js b/docroot/classes/decoder.module.js new file mode 100755 index 0000000..ae33059 --- /dev/null +++ b/docroot/classes/decoder.module.js | |||
@@ -0,0 +1,205 @@ | |||
1 | /* | ||
2 | * Material Experience - Encoder/Decoder Module | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
6 | * | ||
7 | * Encoder/Decoder module for common encoding schemes like URL and | ||
8 | * base64. Code from: http://ostermiller.org/calc/encode.html | ||
9 | */ | ||
10 | var END_OF_INPUT = -1; | ||
11 | |||
12 | var base64Chars = new Array( | ||
13 | 'A','B','C','D','E','F','G','H', | ||
14 | 'I','J','K','L','M','N','O','P', | ||
15 | 'Q','R','S','T','U','V','W','X', | ||
16 | 'Y','Z','a','b','c','d','e','f', | ||
17 | 'g','h','i','j','k','l','m','n', | ||
18 | 'o','p','q','r','s','t','u','v', | ||
19 | 'w','x','y','z','0','1','2','3', | ||
20 | '4','5','6','7','8','9','+','/' | ||
21 | ); | ||
22 | |||
23 | var reverseBase64Chars = new Array(); | ||
24 | var base64Str; | ||
25 | var base64Count; | ||
26 | |||
27 | for (var i=0; i < base64Chars.length; i++) | ||
28 | { | ||
29 | reverseBase64Chars[base64Chars[i]] = i; | ||
30 | } | ||
31 | |||
32 | // -------------------------------------------------------------------------- \\ | ||
33 | |||
34 | function urlDecode(str) | ||
35 | { | ||
36 | return unescape(str.replace(new RegExp('\\+','g'),' ')); | ||
37 | } | ||
38 | |||
39 | function urlEncode(str) | ||
40 | { | ||
41 | str = escape(str); | ||
42 | str = str.replace(new RegExp('\\+','g'),'%2B'); | ||
43 | |||
44 | return str.replace(new RegExp('%20','g'),'+'); | ||
45 | } | ||
46 | |||
47 | // -------------------------------------------------------------------------- \\ | ||
48 | |||
49 | function setBase64Str(str) | ||
50 | { | ||
51 | base64Str = str; | ||
52 | base64Count = 0; | ||
53 | } | ||
54 | |||
55 | function readBase64() | ||
56 | { | ||
57 | if (!base64Str) | ||
58 | { | ||
59 | return END_OF_INPUT; | ||
60 | } | ||
61 | |||
62 | if (base64Count >= base64Str.length) | ||
63 | { | ||
64 | return END_OF_INPUT; | ||
65 | } | ||
66 | |||
67 | var c = base64Str.charCodeAt(base64Count) & 0xff; | ||
68 | |||
69 | base64Count++; | ||
70 | |||
71 | return c; | ||
72 | } | ||
73 | |||
74 | function encodeBase64(str) | ||
75 | { | ||
76 | setBase64Str(str); | ||
77 | |||
78 | var result = ''; | ||
79 | var inBuffer = new Array(3); | ||
80 | var lineCount = 0; | ||
81 | var done = false; | ||
82 | |||
83 | while (!done && (inBuffer[0] = readBase64()) != END_OF_INPUT) | ||
84 | { | ||
85 | inBuffer[1] = readBase64(); | ||
86 | inBuffer[2] = readBase64(); | ||
87 | result += (base64Chars[ inBuffer[0] >> 2 ]); | ||
88 | |||
89 | if (inBuffer[1] != END_OF_INPUT) | ||
90 | { | ||
91 | result += (base64Chars [(( inBuffer[0] << 4 ) & 0x30) | (inBuffer[1] >> 4) ]); | ||
92 | |||
93 | if (inBuffer[2] != END_OF_INPUT) | ||
94 | { | ||
95 | result += (base64Chars [((inBuffer[1] << 2) & 0x3c) | (inBuffer[2] >> 6) ]); | ||
96 | result += (base64Chars [inBuffer[2] & 0x3F]); | ||
97 | } | ||
98 | else | ||
99 | { | ||
100 | result += (base64Chars [((inBuffer[1] << 2) & 0x3c)]); | ||
101 | result += ('='); | ||
102 | done = true; | ||
103 | } | ||
104 | } | ||
105 | else | ||
106 | { | ||
107 | result += (base64Chars [(( inBuffer[0] << 4 ) & 0x30)]); | ||
108 | result += ('='); | ||
109 | result += ('='); | ||
110 | done = true; | ||
111 | } | ||
112 | |||
113 | lineCount += 4; | ||
114 | |||
115 | if (lineCount >= 76) | ||
116 | { | ||
117 | result += ('\n'); | ||
118 | lineCount = 0; | ||
119 | } | ||
120 | } | ||
121 | |||
122 | return result; | ||
123 | } | ||
124 | |||
125 | function readReverseBase64() | ||
126 | { | ||
127 | if (!base64Str) | ||
128 | { | ||
129 | return END_OF_INPUT; | ||
130 | } | ||
131 | |||
132 | while (true) | ||
133 | { | ||
134 | if (base64Count >= base64Str.length) | ||
135 | { | ||
136 | return END_OF_INPUT; | ||
137 | } | ||
138 | |||
139 | var nextCharacter = base64Str.charAt(base64Count); | ||
140 | base64Count++; | ||
141 | |||
142 | if (reverseBase64Chars[nextCharacter]) | ||
143 | { | ||
144 | return reverseBase64Chars[nextCharacter]; | ||
145 | } | ||
146 | |||
147 | if (nextCharacter == 'A') | ||
148 | { | ||
149 | return 0; | ||
150 | } | ||
151 | } | ||
152 | |||
153 | return END_OF_INPUT; | ||
154 | } | ||
155 | |||
156 | function ntos(n) | ||
157 | { | ||
158 | n = n.toString(16); | ||
159 | |||
160 | if (n.length == 1) | ||
161 | { | ||
162 | n = "0" + n; | ||
163 | } | ||
164 | |||
165 | n = "%" + n; | ||
166 | |||
167 | return unescape(n); | ||
168 | } | ||
169 | |||
170 | function decodeBase64(str) | ||
171 | { | ||
172 | setBase64Str(str); | ||
173 | |||
174 | var result = ""; | ||
175 | var inBuffer = new Array(4); | ||
176 | var done = false; | ||
177 | |||
178 | while (!done && (inBuffer[0] = readReverseBase64()) != END_OF_INPUT | ||
179 | && (inBuffer[1] = readReverseBase64()) != END_OF_INPUT) | ||
180 | { | ||
181 | inBuffer[2] = readReverseBase64(); | ||
182 | inBuffer[3] = readReverseBase64(); | ||
183 | result += ntos((((inBuffer[0] << 2) & 0xff)| inBuffer[1] >> 4)); | ||
184 | |||
185 | if (inBuffer[2] != END_OF_INPUT) | ||
186 | { | ||
187 | result += ntos((((inBuffer[1] << 4) & 0xff)| inBuffer[2] >> 2)); | ||
188 | |||
189 | if (inBuffer[3] != END_OF_INPUT) | ||
190 | { | ||
191 | result += ntos((((inBuffer[2] << 6) & 0xff) | inBuffer[3])); | ||
192 | } | ||
193 | else | ||
194 | { | ||
195 | done = true; | ||
196 | } | ||
197 | } | ||
198 | else | ||
199 | { | ||
200 | done = true; | ||
201 | } | ||
202 | } | ||
203 | |||
204 | return result; | ||
205 | } \ No newline at end of file | ||
diff --git a/docroot/classes/history.class.js b/docroot/classes/history.class.js new file mode 100755 index 0000000..12005c3 --- /dev/null +++ b/docroot/classes/history.class.js | |||
@@ -0,0 +1,208 @@ | |||
1 | /* | ||
2 | * Material Experience - History Manager Class | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | */ | ||
8 | |||
9 | var HistoryManager = Class.create(); | ||
10 | Object.extend(HistoryManager.prototype, | ||
11 | { | ||
12 | /* | ||
13 | * Sets up the history storage array and initializes the last | ||
14 | * event to nothing. | ||
15 | */ | ||
16 | initialize: function() | ||
17 | { | ||
18 | this.historyStore = $H(); | ||
19 | this.lastEvent = null; | ||
20 | }, | ||
21 | |||
22 | /* | ||
23 | * Clears the window hash. Generally called when a card closes. | ||
24 | */ | ||
25 | clearHash: function() | ||
26 | { | ||
27 | window.location.hash = '#'; | ||
28 | }, | ||
29 | |||
30 | /* | ||
31 | * Register an event with the history manager. Also updates the | ||
32 | * window hash to reflect the event. This is the only way to | ||
33 | * register an event with the history manager. | ||
34 | */ | ||
35 | registerEvent: function(type, title, url) | ||
36 | { | ||
37 | window.location.hash = '#' + type + url; | ||
38 | |||
39 | // If you don't set this explicitly IE will show the hash as | ||
40 | // the document title. | ||
41 | document.title = Strings.appTitle; | ||
42 | |||
43 | this.lastEvent = '#' + type + url; | ||
44 | this.historyStore[type + url] = title; | ||
45 | |||
46 | return this; | ||
47 | }, | ||
48 | |||
49 | /* | ||
50 | * Stops polling the URL for new events. | ||
51 | */ | ||
52 | stopPolling: function() | ||
53 | { | ||
54 | this.poller.stop(); | ||
55 | |||
56 | return this; | ||
57 | }, | ||
58 | |||
59 | /* | ||
60 | * Starts polling the URL for new events and will execute the event | ||
61 | * handler if an event occurs. | ||
62 | */ | ||
63 | pollEvents: function() | ||
64 | { | ||
65 | this.poller = new PeriodicalExecuter(function() | ||
66 | { | ||
67 | // Make sure there is a valid hash and it is NOT the card | ||
68 | // that we just loaded (prevent double-carding) | ||
69 | if ( | ||
70 | this.lastEvent != window.location.hash && | ||
71 | window.location.hash != '#' && | ||
72 | window.location.hash != '' | ||
73 | ) { | ||
74 | this.lastEvent = window.location.hash; | ||
75 | this._handleEvents(); | ||
76 | } | ||
77 | }.bind(this), 0.1); | ||
78 | |||
79 | return this; | ||
80 | }, | ||
81 | |||
82 | /* | ||
83 | * Handles an event, this is called internally by the pollEvents function | ||
84 | * when a new event occurs and should never be called directly. | ||
85 | */ | ||
86 | _handleEvents: function() | ||
87 | { | ||
88 | // Breaks down the event type and parameters from the hash | ||
89 | var evtTypes = /^(card|table|preview)/; | ||
90 | var hash = window.location.hash; | ||
91 | |||
92 | // When IE does the split it only returns one array element, | ||
93 | // the parameter, so we have to do a little magic to match | ||
94 | // the action too. All other browsers seem to work correctly. | ||
95 | var fullEvt = hash.substr(1, hash.length).split(evtTypes).without(''); | ||
96 | var eventType = (fullEvt.length == 1) ? hash.substr(1, hash.length).match(evtTypes)[0] : fullEvt[0]; | ||
97 | var eventParam = (fullEvt.length == 1) ? fullEvt : fullEvt[1]; | ||
98 | |||
99 | // Hide the current card if there is one | ||
100 | if (SME.currentCard && SME.currentCard.hide) | ||
101 | { | ||
102 | SME.currentCard.hide(); | ||
103 | } | ||
104 | |||
105 | switch (eventType) | ||
106 | { | ||
107 | case 'table': | ||
108 | CardTable.showTable(eventParam); | ||
109 | return; | ||
110 | break; | ||
111 | |||
112 | case 'card': | ||
113 | theURL = SME.url.cards.evaluate({card: eventParam}); | ||
114 | break; | ||
115 | |||
116 | case 'preview': | ||
117 | theURL = SME.url.cardPreview.evaluate({card: eventParam}); | ||
118 | this.stopPolling(); | ||
119 | break; | ||
120 | |||
121 | default: | ||
122 | return; | ||
123 | break; | ||
124 | } | ||
125 | |||
126 | new Ajax.Request(theURL, | ||
127 | { | ||
128 | method: 'get', | ||
129 | |||
130 | onSuccess: function(transport) | ||
131 | { | ||
132 | var data = transport.responseText.cleanJSON().evalJSON(); | ||
133 | |||
134 | var card = new Card( | ||
135 | { | ||
136 | color: CardTable.tableColor(data.type), | ||
137 | contID: eventParam, | ||
138 | title: data.title, | ||
139 | preview: (eventType == 'preview') ? true : false | ||
140 | }); | ||
141 | |||
142 | this.registerEvent(eventType, data.title, eventParam); | ||
143 | card.show(); | ||
144 | }.bind(this) | ||
145 | }); | ||
146 | }, | ||
147 | |||
148 | |||
149 | /* | ||
150 | * Creates and populates the history dropdown based on the | ||
151 | * events stored in the history storage array. Also handles | ||
152 | * showing, hiding and positioning that menu. | ||
153 | */ | ||
154 | getDropDown: function(x, y) | ||
155 | { | ||
156 | var dropdown = $('history'); | ||
157 | |||
158 | // If no x and y then position the menu for the my history | ||
159 | // dropdown from the tools menu. | ||
160 | x = x ? x : 20; | ||
161 | y = y ? y : 26; | ||
162 | |||
163 | // Remove and re-append the menu from the DOM, this prevents | ||
164 | // IE z-index bugs. | ||
165 | document.body.appendChild(dropdown.remove()); | ||
166 | dropdown.innerHTML = ''; | ||
167 | |||
168 | // Populate the menu | ||
169 | this.historyStore.each(function(item) | ||
170 | { | ||
171 | dropdown.appendChild(Builder.node('li', {}, Builder.node('a', { href: '#' + item.key }, item.value))); | ||
172 | }); | ||
173 | |||
174 | // Initially show the menu | ||
175 | dropdown.addClassName('hovered'); | ||
176 | |||
177 | // Must explicitly set left to nothing otherwise positioning | ||
178 | // is wrong. | ||
179 | dropdown.setStyle( | ||
180 | { | ||
181 | right: x + 'px', | ||
182 | top: y + 'px', | ||
183 | left: '', | ||
184 | zIndex: 9999 | ||
185 | }); | ||
186 | |||
187 | // Hide the menu when the user... | ||
188 | $H({ | ||
189 | '#history a' : 'click', // Clicks on a link | ||
190 | 'div.table' : 'mouseover', // Mouses-off onto a table | ||
191 | 'p' : 'mouseover', // Mouses-off onto a paragraph | ||
192 | 'iframe' : 'mouseover', // Mouses-off onto an iframe | ||
193 | 'img' : 'mouseover' // Mouses-off onto an image | ||
194 | }).each(function(aitem) | ||
195 | { | ||
196 | $$(aitem.key).each(function(item) | ||
197 | { | ||
198 | item.observe(aitem.value, function() | ||
199 | { | ||
200 | if ($('history').hasClassName('hovered')) | ||
201 | { | ||
202 | $('history').removeClassName('hovered'); | ||
203 | } | ||
204 | }); | ||
205 | }); | ||
206 | }); | ||
207 | } | ||
208 | }); \ No newline at end of file | ||
diff --git a/docroot/classes/layout.class.js b/docroot/classes/layout.class.js new file mode 100755 index 0000000..a088fbf --- /dev/null +++ b/docroot/classes/layout.class.js | |||
@@ -0,0 +1,51 @@ | |||
1 | /* | ||
2 | * Material Experience - Card Layout Engines Class | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | */ | ||
8 | |||
9 | // Card.Layout.Errors = Class.create(); | ||
10 | // Object.extend(Object.extend(Card.Layout.Errors.prototype, Card.Layout.prototype), | ||
11 | |||
12 | Card.Layout = Class.create(); | ||
13 | Object.extend(Card.Layout.prototype, | ||
14 | { | ||
15 | /* | ||
16 | * Initialize the layout class | ||
17 | */ | ||
18 | initialize: function(cframe, data, card) | ||
19 | { | ||
20 | this.cframe = cframe; | ||
21 | this.data = data; | ||
22 | this.card = card; | ||
23 | this.color = this.card.options.color; | ||
24 | this.hasResources = false; | ||
25 | }, | ||
26 | |||
27 | /* | ||
28 | * Main function to layout the card. | ||
29 | */ | ||
30 | layout: function() | ||
31 | { | ||
32 | throw "Not implemented here."; | ||
33 | }, | ||
34 | |||
35 | /* | ||
36 | * Throw the card into a popup window for printing. | ||
37 | */ | ||
38 | print: function() | ||
39 | { | ||
40 | throw "Not implemented here."; | ||
41 | }, | ||
42 | |||
43 | /* | ||
44 | * Debug the card layout. Generally this should be used to spot out | ||
45 | * data issues. But it could also be used for other debugging purposes. | ||
46 | */ | ||
47 | _debug: function() | ||
48 | { | ||
49 | throw "Not implemented here."; | ||
50 | } | ||
51 | }); \ No newline at end of file | ||
diff --git a/docroot/classes/layouts/layout.custom.class.js b/docroot/classes/layouts/layout.custom.class.js new file mode 100755 index 0000000..d4aa463 --- /dev/null +++ b/docroot/classes/layouts/layout.custom.class.js | |||
@@ -0,0 +1,219 @@ | |||
1 | /* | ||
2 | * Material Experience - Card Layout Engines Class | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | */ | ||
8 | |||
9 | Card.Layout.Custom = Class.create(); | ||
10 | Object.extend(Object.extend(Card.Layout.Custom.prototype, Card.Layout.prototype), | ||
11 | { | ||
12 | /* | ||
13 | * Initialize the layout class | ||
14 | */ | ||
15 | initialize: function(cframe, data, card) | ||
16 | { | ||
17 | this.cframe = cframe; | ||
18 | this.data = data; | ||
19 | this.card = card; | ||
20 | this.color = this.card.options.color; | ||
21 | this.hasResources = false; | ||
22 | this.inited = false; | ||
23 | }, | ||
24 | |||
25 | /* | ||
26 | * Create custom scroll bars for an HTML element. | ||
27 | */ | ||
28 | scrollify: function(div, isiframe) | ||
29 | { | ||
30 | var div2 = null; | ||
31 | |||
32 | // Inherit the color from a link | ||
33 | var color = $$('ul#navigation li a')[0].getStyle('background-color'); | ||
34 | |||
35 | if (isiframe) | ||
36 | { | ||
37 | div2 = div.contentWindow.document.documentElement; | ||
38 | } | ||
39 | else | ||
40 | { | ||
41 | div2 = div; | ||
42 | } | ||
43 | |||
44 | // Create the scroll container and draggable widget | ||
45 | this.cframe.appendChild( | ||
46 | this.scrollCont = Builder.node('div', | ||
47 | { | ||
48 | style: 'border-left: 1px solid ' + color + ';' + | ||
49 | 'width: 1px; position: absolute; ' + | ||
50 | 'height:' + div.offsetHeight + 'px; ' + | ||
51 | 'top: 10px;' + 'left: ' + (div.offsetLeft + div.offsetWidth + 12) + 'px;' | ||
52 | }, | ||
53 | |||
54 | this.scrollBar = Builder.node('img', | ||
55 | { | ||
56 | src: 'http://materialexperience.santoprene.com/images/pill.gif', | ||
57 | style: 'display: block; margin-left: -3px; cursor: move; ' + | ||
58 | 'background: ' + color + '; padding: 0px;' | ||
59 | }) | ||
60 | )); | ||
61 | |||
62 | // Create the scroller | ||
63 | this.scroller = new Control.Slider(this.scrollBar, this.scrollCont, | ||
64 | { | ||
65 | axis: 'vertical', | ||
66 | range: $R(0, div2.scrollHeight), | ||
67 | |||
68 | onSlide: function(value) | ||
69 | { | ||
70 | div2.scrollTop = Math.floor(value); | ||
71 | }.bind(this) | ||
72 | }); | ||
73 | |||
74 | new PeriodicalExecuter(function() | ||
75 | { | ||
76 | // Hide the scroller if there isn't a need for it | ||
77 | if (div2.scrollHeight > 480) | ||
78 | { | ||
79 | this.scrollCont.show(); | ||
80 | } | ||
81 | else | ||
82 | { | ||
83 | this.scrollCont.hide(); | ||
84 | return; | ||
85 | } | ||
86 | |||
87 | // Update the scroller range when the contents change | ||
88 | this.scroller.range = $R(0, div2.scrollHeight); | ||
89 | |||
90 | // Move the scroller when the scrolled element changes | ||
91 | // (e.g. linking down into the page). | ||
92 | if (this.scroller.value != div2.scrollTop) | ||
93 | { | ||
94 | this.scroller.setValue(div2.scrollTop); | ||
95 | } | ||
96 | }.bind(this), 1); | ||
97 | }, | ||
98 | |||
99 | initIframe: function(url) | ||
100 | { | ||
101 | $('wideCol').innerHTML = '<iframe ' + | ||
102 | 'src = "' + url + '" ' + | ||
103 | 'frameborder = "0" ' + | ||
104 | 'scrolling = "auto" ' + | ||
105 | 'width = "660" ' + | ||
106 | 'height = "460" ' + | ||
107 | 'id = "wide_content" ' + | ||
108 | 'name = "wide_content" ' + | ||
109 | '></iframe>'; | ||
110 | }, | ||
111 | |||
112 | /* | ||
113 | * Main function to layout the card. | ||
114 | */ | ||
115 | layout: function() | ||
116 | { | ||
117 | var htmlstr = '<div id="narrowCol" style="float: left; margin: 10px 0px 0px 20px; width: 200px; height: 460px; overflow: hidden;"><ul id="navigation">'; | ||
118 | |||
119 | this.data.contentNarrow.each(function(item) | ||
120 | { | ||
121 | htmlstr += '<li><a target="wide_content" href="' + item.url + '">' + item.title + '</a>'; | ||
122 | |||
123 | if (item.subitems) | ||
124 | { | ||
125 | var parentUrl = item.url; | ||
126 | |||
127 | htmlstr += '<ul>'; | ||
128 | |||
129 | item.subitems.each(function(subitem) | ||
130 | { | ||
131 | htmlstr += '<li><a target="wide_content" href="' + parentUrl + '#' + subitem.url + '">' + subitem.title + '</a></li>'; | ||
132 | }.bind(this)); | ||
133 | |||
134 | htmlstr += '</ul></li>'; | ||
135 | } | ||
136 | else | ||
137 | { | ||
138 | htmlstr += '</li>'; | ||
139 | } | ||
140 | }.bind(this)); | ||
141 | |||
142 | htmlstr += '</ul></div><div id="wideCol" style="width: 660px; height: 460px; float: right; margin: 10px 10px 0px 0px;"></div>'; | ||
143 | this.cframe.innerHTML = htmlstr; | ||
144 | |||
145 | if (this.data.intro) | ||
146 | { | ||
147 | $('wideCol').innerHTML = this.data.intro; | ||
148 | } | ||
149 | |||
150 | // this.scrollify($('wide_content'), true); | ||
151 | this.scrollify($('narrowCol')); | ||
152 | |||
153 | // Hide everything first | ||
154 | $$('ul#navigation li ul').each(function(item) | ||
155 | { | ||
156 | item.setStyle({ display: 'none' }); | ||
157 | }); | ||
158 | |||
159 | var curr = null; | ||
160 | // Then attach the onclick events | ||
161 | $$('ul#navigation li').each(function(item) | ||
162 | { | ||
163 | Event.observe(item, 'click', function(event) | ||
164 | { | ||
165 | // Hide the currently opened item if it is clicked again | ||
166 | // per BS. | ||
167 | if (curr && item == curr) | ||
168 | { | ||
169 | curr.down('ul').hide(); | ||
170 | item.setStyle({ borderLeft: '0px' }); | ||
171 | curr = null; | ||
172 | return; | ||
173 | } | ||
174 | |||
175 | curr = item; | ||
176 | |||
177 | // Hide everything | ||
178 | $$('ul#navigation li ul').each(function(item) | ||
179 | { | ||
180 | item.setStyle({ display: 'none' }); | ||
181 | }); | ||
182 | |||
183 | // Clear out the borders | ||
184 | $$('ul#navigation li').each(function(item) | ||
185 | { | ||
186 | item.setStyle({ borderLeft: '' }); | ||
187 | }); | ||
188 | |||
189 | // Figure out what color we SHOULD be, this will change | ||
190 | // depending on the card color we are on so just rely | ||
191 | // on the designer to communicate through the CSS, | ||
192 | // probably not wise relying on a designer for code | ||
193 | // but alas... | ||
194 | var color = $$('ul#navigation li a')[0].getStyle('background-color'); | ||
195 | |||
196 | this.initIframe(item.href); | ||
197 | |||
198 | // Catch and execute or just leave it lie | ||
199 | try | ||
200 | { | ||
201 | if (item.down('ul').style.display != 'none') | ||
202 | { | ||
203 | item.down('ul').hide(); | ||
204 | Event.stop(event); | ||
205 | return; | ||
206 | } | ||
207 | |||
208 | item.down('ul').show(); | ||
209 | item.setStyle({ borderLeft: '3px solid ' + color }); | ||
210 | //Event.stop(event); | ||
211 | } | ||
212 | catch (e) | ||
213 | { | ||
214 | // Just let the link do its thing | ||
215 | } | ||
216 | }.bind(this)); | ||
217 | }.bind(this)); | ||
218 | } | ||
219 | }); \ No newline at end of file | ||
diff --git a/docroot/classes/layouts/layout.error.class.js b/docroot/classes/layouts/layout.error.class.js new file mode 100755 index 0000000..2fedfcf --- /dev/null +++ b/docroot/classes/layouts/layout.error.class.js | |||
@@ -0,0 +1,33 @@ | |||
1 | /* | ||
2 | * Material Experience - Error Card Layout Engine | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | */ | ||
8 | |||
9 | Card.Layout.Errors = Class.create(); | ||
10 | Object.extend(Object.extend(Card.Layout.Errors.prototype, Card.Layout.prototype), | ||
11 | { | ||
12 | /* | ||
13 | * Initialize the layout class | ||
14 | */ | ||
15 | initialize: function(cframe, data, card) | ||
16 | { | ||
17 | this.cframe = cframe; | ||
18 | this.data = data; | ||
19 | this.card = card; | ||
20 | this.color = this.card.options.color; | ||
21 | this.hasResources = false; | ||
22 | }, | ||
23 | |||
24 | /* | ||
25 | * Main function to layout the card. | ||
26 | */ | ||
27 | layout: function() | ||
28 | { | ||
29 | this.cframe.innerHTML = ''; | ||
30 | this.cframe.appendChild(Builder.node('h1', Strings.cardErrorTitle)); | ||
31 | this.cframe.appendChild(Builder.node('p', Strings.cardErrorText)); | ||
32 | } | ||
33 | }); \ No newline at end of file | ||
diff --git a/docroot/classes/layouts/layout.primary.class.js b/docroot/classes/layouts/layout.primary.class.js new file mode 100755 index 0000000..87bc910 --- /dev/null +++ b/docroot/classes/layouts/layout.primary.class.js | |||
@@ -0,0 +1,411 @@ | |||
1 | /* | ||
2 | * Material Experience - Primary Card Layout Class | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | * | ||
8 | * WARNING: Here be dragons, OK so not as many as before but good luck you poor | ||
9 | * sap. | ||
10 | */ | ||
11 | |||
12 | Card.Layout.Primary = Class.create(); | ||
13 | Object.extend(Object.extend(Card.Layout.Primary.prototype, Card.Layout.prototype), | ||
14 | { | ||
15 | /* | ||
16 | * Initialize the layout class | ||
17 | */ | ||
18 | initialize: function(cframe, data, card) | ||
19 | { | ||
20 | this.cframe = cframe; | ||
21 | this.data = data; | ||
22 | this.card = card; | ||
23 | this.color = this.card.options.color; | ||
24 | this.hasResources = false; | ||
25 | |||
26 | var direction = data.template.replace(/narrow-/,''); | ||
27 | this.direction = direction ? direction : 'left'; | ||
28 | this.oppositeDirection = (direction == 'left') ? 'right' : 'left'; | ||
29 | }, | ||
30 | |||
31 | /* | ||
32 | * Set the column content. This can be flash, an image or an iframe. | ||
33 | */ | ||
34 | setColumn: function(url, direction, contTitle) | ||
35 | { | ||
36 | var content = Builder.node('div', { style: 'float: ' + this.oppositeDirection }); | ||
37 | var myCol = direction + 'Col'; | ||
38 | |||
39 | if (/swf$/.test(url)) // Flash | ||
40 | { | ||
41 | content.innerHTML = new SWFObject(url, 'cardFlash', 580, 479, 9, '#FFFFFF').getSWFHTML(); | ||
42 | } | ||
43 | else if (/(png|gif|jpg|jpeg)$/.test(url)) // Image | ||
44 | { | ||
45 | content = Builder.node('img', | ||
46 | { | ||
47 | src: url, | ||
48 | alt: contTitle, | ||
49 | title: contTitle, | ||
50 | style: 'float: ' + this.oppositeDirection | ||
51 | }); | ||
52 | } | ||
53 | else // iFrame | ||
54 | { | ||
55 | // Can't use DOM methods because IE is allergic | ||
56 | content.innerHTML = '<iframe ' + | ||
57 | 'src = "' + url + '" ' + | ||
58 | 'frameborder = "0" ' + | ||
59 | 'scrolling = "auto" ' + | ||
60 | 'width = "580" ' + | ||
61 | 'height = "480" ' + | ||
62 | 'id = "wide_content" ' + | ||
63 | 'name = "wide_content" ' + | ||
64 | '></iframe>'; | ||
65 | } | ||
66 | |||
67 | // We need to replace the content of the column if it exists to | ||
68 | // facilitate changing content later on (i.e. thumbnails) | ||
69 | if (typeof this[myCol] != 'undefined') | ||
70 | { | ||
71 | this.cframe.replaceChild(content, this[myCol]); | ||
72 | this[myCol] = content; | ||
73 | } | ||
74 | else | ||
75 | { | ||
76 | this[myCol] = this.cframe.appendChild(content); | ||
77 | } | ||
78 | |||
79 | }, | ||
80 | |||
81 | /* | ||
82 | * Build the resource (more information) list. | ||
83 | */ | ||
84 | getResourceList: function() | ||
85 | { | ||
86 | // Only run if we need to | ||
87 | if (!this.data.resources) | ||
88 | { | ||
89 | return; | ||
90 | } | ||
91 | |||
92 | var numResources = 0; | ||
93 | var iter = 0; | ||
94 | var limit = 5; | ||
95 | this.hasResources = true; | ||
96 | |||
97 | this.resourceContainer = this.contentArea.appendChild( | ||
98 | Builder.node('div', | ||
99 | [ | ||
100 | Builder.node('h2', { style: 'color: ' + this.card.options.color }, Strings.moreInfo), | ||
101 | this.resources = Builder.node('ul', { className: 'resourceList' }) | ||
102 | ] | ||
103 | )); | ||
104 | |||
105 | this.data.resources.each(function(item) | ||
106 | { | ||
107 | // Limit the number of allowable resources | ||
108 | if (typeof item == 'undefined' || ++iter > limit) | ||
109 | { | ||
110 | return; | ||
111 | } | ||
112 | |||
113 | numResources++; | ||
114 | |||
115 | this.resources.appendChild(Builder.node('li', | ||
116 | Builder.node('a', | ||
117 | { | ||
118 | href: item.link + '?entrypoint=DESIGNER', | ||
119 | target: '_new', | ||
120 | style: 'color: ' + this.color | ||
121 | }, item.title) | ||
122 | )); | ||
123 | }.bind(this)); | ||
124 | }, | ||
125 | |||
126 | /* | ||
127 | * Set the contents of the narrow column. | ||
128 | */ | ||
129 | setHTML: function(content) | ||
130 | { | ||
131 | this.htmlDiv.innerHTML = content; | ||
132 | }, | ||
133 | |||
134 | /* | ||
135 | * Setup the thumbnails. | ||
136 | */ | ||
137 | getThumbnails: function() | ||
138 | { | ||
139 | // Only run if we need to | ||
140 | if (this.data.media.size() <= 1) | ||
141 | { | ||
142 | return; | ||
143 | } | ||
144 | |||
145 | var iter = 0; | ||
146 | var limit = 5; // Includes main media file | ||
147 | var lData = this.data.media; | ||
148 | |||
149 | this.thumbStrip = this.contentArea.appendChild(Builder.node('div', { className: 'thumbStrip' })); | ||
150 | |||
151 | // Add the main media file to the data array | ||
152 | lData.push( | ||
153 | { | ||
154 | thumb: this.data.contentWide[0].thumb, | ||
155 | url: this.data.contentWide[0].url | ||
156 | }); | ||
157 | |||
158 | this.data.media.each(function(item) | ||
159 | { | ||
160 | // Limit the number of thumbnails | ||
161 | if (typeof item == 'undefined' || ++iter > limit) | ||
162 | { | ||
163 | return; | ||
164 | } | ||
165 | |||
166 | var mediaPiece = this.thumbStrip.appendChild( | ||
167 | Builder.node('img', | ||
168 | { | ||
169 | src: item.thumb, | ||
170 | className: 'mediaThumb' | ||
171 | }) | ||
172 | ); | ||
173 | |||
174 | Event.observe(mediaPiece, 'click', function() | ||
175 | { | ||
176 | this.setColumn(item.url, this.direction, ''); | ||
177 | }.bindAsEventListener(this)); | ||
178 | }.bind(this)); | ||
179 | }, | ||
180 | |||
181 | /* | ||
182 | * Clean up the data. | ||
183 | */ | ||
184 | _cleanData: function() | ||
185 | { | ||
186 | var lData = this.data.contentNarrow[0].htmlContent; | ||
187 | this.data.contentNarrow[0].htmlContent = lData.replace(/<a/gi, '<a style="color: '+this.color+'"'); | ||
188 | }, | ||
189 | |||
190 | /* | ||
191 | * Calculate the heights of various interface elements for use in | ||
192 | * laying out the page. | ||
193 | */ | ||
194 | _calculateHeights: function() | ||
195 | { | ||
196 | // This data structure will hold the heights of all the elements | ||
197 | // in the narrow column for use in dynamically calculating the | ||
198 | // layout of the column. | ||
199 | this.heightTable = | ||
200 | { | ||
201 | headline : this.headLine ? this.headLine.getHeight() : 0, | ||
202 | content : this.htmlDiv ? this.htmlDiv.getHeight() : 0, | ||
203 | thumbnails : this.thumbStrip ? this.thumbStrip.getHeight() : 0, | ||
204 | resources : this.resourceContainer ? this.resourceContainer.getHeight() : 0, | ||
205 | paddingFactor : !Prototype.Browser.IE ? 40 : 0 | ||
206 | }; | ||
207 | |||
208 | // If there is no headline IE comes up with a ridiculous height for some | ||
209 | // reason so we correct for that here. | ||
210 | if (Prototype.Browser.IE && this.data.contentNarrow[0].title.length == 0) | ||
211 | { | ||
212 | this.heightTable.headline = 0; | ||
213 | } | ||
214 | |||
215 | // A variety of exceptions/tweaks for IE6 found by testing every combination | ||
216 | // of layouts and finding the variance. There isn't much of another way to | ||
217 | // do it. | ||
218 | if (Prototype.Browser.IE) | ||
219 | { | ||
220 | this.heightTable.thumbnail += 2; | ||
221 | this.heightTable.content += 24; | ||
222 | } | ||
223 | |||
224 | if (Prototype.Browser.IE && this.resourceContainer) | ||
225 | { | ||
226 | this.heightTable.resources -= 30; | ||
227 | } | ||
228 | |||
229 | if (Prototype.Browser.IE && this.htmlDiv && this.thumbStrip && !this.resourceContainer) | ||
230 | { | ||
231 | this.heightTable.thumbnails += 10; | ||
232 | } | ||
233 | |||
234 | |||
235 | if (Prototype.Browser.IE && this.resourceContainer && !this.thumbStrip) | ||
236 | { | ||
237 | this.heightTable.resources += 70; | ||
238 | } | ||
239 | |||
240 | // Calculate the height available for content | ||
241 | this.heightTable.avaliableForContent = SME.sizes.innerCard.height - ( | ||
242 | this.heightTable.headline + | ||
243 | this.heightTable.thumbnails + | ||
244 | this.heightTable.resources + | ||
245 | this.heightTable.paddingFactor | ||
246 | ); | ||
247 | }, | ||
248 | |||
249 | /* | ||
250 | * Setup the scroll bar on the wide content area. | ||
251 | */ | ||
252 | _setupScroller: function() | ||
253 | { | ||
254 | // We only want to show the scroll bar if there is a need for it | ||
255 | if (this.heightTable.content < this.heightTable.avaliableForContent) | ||
256 | { | ||
257 | return; | ||
258 | } | ||
259 | |||
260 | this.contentArea.appendChild( | ||
261 | this.scrollCont = Builder.node('div', | ||
262 | { | ||
263 | style: 'border-left: 1px solid ' + this.card.options.color + ';' + | ||
264 | 'width: 1px; right: 0px; position: absolute; ' + | ||
265 | 'height:' + this.heightTable.avaliableForContent + 'px; ' + | ||
266 | 'top: ' + (this.heightTable.headline + 10) + 'px;' | ||
267 | }, | ||
268 | |||
269 | this.scrollBar = Builder.node('img', | ||
270 | { | ||
271 | src: 'images/pill.gif', | ||
272 | style: 'display: block; margin-left: -3px; cursor: move; ' + | ||
273 | 'background: ' + this.card.options.color + ';' | ||
274 | }) | ||
275 | )); | ||
276 | |||
277 | new Control.Slider(this.scrollBar, this.scrollCont, | ||
278 | { | ||
279 | axis: 'vertical', | ||
280 | range: $R(0, this.heightTable.content), | ||
281 | |||
282 | onSlide: function(value) | ||
283 | { | ||
284 | this.htmlDiv.scrollTop = value; | ||
285 | }.bind(this) | ||
286 | }); | ||
287 | |||
288 | }, | ||
289 | |||
290 | /* | ||
291 | * Main function to layout the card. | ||
292 | */ | ||
293 | layout: function() | ||
294 | { | ||
295 | this._cleanData(); | ||
296 | |||
297 | this.setColumn(this.data.contentWide[0].url, this.direction, this.data.contentWide[0].title); | ||
298 | |||
299 | this.contentArea = this.cframe.appendChild( | ||
300 | Builder.node('div', { className: 'narrowCol' }, | ||
301 | this.headLine = Builder.node('h1', { style: 'color: ' + this.color }, this.data.contentNarrow[0].title) | ||
302 | ) | ||
303 | ); | ||
304 | |||
305 | // We've gotta set the width here otherwise the height | ||
306 | // calculations will be incorrect | ||
307 | this.htmlDiv = Builder.node('div', { style: 'width: 98%; overflow: hidden; width: 300px;' }); | ||
308 | |||
309 | // Must set this early on otherwise there is no way to determine | ||
310 | // the actual height of the content div | ||
311 | this.setHTML(this.data.contentNarrow[0].htmlContent); | ||
312 | this.contentArea.appendChild(this.htmlDiv); | ||
313 | |||
314 | this.getResourceList(); | ||
315 | this.getThumbnails(); | ||
316 | |||
317 | // IE doesn't let prototype mess with the default object | ||
318 | // prototypes so we have to manually extend them. Sigh... | ||
319 | [ | ||
320 | this.headLine, | ||
321 | this.htmlDiv, | ||
322 | this.thumbStrip, | ||
323 | this.resourceContainer, | ||
324 | this.contentArea | ||
325 | ].each(Element.extend); | ||
326 | |||
327 | // Set these styles up here or we get a -21px bug in our | ||
328 | // calculation code | ||
329 | this.contentArea.setStyle( | ||
330 | { | ||
331 | width: '310px', | ||
332 | position: 'absolute', | ||
333 | top: '0px' | ||
334 | }); | ||
335 | |||
336 | this._calculateHeights(); | ||
337 | this._setupScroller(); | ||
338 | |||
339 | // We set this last, after all the height calculations are | ||
340 | // completed. That makes it a lot easier to work with. | ||
341 | this.htmlDiv.setStyle({ height: this.heightTable.avaliableForContent + 'px' }); | ||
342 | |||
343 | // Webkit doesn't pad things correctly | ||
344 | if (this.direction == 'left') | ||
345 | { | ||
346 | this.contentArea.setStyle({ left: '10px' }); | ||
347 | } | ||
348 | else | ||
349 | { | ||
350 | this.contentArea.setStyle({ right: '10px' }); | ||
351 | } | ||
352 | |||
353 | // If this is an intro card do the intro card stuff. | ||
354 | if (this.data.template == 'intro') | ||
355 | { | ||
356 | this._doIntro(); | ||
357 | } | ||
358 | |||
359 | return this; | ||
360 | }, | ||
361 | |||
362 | /* | ||
363 | * Setup an intro card. Because intro cards aren't really that much | ||
364 | * different from a basic card and because class inheritance sucks in | ||
365 | * Prototype > 1.6 I'm just going to add this into here. | ||
366 | */ | ||
367 | _doIntro: function() | ||
368 | { | ||
369 | this.contentArea.appendChild(Builder.node('div', { className: 'skipbox' }, | ||
370 | this.skipLink = Builder.node('a', { href: '#tablehome', className: 'skiplink' }, | ||
371 | [ | ||
372 | Strings.skipButton, | ||
373 | Builder.node('img', { src: 'images/arrow_right_grey.gif', className: 'skipimg' }) | ||
374 | ]) | ||
375 | )); | ||
376 | }, | ||
377 | |||
378 | /* | ||
379 | * Throw the card into a popup window for printing. | ||
380 | */ | ||
381 | print: function() | ||
382 | { | ||
383 | var wind = window.open('', '', 'width=' + SME.sizes.innerCard.width + ',height=' + (SME.sizes.innerCard.height + 60)); | ||
384 | |||
385 | with (wind) | ||
386 | { | ||
387 | with (document) | ||
388 | { | ||
389 | write('<html><head><style type="text/css">@import url(application.css); .narrowCol{top: 30px !important;}</style></head><body>'); | ||
390 | write('<img src="images/logo.jpg" style="display: block; margin: 5px 0px 30px 5px;"/>'); | ||
391 | write(this.cframe.innerHTML); | ||
392 | write('</body></html>'); | ||
393 | close(); | ||
394 | } | ||
395 | |||
396 | print(); | ||
397 | close(); | ||
398 | } | ||
399 | |||
400 | return true; | ||
401 | }, | ||
402 | |||
403 | /* | ||
404 | * Debug the card layout. Generally this should be used to spot out | ||
405 | * data issues. But it could also be used for other debugging purposes. | ||
406 | */ | ||
407 | _debug: function() | ||
408 | { | ||
409 | |||
410 | } | ||
411 | }); \ No newline at end of file | ||
diff --git a/docroot/classes/layouts/layout.special.class.js b/docroot/classes/layouts/layout.special.class.js new file mode 100755 index 0000000..b285390 --- /dev/null +++ b/docroot/classes/layouts/layout.special.class.js | |||
@@ -0,0 +1,43 @@ | |||
1 | /* | ||
2 | * Material Experience - Special Case Layout Engine | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | */ | ||
8 | |||
9 | Card.Layout.Special = Class.create(); | ||
10 | Object.extend(Object.extend(Card.Layout.Special.prototype, Card.Layout.prototype), | ||
11 | { | ||
12 | /* | ||
13 | * Initialize the layout class | ||
14 | */ | ||
15 | initialize: function(cframe, data, card) | ||
16 | { | ||
17 | this.cframe = cframe; | ||
18 | this.data = data; | ||
19 | this.card = card; | ||
20 | this.color = this.card.options.color; | ||
21 | this.hasResources = false; | ||
22 | |||
23 | // Check if the url is provided otherwise pull it out of the | ||
24 | // wide content field. | ||
25 | if (!this.data.url) | ||
26 | { | ||
27 | this.data.url = this.data.contentWide[0].url; | ||
28 | } | ||
29 | }, | ||
30 | |||
31 | /* | ||
32 | * Main function to layout the card. | ||
33 | */ | ||
34 | layout: function() | ||
35 | { | ||
36 | this.cframe.innerHTML = '<iframe ' + | ||
37 | 'src = "' + this.data.url + '" ' + | ||
38 | 'frameborder = "0" ' + | ||
39 | 'width = "' + (SME.sizes.card.width - 11) + '" ' + | ||
40 | 'height = "' + (SME.sizes.card.height - 40) + '"' + | ||
41 | '></iframe>'; | ||
42 | } | ||
43 | }); \ No newline at end of file | ||
diff --git a/docroot/classes/overlay.class.js b/docroot/classes/overlay.class.js new file mode 100755 index 0000000..9a6e110 --- /dev/null +++ b/docroot/classes/overlay.class.js | |||
@@ -0,0 +1,118 @@ | |||
1 | /* | ||
2 | * Material Experience - Document Overlay Class | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | */ | ||
8 | |||
9 | var Overlay = Class.create(); | ||
10 | Object.extend(Overlay.prototype, | ||
11 | { | ||
12 | /* | ||
13 | * Initializes the overlay class | ||
14 | */ | ||
15 | initialize: function() | ||
16 | { | ||
17 | this.options = Object.extend( | ||
18 | { | ||
19 | zIndex: 9000, // z-index of the overlay | ||
20 | background: 'white', // Overlay color | ||
21 | opacity: 0.5, // Overlay opacity | ||
22 | fadeInSpeed: 0.2, // Fade in speed | ||
23 | fadeOutSpeed: 1, // Fade out speed | ||
24 | onClick: this.hide, // Callback for overlay click | ||
25 | onShow: Prototype.emptyFunction, // Callback when the overlay is shown | ||
26 | onHide: Prototype.emptyFunction // Callback when the overlay is hidden | ||
27 | }, $H(arguments[0]) || {}); | ||
28 | |||
29 | this._createOverlay(); | ||
30 | }, | ||
31 | |||
32 | /* | ||
33 | * Creates the actual DOM elements of the overlay. | ||
34 | */ | ||
35 | _createOverlay: function() | ||
36 | { | ||
37 | this.overlay = Element.extend(document.createElement('div')); | ||
38 | document.body.appendChild(this.overlay); | ||
39 | |||
40 | Event.observe(this.overlay, 'click', this.options.onClick.bindAsEventListener(this)); | ||
41 | |||
42 | this.overlay.setStyle( | ||
43 | { | ||
44 | backgroundColor: this.options.background, | ||
45 | zIndex: this.options.zIndex, | ||
46 | height: '100%', | ||
47 | width: '100%', | ||
48 | position: 'absolute', | ||
49 | display: 'none', | ||
50 | top: 0, | ||
51 | left: 0 | ||
52 | }); | ||
53 | }, | ||
54 | |||
55 | /* | ||
56 | * Displays the overlay. | ||
57 | */ | ||
58 | show: function() | ||
59 | { | ||
60 | this._fixBody(); | ||
61 | |||
62 | this.options.onShow(this); | ||
63 | |||
64 | new Effect.Appear(this.overlay, | ||
65 | { | ||
66 | to: this.options.opacity, | ||
67 | duration: this.options.fadeInSpeed, | ||
68 | limit: 1 | ||
69 | }); | ||
70 | }, | ||
71 | |||
72 | /* | ||
73 | * Hides the overlay. | ||
74 | */ | ||
75 | hide: function() | ||
76 | { | ||
77 | this.options.onHide(this); | ||
78 | |||
79 | new Effect.Fade(this.overlay, | ||
80 | { | ||
81 | duration: this.options.fadeOutSpeed, | ||
82 | limit: 1, | ||
83 | |||
84 | afterFinish: function() | ||
85 | { | ||
86 | this._fixBody(true); | ||
87 | }.bind(this) | ||
88 | }); | ||
89 | }, | ||
90 | |||
91 | /* | ||
92 | * Removes the overlay from the DOM | ||
93 | */ | ||
94 | destroy: function() | ||
95 | { | ||
96 | document.body.removeChild(this.overlay); | ||
97 | }, | ||
98 | |||
99 | /* | ||
100 | * Fix for IE 6, sets the body height and overflow so people can not | ||
101 | * scroll beyond the overlay. Disable overflow on the body and html | ||
102 | * so you can see the whole overlay. | ||
103 | */ | ||
104 | _fixBody: function(reset) | ||
105 | { | ||
106 | var myHeight = reset ? '' : '100%'; | ||
107 | var myOverflow = reset ? '' : 'hidden'; | ||
108 | |||
109 | $A([document.body,document.getElementsByTagName('html')[0]]).each(function(t) | ||
110 | { | ||
111 | Element.extend(t).setStyle( | ||
112 | { | ||
113 | height: myHeight, | ||
114 | overflow: myOverflow | ||
115 | }); | ||
116 | }); | ||
117 | } | ||
118 | }); \ No newline at end of file | ||
diff --git a/docroot/classes/roundcorners.class.js b/docroot/classes/roundcorners.class.js new file mode 100755 index 0000000..c156d30 --- /dev/null +++ b/docroot/classes/roundcorners.class.js | |||
@@ -0,0 +1,60 @@ | |||
1 | /* | ||
2 | * Material Experience - Card Chip Class | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | * | ||
8 | * An HTML based approach to drawing rounded corners using element | ||
9 | * "slices" to create the corners instead of images. This depends | ||
10 | * on a section in the stylesheet to work correctly. | ||
11 | * | ||
12 | * Code adapted from: http://www.html.it/articoli/nifty/index.html | ||
13 | */ | ||
14 | |||
15 | var RoundedCorners = Class.create(); | ||
16 | Object.extend(RoundedCorners.prototype, | ||
17 | { | ||
18 | // Direction table for rounded corners. | ||
19 | directions: | ||
20 | { | ||
21 | top : 'top', | ||
22 | bottom : 'bottom' | ||
23 | }, | ||
24 | |||
25 | /* | ||
26 | * Initialize the object | ||
27 | */ | ||
28 | initialize: function(color) | ||
29 | { | ||
30 | this.color = color; | ||
31 | }, | ||
32 | |||
33 | /* | ||
34 | * Creates a rounded corner container. | ||
35 | */ | ||
36 | get: function(direction) | ||
37 | { | ||
38 | var slicebox = document.createElement('b'); | ||
39 | slicebox.className = 'round'; | ||
40 | |||
41 | if (direction == this.directions.top) | ||
42 | { | ||
43 | var myEnum = $A($R(1,10)); | ||
44 | } | ||
45 | else | ||
46 | { | ||
47 | var myEnum = $A($R(1,10)).reverse(); | ||
48 | } | ||
49 | |||
50 | myEnum.each(function(item) | ||
51 | { | ||
52 | var corner = document.createElement('b'); | ||
53 | corner.className = 'rcSlice_' + item; | ||
54 | corner.style.backgroundColor = this.color; | ||
55 | slicebox.appendChild(corner); | ||
56 | }.bind(this)); | ||
57 | |||
58 | return slicebox; | ||
59 | } | ||
60 | }); \ No newline at end of file | ||
diff --git a/docroot/classes/sketchbook.class.js b/docroot/classes/sketchbook.class.js new file mode 100755 index 0000000..d62101e --- /dev/null +++ b/docroot/classes/sketchbook.class.js | |||
@@ -0,0 +1,288 @@ | |||
1 | /* | ||
2 | * Material Experience - Sketchbook Class | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | */ | ||
8 | |||
9 | var Sketchbook = Class.create(); | ||
10 | |||
11 | Object.extend(Sketchbook.prototype, | ||
12 | { | ||
13 | /* | ||
14 | * Initializes a new sketchbook and table. | ||
15 | */ | ||
16 | initialize: function() | ||
17 | { | ||
18 | // Sketchbook chip data store | ||
19 | this.dataStore = []; | ||
20 | |||
21 | this.table = new CardTable( | ||
22 | { | ||
23 | color: SME.colors.grey, | ||
24 | name: Strings.sketchbook, | ||
25 | id: 'sketchbook', | ||
26 | |||
27 | onRender: function(table) | ||
28 | { | ||
29 | table.table.appendChild(Builder.node('img', | ||
30 | { | ||
31 | id: 'sbTrash', | ||
32 | src: 'images/trash_can.jpg' | ||
33 | })); | ||
34 | |||
35 | Droppables.add($('sbTrash'), | ||
36 | { | ||
37 | accept: 'chip', | ||
38 | |||
39 | onDrop: function(chip) | ||
40 | { | ||
41 | this.removeChip(chip); | ||
42 | }.bind(this) | ||
43 | }); | ||
44 | }.bind(this) | ||
45 | }); | ||
46 | |||
47 | this.hide(); | ||
48 | |||
49 | if (Sketchbook.loggedIn) | ||
50 | { | ||
51 | this.loadData(); | ||
52 | } | ||
53 | |||
54 | // Be sure to save everything when the window closes | ||
55 | Event.observe(window, 'unload', this.saveData.bindAsEventListener(this)); | ||
56 | }, | ||
57 | |||
58 | /* | ||
59 | * Loads sketchbook data from the server and calls a function to merge | ||
60 | * that data into the currently loaded sketchbook. | ||
61 | */ | ||
62 | loadData: function() | ||
63 | { | ||
64 | new Ajax.Request(SME.url.sketchBookIN, | ||
65 | { | ||
66 | method: 'get', | ||
67 | |||
68 | onSuccess: function(transport) | ||
69 | { | ||
70 | this.dataStore = transport.responseText.cleanJSON().evalJSON(); | ||
71 | this._mergeSketchbooks(); | ||
72 | }.bind(this) | ||
73 | }); | ||
74 | }, | ||
75 | |||
76 | /* | ||
77 | * Merges the currently active (presumably guest) sketchbook with the | ||
78 | * data loaded from the server. Prevents the user from losing data when | ||
79 | * they login after adding chips to a guest sketchbook. | ||
80 | */ | ||
81 | _mergeSketchbooks: function() | ||
82 | { | ||
83 | this.dataStore.each(function(chip) | ||
84 | { | ||
85 | if (!this.table.hasChip(chip.cid)) | ||
86 | { | ||
87 | this.table.addChip(chip, { animate: false }); | ||
88 | } | ||
89 | }.bind(this)); | ||
90 | }, | ||
91 | |||
92 | /* | ||
93 | * Add a chip to the sketchbook. This function does duplicate detection | ||
94 | * using methods provided by the CardTable class to avoid adding a chip | ||
95 | * more than once. The function also updates the sketchbook's data | ||
96 | * store. | ||
97 | */ | ||
98 | addChip: function(contID) | ||
99 | { | ||
100 | if (this.table.hasChip(contID)) | ||
101 | { | ||
102 | new Bezel().show(Strings.alreadyAddedSB); | ||
103 | return; | ||
104 | } | ||
105 | |||
106 | new Ajax.Request(SME.url.chipResolver.evaluate({card: contID}), | ||
107 | { | ||
108 | method: 'get', | ||
109 | |||
110 | onSuccess: function(transport) | ||
111 | { | ||
112 | var data = transport.responseText.cleanJSON().evalJSON(); | ||
113 | var windSize = window.getDimensions(); | ||
114 | |||
115 | this.table.addChip(data, | ||
116 | { | ||
117 | x: Math.floor(1 + (windSize.width - 1) * Math.random()), | ||
118 | y: Math.floor(1 + ((windSize.height - 120) - 1) * Math.random()), | ||
119 | animate: false | ||
120 | }); | ||
121 | |||
122 | // Per BS: Would like different messages if logged in or not | ||
123 | if (Sketchbook.loggedIn) | ||
124 | { | ||
125 | new Bezel().show(Strings.addedSketchbook); | ||
126 | } | ||
127 | else | ||
128 | { | ||
129 | new Bezel({ fadeTime: 5 }).show(Strings.addedGuestSB); | ||
130 | } | ||
131 | |||
132 | this.saveData(); | ||
133 | }.bind(this) | ||
134 | }); | ||
135 | }, | ||
136 | |||
137 | /* | ||
138 | * Remove a chip from the table by DOM node. | ||
139 | */ | ||
140 | removeChip: function(chip) | ||
141 | { | ||
142 | this.table.removeChip(chip.classLink.options.contID); | ||
143 | this.saveData(); | ||
144 | }, | ||
145 | |||
146 | /* | ||
147 | * Serialize the datastore and post it back to the server for safe | ||
148 | * keeping. | ||
149 | */ | ||
150 | saveData: function() | ||
151 | { | ||
152 | new Ajax.Request(SME.url.sketchBook, | ||
153 | { | ||
154 | method: 'post', | ||
155 | asynchronous: false, | ||
156 | parameters: { 'sketchbook_data': this.table.getChipData().toJSON() } | ||
157 | }); | ||
158 | }, | ||
159 | |||
160 | /* | ||
161 | * Show the sketchbook table or if not logged in show a login screen. | ||
162 | */ | ||
163 | show: function() | ||
164 | { | ||
165 | if (!Sketchbook.loggedIn) | ||
166 | { | ||
167 | Sketchbook.showLoginScreen(); | ||
168 | } | ||
169 | else | ||
170 | { | ||
171 | this.table.show(); | ||
172 | } | ||
173 | }, | ||
174 | |||
175 | /* | ||
176 | * Hide the sketchbook table. | ||
177 | */ | ||
178 | hide: function() | ||
179 | { | ||
180 | this.table.hide(); | ||
181 | } | ||
182 | }); | ||
183 | |||
184 | Object.extend(Sketchbook, | ||
185 | { | ||
186 | // Flag to determine if the user is logged in or not. | ||
187 | loggedIn: false, | ||
188 | |||
189 | // Username of the logged in user. | ||
190 | username: null, | ||
191 | |||
192 | // Show the login screen in a special case card. | ||
193 | showLoginScreen: function() | ||
194 | { | ||
195 | var t = new Card( | ||
196 | { | ||
197 | color: SME.colors.grey, | ||
198 | title: Strings.pleaseLogin, | ||
199 | addExtraButtons: false, | ||
200 | |||
201 | onClose: function() | ||
202 | { | ||
203 | Sketchbook.checkLogin(); | ||
204 | } | ||
205 | }); | ||
206 | |||
207 | t.setLayout(Card.Layout.Special, { url: SME.url.loginScreen }); | ||
208 | t.show(); | ||
209 | }, | ||
210 | |||
211 | /* | ||
212 | * Once the user has successfully logged in do some actions to setup | ||
213 | * the user interface. | ||
214 | */ | ||
215 | doLoggedIn: function() | ||
216 | { | ||
217 | // Assume that if your calling this function the login succeeded | ||
218 | Sketchbook.loggedIn = true; | ||
219 | |||
220 | $$('div.history')[0].innerHTML = 'Hello ' + Sketchbook.username + '! | ' + | ||
221 | '<a href="http://santoprene.com/cgi-bin/protected/register/logout_designer.pl">' + Strings.logout + '</a> | ' + | ||
222 | '<a href="#" class="manage">' + Strings.manageAccount + '</a> | ' + | ||
223 | '<a href="#" class="history">' + Strings.myHistory + '</a>'; | ||
224 | |||
225 | // On mouseover of the history link show the dropdown. | ||
226 | // We need to re-attach it here because we're changing the | ||
227 | // links (above) and that causes the DOM to lose the original | ||
228 | // event. | ||
229 | $$('div.history a.history')[0].observe('mouseover', function() | ||
230 | { | ||
231 | SME.history.getDropDown(); | ||
232 | }); | ||
233 | |||
234 | $$('a.manage')[0].observe('click', function(event) | ||
235 | { | ||
236 | var t = new Card( | ||
237 | { | ||
238 | color: SME.colors.grey, | ||
239 | addExtraButtons: false, | ||
240 | title: Strings.manageAccount | ||
241 | }); | ||
242 | |||
243 | t.setLayout(Card.Layout.Special, { url: SME.url.manageAccount }); | ||
244 | t.show(); | ||
245 | |||
246 | Event.stop(event); | ||
247 | }); | ||
248 | }, | ||
249 | |||
250 | /* | ||
251 | * Check that the user actually logged in. | ||
252 | */ | ||
253 | checkLogin: function() | ||
254 | { | ||
255 | // If the site cookie doesn't exist no point even going on | ||
256 | if (!$C('Site')) | ||
257 | { | ||
258 | return; | ||
259 | } | ||
260 | |||
261 | /* | ||
262 | * Site cookie is in a weird format, basically sub-cookies are | ||
263 | * separated by & signs. The username and password of the | ||
264 | * currently logged in user is store as username:password in | ||
265 | * the sub-cookie called cookie. Oh yeah and the | ||
266 | * username/password pair is base64 encoded. | ||
267 | */ | ||
268 | var username = decodeBase64($C('Site').split('Cookie&')[1]).split(':')[0]; | ||
269 | Sketchbook.username = username; | ||
270 | |||
271 | /* | ||
272 | * Per Andy: the username and password won't be in the cookie | ||
273 | * (even though the cookie itself is set) if the login failed. | ||
274 | * So lets assume that if we find a username in the appropriate | ||
275 | * place in the cookie we can go ahead and log the user in. | ||
276 | */ | ||
277 | if (username) | ||
278 | { | ||
279 | Sketchbook.loggedIn = true; | ||
280 | Sketchbook.doLoggedIn(); | ||
281 | return true; | ||
282 | } | ||
283 | else | ||
284 | { | ||
285 | return false; | ||
286 | } | ||
287 | } | ||
288 | }); \ No newline at end of file | ||
diff --git a/docroot/classes/sme.namespace.js b/docroot/classes/sme.namespace.js new file mode 100755 index 0000000..05ad125 --- /dev/null +++ b/docroot/classes/sme.namespace.js | |||
@@ -0,0 +1,140 @@ | |||
1 | /* | ||
2 | * Material Experience - Santoprene Material Experience Namespace | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | * | ||
8 | * The Santoprene Materials Experience Namespace holds all non-generic data | ||
9 | * and methods governing the working of this application. | ||
10 | * | ||
11 | * There may be a lot of things set here that are used at runtime but I | ||
12 | * explicitly define them in the SME namespace so other developers can see | ||
13 | * exactly what is contained here. There are also some dynamically defined | ||
14 | * variables based on the current querystring. | ||
15 | */ | ||
16 | |||
17 | var SME = | ||
18 | { | ||
19 | /* | ||
20 | * Release version of the site. This has nothing to do with the SVN | ||
21 | * version of the site. This is not used right now but should be | ||
22 | * incremented with each production release of the site. | ||
23 | */ | ||
24 | releaseVersion: '1.2.2', | ||
25 | |||
26 | /* | ||
27 | * The color table exists to provide aliases to the colors used in the | ||
28 | * interface. This will make it much easier to change at a later date | ||
29 | * should that ever be required. The color table should be used only | ||
30 | * for default values. | ||
31 | */ | ||
32 | colors: | ||
33 | { | ||
34 | blue : 'rgb(100, 129, 145)', | ||
35 | brown : 'rgb(100, 065, 040)', | ||
36 | green : 'rgb(100, 150, 040)', | ||
37 | grey : 'rgb(100, 100, 100)', | ||
38 | orange : 'rgb(225, 125, 010)', | ||
39 | red : 'rgb(150, 035, 020)' | ||
40 | }, | ||
41 | |||
42 | /* | ||
43 | * The sizes table governs the size of the user interface elements. | ||
44 | * This exists for much the same reason as the color table, to provide | ||
45 | * an easy way to change sizes later. Note that the rest of the styles | ||
46 | * in the interface should be set relatively so changing these | ||
47 | * parameters should not destroy the interface. | ||
48 | */ | ||
49 | sizes: | ||
50 | { | ||
51 | card : { height: 520, width: 925 }, // Card Size | ||
52 | innerCard : { height: 481, width: 914 }, // Inside Size of Card | ||
53 | chipMax : { height: 180, width: 143 }, // Maximum Chip Size | ||
54 | windMin : { height: 589, width: 1020 } // Minimum Window Size | ||
55 | }, | ||
56 | |||
57 | // URLs Used by the Application | ||
58 | url: | ||
59 | { | ||
60 | tableList : 'data/card_tables.js', | ||
61 | perisitChips : 'data/persist_chip.js', | ||
62 | loginScreen : 'http://' + window.location.hostname + '/cgi-bin/login.pl', | ||
63 | sketchBook : 'http://' + window.location.hostname + '/cgi-bin/sketchbook.pl', | ||
64 | sketchBookIN : 'http://' + window.location.hostname + '/cgi-bin/sketchbook.pl?interactive=false', | ||
65 | manageAccount: 'http://www.santoprene.com/cgi-bin/protected/register/account.pl?template=designer_', | ||
66 | introCards : 'http://' + window.location.hostname + '/cgi-bin/sop_proxy.pl?site=http://www.santoprene.com/cms/designer_json/224fa06db73ece8a/index.html', | ||
67 | cards : new Template('http://' + window.location.hostname + '/cgi-bin/sop_proxy.pl?site=http://www.santoprene.com/cms/designer_card/#{card}/index.html'), | ||
68 | sendToFriend : new Template('http://www.santoprene.com/cgi-bin/send_page/send.pl?tmpl=designer&title=#{title}&durl=#{durl}'), | ||
69 | cardTables : new Template('http://' + window.location.hostname + '/cgi-bin/card_table.pl?table=#{table}&h=#{h}&w=#{w}'), | ||
70 | chipResolver : new Template('http://' + window.location.hostname + '/cgi-bin/sketchbook_resolver.pl?card=#{card}'), | ||
71 | cardPreview : new Template('http://' + window.location.hostname + '/cgi-bin/sop_preview_proxy.pl?site=http://admin.santoprene.com/cgi-bin/content/display_content.pl?form_id=48&content_id=#{card}&site_id=1'), | ||
72 | |||
73 | // Debugging and Development URLS (removed by build system) | ||
74 | /*;;;*/ chipResolver : new Template('http://' + window.location.hostname + '/cgi-bin/sop_proxy.pl?site=http://materialexperience.santoprene.com/cgi-bin/sketchbook_resolver.pl?card=#{card}'), | ||
75 | /*;;;*/ cardTables : new Template('http://' + window.location.hostname + '/cgi-bin/sop_proxy.pl?site=http://materialexperience.santoprene.com/cgi-bin/card_table.pl?table=#{table}&h=#{h}&w=#{w}'), | ||
76 | // END Debugging and Development URLS | ||
77 | |||
78 | // Dummy item terminates the list since the items above are | ||
79 | // removed by the build system and the trailing comma would | ||
80 | // break in IE. Don't Remove this! | ||
81 | dontRemove: null | ||
82 | }, | ||
83 | |||
84 | /* | ||
85 | * Compose layout name to layout engine mapping. Allows us to add more | ||
86 | * layout engines later a lot simpler than the previous method allowed. | ||
87 | */ | ||
88 | engineMapping : | ||
89 | { | ||
90 | 'narrow-left' : Card.Layout.Primary, | ||
91 | 'narrow-right' : Card.Layout.Primary, | ||
92 | 'special' : Card.Layout.Special, | ||
93 | 'intro' : Card.Layout.Primary, | ||
94 | 'custom-menu' : Card.Layout.Custom | ||
95 | }, | ||
96 | |||
97 | /* | ||
98 | * When the page is called with a query string of debug then you will | ||
99 | * be able to open a firebug console and view debugging messages. Also | ||
100 | * checks to make sure console.log() is actually defined to avoid | ||
101 | * throwing errors on browsers w/out firebug. | ||
102 | */ | ||
103 | debug: /debug=.*(true)/.test(window.location.search) && !!(console.log), | ||
104 | |||
105 | /* | ||
106 | * Skip the intro card because it wastes time during development. | ||
107 | */ | ||
108 | skipIntro: /.*intro=false.*/.test(window.location.search), | ||
109 | |||
110 | /* | ||
111 | * | ||
112 | * RUNTIME VARIABLES SECTION | ||
113 | * | ||
114 | * These are variables set at runtime but declared explicitly here | ||
115 | * so other developers can have a clear picture of what is in this | ||
116 | * namespace. | ||
117 | * | ||
118 | * If you set a variable in this namespace at runtime please declare | ||
119 | * it explicitly here. | ||
120 | * | ||
121 | */ | ||
122 | |||
123 | // Array of all loaded tables. | ||
124 | tables: [], | ||
125 | |||
126 | // AJAX information bezel. | ||
127 | AJAXBezel: null, | ||
128 | |||
129 | // Currently active card table. | ||
130 | currentTable: null, | ||
131 | |||
132 | // Sketchbook. | ||
133 | sketchbook: null, | ||
134 | |||
135 | // History manager. | ||
136 | history: null, | ||
137 | |||
138 | // Card currently being displayed. | ||
139 | currentCard: null | ||
140 | }; | ||
diff --git a/docroot/classes/table.class.js b/docroot/classes/table.class.js new file mode 100755 index 0000000..1784c5c --- /dev/null +++ b/docroot/classes/table.class.js | |||
@@ -0,0 +1,315 @@ | |||
1 | /* | ||
2 | * Material Experience - Card Table Class | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | */ | ||
8 | |||
9 | var CardTable = Class.create(); | ||
10 | |||
11 | Object.extend(CardTable.prototype, | ||
12 | { | ||
13 | /* | ||
14 | * Create the table object. | ||
15 | */ | ||
16 | initialize: function() | ||
17 | { | ||
18 | this.options = Object.extend( | ||
19 | { | ||
20 | name: 'New Table', // Table Name | ||
21 | color: SME.colors.blue, // Table Color | ||
22 | id: null, // Table ID | ||
23 | decorate: true, // Draw table decorator | ||
24 | onRender: Prototype.emptyFunction // Callback function when the table renders | ||
25 | }, arguments[0] || {}); | ||
26 | |||
27 | // Cache of the chips on the table | ||
28 | this.chips = []; | ||
29 | |||
30 | // Cache of table JSON data | ||
31 | this.data = null; | ||
32 | |||
33 | this._createTable(); | ||
34 | |||
35 | // Initially resizes the window to the correct height, and keep | ||
36 | // resizing it as the window changes | ||
37 | this.resize(); | ||
38 | Event.observe(window, 'resize', function() | ||
39 | { | ||
40 | this.resize(); | ||
41 | }.bindAsEventListener(this)); | ||
42 | |||
43 | if (this.options.decorate) | ||
44 | { | ||
45 | this._createAppDecorator(); | ||
46 | } | ||
47 | }, | ||
48 | |||
49 | /* | ||
50 | * Create the table DOM object. | ||
51 | */ | ||
52 | _createTable: function() | ||
53 | { | ||
54 | this.table = document.createElement('div'); | ||
55 | |||
56 | if (this.options.id) | ||
57 | { | ||
58 | this.table.id = this.options.id; | ||
59 | } | ||
60 | |||
61 | this.table.className = 'table'; | ||
62 | document.body.appendChild(this.table); | ||
63 | |||
64 | this.options.onRender(this); | ||
65 | }, | ||
66 | |||
67 | /* | ||
68 | * Load the chip data from the server and cache it in the object. | ||
69 | */ | ||
70 | loadChipData: function(feedURL, urlParams, showAfterLoad) | ||
71 | { | ||
72 | new Ajax.Request(feedURL.evaluate(urlParams), | ||
73 | { | ||
74 | method: 'get', | ||
75 | |||
76 | onSuccess: function(transport) | ||
77 | { | ||
78 | this.data = transport.responseText.evalJSON(); | ||
79 | |||
80 | if (showAfterLoad || this.options.id == SME.currentTable) | ||
81 | this.showChips(); | ||
82 | }.bind(this) | ||
83 | }); | ||
84 | }, | ||
85 | |||
86 | /* | ||
87 | * Creates the decorator icon show in the footer of the user interface | ||
88 | * which just links to a table hash and relies on the history manager | ||
89 | * to load the appropriate table. | ||
90 | */ | ||
91 | _createAppDecorator: function() | ||
92 | { | ||
93 | var link = Builder.node('a', | ||
94 | { | ||
95 | href: '#table' + this.options.id, | ||
96 | style: 'color: ' + this.options.color, | ||
97 | alt: this.options.name + ' Table', | ||
98 | title: this.options.name + ' Table' | ||
99 | }, | ||
100 | [ | ||
101 | Builder.node('img', | ||
102 | { | ||
103 | className: 'appDecorator', | ||
104 | src: 'images/pill.gif', | ||
105 | style: 'background: ' + this.options.color | ||
106 | }), | ||
107 | |||
108 | this.options.name.toUpperCase() | ||
109 | ]); | ||
110 | |||
111 | $('footer').appendChild(link); | ||
112 | }, | ||
113 | |||
114 | /* | ||
115 | * Show chips on the table once data is loaded. This function creates | ||
116 | * and caches chip objects on the table. | ||
117 | */ | ||
118 | showChips: function() | ||
119 | { | ||
120 | // Only load the chips once | ||
121 | if (this.chipsDone || !this.data) | ||
122 | { | ||
123 | return; | ||
124 | } | ||
125 | else | ||
126 | { | ||
127 | this.chipsDone = true; | ||
128 | } | ||
129 | |||
130 | this.bez = new Bezel( | ||
131 | { | ||
132 | onShow: function() | ||
133 | { | ||
134 | this.data.each(function(chip) | ||
135 | { | ||
136 | this.addChip(chip); | ||
137 | }.bind(this)); | ||
138 | |||
139 | this.chips.each(function(chip) | ||
140 | { | ||
141 | chip.animate(); | ||
142 | }); | ||
143 | }.bind(this) | ||
144 | }).show(Strings.loadingNoAnim); | ||
145 | }, | ||
146 | |||
147 | /* | ||
148 | * Remove a chip from the table. | ||
149 | */ | ||
150 | removeChip: function(chipContID) | ||
151 | { | ||
152 | this.chips.each(function(chip) | ||
153 | { | ||
154 | if (chip.options.contID == chipContID) | ||
155 | { | ||
156 | chip.hide(); | ||
157 | this.chips = this.chips.without(chip); | ||
158 | } | ||
159 | }.bind(this)) | ||
160 | }, | ||
161 | |||
162 | /* | ||
163 | * Add a chip to the table. | ||
164 | */ | ||
165 | addChip: function(data) | ||
166 | { | ||
167 | var cords = Object.extend( | ||
168 | { | ||
169 | x: data.x, | ||
170 | y: data.y, | ||
171 | animate: true | ||
172 | }, arguments[1] || {}); | ||
173 | |||
174 | var chip = new CardChip( | ||
175 | { | ||
176 | x: cords.x, | ||
177 | y: cords.y, | ||
178 | category: data.category, | ||
179 | contID: data.cid, | ||
180 | title: data.title, | ||
181 | image: data.chip, | ||
182 | locked: data.locked || false, | ||
183 | animate: cords.animate | ||
184 | }); | ||
185 | |||
186 | chip.addTo(this.table); | ||
187 | this.chips.push(chip); | ||
188 | }, | ||
189 | |||
190 | /* | ||
191 | * Clear out the chip cache. This function is not to be used within | ||
192 | * the code. It exists to be called manually on the command line for | ||
193 | * debugging purposes. | ||
194 | */ | ||
195 | _blowChips: function() | ||
196 | { | ||
197 | this.chips = []; | ||
198 | }, | ||
199 | |||
200 | /* | ||
201 | * Serialize all the chip data on the table to an object, presumably | ||
202 | * to be converted to JSON later. The object should match the output | ||
203 | * of card_table.pl | ||
204 | */ | ||
205 | getChipData: function() | ||
206 | { | ||
207 | var data = []; | ||
208 | |||
209 | this.chips.each(function(c) | ||
210 | { | ||
211 | data.push(c._serialize()); | ||
212 | }); | ||
213 | |||
214 | return data; | ||
215 | }, | ||
216 | |||
217 | /* | ||
218 | * Tests if the table contains a chip with the particular content ID. | ||
219 | */ | ||
220 | hasChip: function(chipID) | ||
221 | { | ||
222 | var chip = this.chips.find(function(item) | ||
223 | { | ||
224 | if (item.options.contID == chipID) | ||
225 | return true; | ||
226 | |||
227 | return false; | ||
228 | }); | ||
229 | |||
230 | return chip ? true : false; | ||
231 | }, | ||
232 | |||
233 | /* | ||
234 | * Changes the size of the table when the browser window is resized. | ||
235 | */ | ||
236 | resize: function() | ||
237 | { | ||
238 | this.table.style.height = ($('footer').offsetTop - | ||
239 | $('footer').offsetHeight) + 'px'; | ||
240 | }, | ||
241 | |||
242 | /* | ||
243 | * Shows the table. | ||
244 | */ | ||
245 | show: function() | ||
246 | { | ||
247 | this.showChips(); | ||
248 | SME.currentTable = this.options.id; | ||
249 | this.table.style.display = 'block'; | ||
250 | }, | ||
251 | |||
252 | /* | ||
253 | * Hides the table. | ||
254 | */ | ||
255 | hide: function() | ||
256 | { | ||
257 | this.table.style.display = 'none'; | ||
258 | } | ||
259 | }); | ||
260 | |||
261 | Object.extend(CardTable, | ||
262 | { | ||
263 | /* | ||
264 | * Show a table and hide all others. Not the special exception for the | ||
265 | * sketchbook. | ||
266 | */ | ||
267 | showTable: function(table) | ||
268 | { | ||
269 | SME.tables.each(function(item) | ||
270 | { | ||
271 | if (item.options.id == table) | ||
272 | { | ||
273 | item.show(); | ||
274 | } | ||
275 | else | ||
276 | { | ||
277 | item.hide(); | ||
278 | } | ||
279 | }); | ||
280 | |||
281 | if (table == 'sketchbook') | ||
282 | { | ||
283 | SME.sketchbook.show(); | ||
284 | } | ||
285 | else | ||
286 | { | ||
287 | SME.sketchbook.hide(); | ||
288 | } | ||
289 | }, | ||
290 | |||
291 | /* | ||
292 | * Return the color as a string for a table based on table ID. Note | ||
293 | * the special exception for special case cards which are not associated | ||
294 | * with a particular table. | ||
295 | */ | ||
296 | tableColor: function(table) | ||
297 | { | ||
298 | if (table == 'special') | ||
299 | { | ||
300 | return SME.colors.grey; | ||
301 | } | ||
302 | |||
303 | var color = SME.tables.find(function(item) | ||
304 | { | ||
305 | if (item.options.id == table) | ||
306 | { | ||
307 | return true; | ||
308 | } | ||
309 | |||
310 | return false; | ||
311 | }).options.color; | ||
312 | |||
313 | return color; | ||
314 | } | ||
315 | }); \ No newline at end of file | ||
diff --git a/docroot/classes/utility.js b/docroot/classes/utility.js new file mode 100755 index 0000000..218a059 --- /dev/null +++ b/docroot/classes/utility.js | |||
@@ -0,0 +1,199 @@ | |||
1 | /* | ||
2 | * Material Experience - Utility Functions | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | * | ||
8 | * Various utility functions that do not natively exist in any of | ||
9 | * the libraries we use. | ||
10 | * | ||
11 | * This is also the zoo for monkey patches. Be careful. | ||
12 | */ | ||
13 | |||
14 | Object.extend(window, | ||
15 | { | ||
16 | /* | ||
17 | * Calculates the top and left offsets to center an element within the | ||
18 | * page. Every browser seems to approach this in a different fashion but | ||
19 | * this function works well on FX, OP, WK, and IE. | ||
20 | */ | ||
21 | calcCordsToCenter: function(height, width) | ||
22 | { | ||
23 | var windowSize = window.getDimensions(); | ||
24 | |||
25 | return { | ||
26 | top: Math.round(((windowSize.height - height) / 2)), | ||
27 | left: Math.round(((windowSize.width - width) / 2)) | ||
28 | }; | ||
29 | }, | ||
30 | |||
31 | /* | ||
32 | * Gets the dimensions of the current window accounting for variations | ||
33 | * in browsers. Supports IE in either quirks or strict mode. | ||
34 | */ | ||
35 | getDimensions: function() | ||
36 | { | ||
37 | var windowSize = { | ||
38 | height: document.documentElement.clientHeight, | ||
39 | width: document.documentElement.clientWidth | ||
40 | }; | ||
41 | |||
42 | if (Prototype.Browser.Opera) | ||
43 | { | ||
44 | windowSize = { | ||
45 | height: document.body.clientHeight, | ||
46 | width: document.body.clientWidth | ||
47 | }; | ||
48 | } | ||
49 | |||
50 | if (Prototype.Browser.WebKit) | ||
51 | { | ||
52 | windowSize = { | ||
53 | height: window.innerHeight, | ||
54 | width: window.innerWidth | ||
55 | }; | ||
56 | } | ||
57 | |||
58 | return windowSize; | ||
59 | }, | ||
60 | |||
61 | /* | ||
62 | * Verify the screen size is great than a set minimum or warn the user. | ||
63 | * Probably superfluous but why not. | ||
64 | */ | ||
65 | checkResolution: function() | ||
66 | { | ||
67 | var size = window.getDimensions(); | ||
68 | |||
69 | if ( | ||
70 | size.height < SME.sizes.windMin.height || | ||
71 | size.width < SME.sizes.windMin.width | ||
72 | ) | ||
73 | { | ||
74 | new Bezel({ displayTime: 2 }).show(Strings.windowSize); | ||
75 | Event.stopObserving(window, 'resize', window.checkResolution); | ||
76 | } | ||
77 | } | ||
78 | }); | ||
79 | |||
80 | Object.extend(String.prototype, | ||
81 | { | ||
82 | /* | ||
83 | * Cleans up sloppy Compose JSON output by removing comments, newlines, | ||
84 | * and tabs. | ||
85 | */ | ||
86 | cleanJSON: function() | ||
87 | { | ||
88 | var data = this; | ||
89 | data = data.replace(/^\/\/.*/gm,''); | ||
90 | data = data.replace(/(\n|\t|\r)/g,''); | ||
91 | |||
92 | return data; | ||
93 | } | ||
94 | }); | ||
95 | |||
96 | Object.extend(Ajax.Request.prototype, | ||
97 | { | ||
98 | /* | ||
99 | * Forces IE to enqueue XHR requests otherwise it gets bogged down and | ||
100 | * freezes the interface. | ||
101 | */ | ||
102 | initialize: function(url, options) | ||
103 | { | ||
104 | this.transport = Ajax.getTransport(); | ||
105 | this.setOptions(options); | ||
106 | this.url = url; | ||
107 | |||
108 | if (Prototype.Browser.IE) | ||
109 | { | ||
110 | Ajax.requestQueue.push(this); | ||
111 | Ajax._runQueue(); | ||
112 | } | ||
113 | else | ||
114 | { | ||
115 | this.request(url); | ||
116 | } | ||
117 | }, | ||
118 | |||
119 | /* | ||
120 | * Monkey patch prototype so it stops auto-evaluating the returned JSON | ||
121 | * data. Our JSON data is simply not clean enough for that and it throws | ||
122 | * errors all over the place. | ||
123 | * | ||
124 | * I've made as few modifications from the original as possible to ease | ||
125 | * in porting forward changes made in Prototype. | ||
126 | */ | ||
127 | respondToReadyState: function(readyState) | ||
128 | { | ||
129 | var state = Ajax.Request.Events[readyState]; | ||
130 | var transport = this.transport; | ||
131 | |||
132 | if (state == 'Complete') | ||
133 | { | ||
134 | try | ||
135 | { | ||
136 | this._complete = true; | ||
137 | (this.options['on' + this.transport.status] | ||
138 | || this.options['on' + (this.success() ? 'Success' : 'Failure')] | ||
139 | || Prototype.emptyFunction)(transport); | ||
140 | } | ||
141 | catch (e) | ||
142 | { | ||
143 | this.dispatchException(e); | ||
144 | } | ||
145 | } | ||
146 | |||
147 | try | ||
148 | { | ||
149 | (this.options['on' + state] || Prototype.emptyFunction)(transport); | ||
150 | Ajax.Responders.dispatch('on' + state, this, transport); | ||
151 | } | ||
152 | catch (e) | ||
153 | { | ||
154 | this.dispatchException(e); | ||
155 | } | ||
156 | |||
157 | if (state == 'Complete') | ||
158 | { | ||
159 | // avoid memory leak in MSIE: clean up | ||
160 | this.transport.onreadystatechange = Prototype.emptyFunction; | ||
161 | } | ||
162 | } | ||
163 | }); | ||
164 | |||
165 | /* | ||
166 | * Implement AJAX request queueing. IE does not handle AJAX request concurrency | ||
167 | * very well and will freeze the entire browser interface if more than 2 calls | ||
168 | * are queued at a single time. We implement a global request queue and a queue | ||
169 | * runner that handles concurrent AJAX requests. The code is pretty straight | ||
170 | * forward but it also depends on modifications made to Ajax.Request.prototype. | ||
171 | */ | ||
172 | Object.extend(Ajax, | ||
173 | { | ||
174 | // Global AJAX Request Queue | ||
175 | requestQueue: [], | ||
176 | |||
177 | |||
178 | /* | ||
179 | * Process events in the request queue. | ||
180 | */ | ||
181 | _runQueue: function() | ||
182 | { | ||
183 | if (this.activeRequestCount > 1) | ||
184 | { | ||
185 | // Too many requests so we will try again later | ||
186 | new PeriodicalExecuter(function(executer) | ||
187 | { | ||
188 | this._runQueue(); | ||
189 | executer.stop(); | ||
190 | }.bind(this), 0.1); | ||
191 | |||
192 | return; | ||
193 | } | ||
194 | |||
195 | // Run the first request in the queue | ||
196 | var currentRequest = this.requestQueue.shift(); | ||
197 | currentRequest.request(currentRequest.url); | ||
198 | } | ||
199 | }); \ No newline at end of file | ||
diff --git a/docroot/custom_content/2col.css b/docroot/custom_content/2col.css new file mode 100755 index 0000000..4e368c2 --- /dev/null +++ b/docroot/custom_content/2col.css | |||
@@ -0,0 +1,68 @@ | |||
1 | /* | ||
2 | * Material Experience - 2 Column Custom Content Base Styles | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 10/21/07 | ||
7 | */ | ||
8 | |||
9 | body | ||
10 | { | ||
11 | font-family: Verdana, sans-serif; | ||
12 | font-size: 11px; | ||
13 | margin: 10px; | ||
14 | padding: 0px; | ||
15 | } | ||
16 | |||
17 | ul#navigation | ||
18 | { | ||
19 | margin: 0px; | ||
20 | padding: 0px; | ||
21 | } | ||
22 | |||
23 | ul#navigation li | ||
24 | { | ||
25 | margin: 5px 30px 0px 0px; | ||
26 | list-style: none; | ||
27 | font-weight: bolder; | ||
28 | border: 1px solid white; | ||
29 | } | ||
30 | |||
31 | ul#navigation li a | ||
32 | { | ||
33 | display: block; | ||
34 | text-decoration: none; | ||
35 | padding: 2px; | ||
36 | border: 3px solid black; | ||
37 | } | ||
38 | |||
39 | ul#navigation li ul li a | ||
40 | { | ||
41 | margin: 0px -60px 0px -30px; | ||
42 | font-weight: normal; | ||
43 | } | ||
44 | |||
45 | .selected | ||
46 | { | ||
47 | background: black; | ||
48 | color: white; | ||
49 | padding: 5px; | ||
50 | } | ||
51 | |||
52 | .narrowCol, .wideCol | ||
53 | { | ||
54 | float: left; | ||
55 | height: 460px; | ||
56 | overflow: hidden; | ||
57 | } | ||
58 | |||
59 | .narrowCol | ||
60 | { | ||
61 | width: 290px; | ||
62 | margin-right: 20px; | ||
63 | } | ||
64 | |||
65 | .wideCol | ||
66 | { | ||
67 | width: 569px; | ||
68 | } | ||
diff --git a/docroot/custom_content/2col.js b/docroot/custom_content/2col.js new file mode 100755 index 0000000..3a91e4d --- /dev/null +++ b/docroot/custom_content/2col.js | |||
@@ -0,0 +1,170 @@ | |||
1 | /* | ||
2 | * Material Experience - 2 Column Custom Content Script | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 10/21/07 | ||
7 | */ | ||
8 | |||
9 | // Tracks the last ID requested | ||
10 | var lastID = null; | ||
11 | |||
12 | // Track the currently open nav item | ||
13 | var curr = null; | ||
14 | |||
15 | document.write('<script type="text/javascript" src="http://materialexperience.santoprene.com/lib/scriptaculous/scriptaculous.js"></script>'); | ||
16 | |||
17 | function doLoad() | ||
18 | { | ||
19 | // Hide everything first | ||
20 | $$('ul#navigation li ul').each(function(item) | ||
21 | { | ||
22 | item.setStyle({ display: 'none' }); | ||
23 | }); | ||
24 | |||
25 | // Then attach the onclick events | ||
26 | $$('ul#navigation li').each(function(item) | ||
27 | { | ||
28 | Event.observe(item, 'click', function(event) | ||
29 | { | ||
30 | // Hide the currently opened item if it is clicked again | ||
31 | // per BS. | ||
32 | if (curr && item == curr) | ||
33 | { | ||
34 | curr.down('ul').hide(); | ||
35 | item.setStyle({ borderLeft: '0px' }); | ||
36 | curr = null; | ||
37 | return; | ||
38 | } | ||
39 | |||
40 | curr = item; | ||
41 | |||
42 | // Hide everything | ||
43 | $$('ul#navigation li ul').each(function(item) | ||
44 | { | ||
45 | item.setStyle({ display: 'none' }); | ||
46 | }); | ||
47 | |||
48 | // Clear out the borders | ||
49 | $$('ul#navigation li').each(function(item) | ||
50 | { | ||
51 | item.setStyle({ borderLeft: '' }); | ||
52 | }); | ||
53 | |||
54 | // Figure out what color we SHOULD be, this will change | ||
55 | // depending on the card color we are on so just rely | ||
56 | // on the designer to communicate through the CSS, | ||
57 | // probably not wise relying on a designer for code | ||
58 | // but alas... | ||
59 | var color = $$('ul#navigation li a')[0].getStyle('background-color'); | ||
60 | |||
61 | // Catch and execute or just leave it lie | ||
62 | try | ||
63 | { | ||
64 | if (item.down('ul').style.display != 'none') | ||
65 | { | ||
66 | item.down('ul').hide(); | ||
67 | Event.stop(event); | ||
68 | return; | ||
69 | } | ||
70 | |||
71 | item.down('ul').show(); | ||
72 | item.setStyle({ borderLeft: '3px solid ' + color }); | ||
73 | Event.stop(event); | ||
74 | } | ||
75 | catch (e) | ||
76 | { | ||
77 | // Just let the link do its thing | ||
78 | } | ||
79 | }); | ||
80 | }); | ||
81 | } | ||
82 | |||
83 | function locateContent(contID) | ||
84 | { | ||
85 | // Hide the last ID from a global variable because going over each | ||
86 | // item and hiding it could take a prohibitively long time on a big | ||
87 | // page and we will most definitely be dealing with big pages here. | ||
88 | if (lastID) | ||
89 | { | ||
90 | $(lastID).className = ""; | ||
91 | } | ||
92 | |||
93 | // Track the requested content ID | ||
94 | lastID = contID; | ||
95 | |||
96 | // The scroll top but subtract the height of the item itself or we | ||
97 | // always scroll to the bottom of items. | ||
98 | $('test').scrollTop = $(contID).offsetTop - $(contID).getHeight(); | ||
99 | $(contID).className = "selected"; | ||
100 | } | ||
101 | |||
102 | /* | ||
103 | * Create custom scroll bars for an HTML element. | ||
104 | */ | ||
105 | function scrollify(div) | ||
106 | { | ||
107 | // Inherit the color from a link | ||
108 | var color = $$('ul#navigation li a')[0].getStyle('background-color'); | ||
109 | |||
110 | // Create the scroll container and draggable widget | ||
111 | document.body.appendChild( | ||
112 | this.scrollCont = Builder.node('div', | ||
113 | { | ||
114 | style: 'border-left: 1px solid ' + color + ';' + | ||
115 | 'width: 1px; position: absolute; ' + | ||
116 | 'height:' + div.offsetHeight + 'px; ' + | ||
117 | 'top: 10px;' + 'left: ' + (div.offsetLeft + div.offsetWidth + 12) + 'px;' | ||
118 | }, | ||
119 | |||
120 | this.scrollBar = Builder.node('img', | ||
121 | { | ||
122 | src: 'http://materialexperience.santoprene.com/images/pill.gif', | ||
123 | style: 'display: block; margin-left: -3px; cursor: move; ' + | ||
124 | 'background: ' + color + '; padding: 0px;' | ||
125 | }) | ||
126 | )); | ||
127 | |||
128 | // Create the scroller | ||
129 | this.scroller = new Control.Slider(this.scrollBar, this.scrollCont, | ||
130 | { | ||
131 | axis: 'vertical', | ||
132 | range: $R(0, div.scrollHeight), | ||
133 | |||
134 | onSlide: function(value) | ||
135 | { | ||
136 | div.scrollTop = Math.floor(value); | ||
137 | }.bind(this) | ||
138 | }); | ||
139 | |||
140 | new PeriodicalExecuter(function() | ||
141 | { | ||
142 | // Update the scroller range when the contents change | ||
143 | this.scroller.range = $R(0, div.scrollHeight); | ||
144 | |||
145 | // Move the scroller when the scrolled element changes | ||
146 | // (e.g. linking down into the page). | ||
147 | if (this.scroller.value != div.scrollTop) | ||
148 | { | ||
149 | this.scroller.setValue(div.scrollTop); | ||
150 | } | ||
151 | }.bind(this), 1); | ||
152 | } | ||
153 | |||
154 | /* | ||
155 | * Attach scrollbars to the narrow column and the wide column if they exist. | ||
156 | */ | ||
157 | Event.observe(window, 'load', function() | ||
158 | { | ||
159 | if ($$('.narrowCol')[0]) | ||
160 | { | ||
161 | new scrollify($$('.narrowCol')[0]); | ||
162 | } | ||
163 | |||
164 | if ($$('.wideCol')[0]) | ||
165 | { | ||
166 | new scrollify($$('.wideCol')[0]); | ||
167 | } | ||
168 | }); | ||
169 | |||
170 | Event.observe(window, 'load', doLoad); \ No newline at end of file | ||
diff --git a/docroot/data/card_tables.js b/docroot/data/card_tables.js new file mode 100755 index 0000000..6b33ed7 --- /dev/null +++ b/docroot/data/card_tables.js | |||
@@ -0,0 +1,63 @@ | |||
1 | /* | ||
2 | * Material Experience - Card Tables Data File | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | * | ||
8 | * Data file for the card tables. This allows us the flexibility | ||
9 | * to add or remove tables at a later date. Note that each table | ||
10 | * id should correspond to a compose card type ID otherwise | ||
11 | * no data will be loaded. | ||
12 | */ | ||
13 | |||
14 | [ | ||
15 | { | ||
16 | tid: 'home', | ||
17 | name: 'Home', | ||
18 | color: SME.colors.grey, | ||
19 | decorate: true | ||
20 | }, | ||
21 | |||
22 | { | ||
23 | tid: 'inspiration', | ||
24 | name: 'Inspiration', | ||
25 | color: SME.colors.brown, | ||
26 | decorate: true | ||
27 | }, | ||
28 | |||
29 | { | ||
30 | tid: 'exploration', | ||
31 | name: 'Exploration', | ||
32 | color: SME.colors.red, | ||
33 | decorate: true | ||
34 | }, | ||
35 | |||
36 | { | ||
37 | tid: 'close-up', | ||
38 | name: 'Close-Up', | ||
39 | color: SME.colors.orange, | ||
40 | decorate: true | ||
41 | }, | ||
42 | |||
43 | { | ||
44 | tid: 'making_it', | ||
45 | name: 'Making It', | ||
46 | color: SME.colors.blue, | ||
47 | decorate: true | ||
48 | }, | ||
49 | |||
50 | { | ||
51 | tid: 'info-samples', | ||
52 | name: 'Info & Samples', | ||
53 | color: SME.colors.green, | ||
54 | decorate: true | ||
55 | }, | ||
56 | |||
57 | { | ||
58 | tid: 'preview', | ||
59 | name: 'Preview', | ||
60 | color: SME.colors.grey, | ||
61 | decorate: false | ||
62 | } | ||
63 | ] \ No newline at end of file | ||
diff --git a/docroot/data/persist_chips.js b/docroot/data/persist_chips.js new file mode 100755 index 0000000..84c3e59 --- /dev/null +++ b/docroot/data/persist_chips.js | |||
@@ -0,0 +1,3 @@ | |||
1 | [ | ||
2 | |||
3 | ] \ No newline at end of file | ||
diff --git a/docroot/data/strings.en.js b/docroot/data/strings.en.js new file mode 100755 index 0000000..c9a1faf --- /dev/null +++ b/docroot/data/strings.en.js | |||
@@ -0,0 +1,122 @@ | |||
1 | /* | ||
2 | * Material Experience - English Strings File | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | * | ||
8 | * This strings file is the foundation for internationalization of the | ||
9 | * Material Experience website. These strings can be changed to anything | ||
10 | * and those changes will be reflected in the interface. Be careful, you | ||
11 | * can also potentially seriously break things. Follow the rules and | ||
12 | * comments. | ||
13 | * | ||
14 | * Note that as of this writing full internationalization is not fully | ||
15 | * implemented and won't be till the client requests it, I anticipate | ||
16 | * this happening some day so here we are with a strings file. | ||
17 | * | ||
18 | * Rules: | ||
19 | * Quoting -- if you need to use quotes, either single or double make sure | ||
20 | * you vary your quoting, 'this won't' work you need "this'll work" | ||
21 | * | ||
22 | * HTML -- some strings can contain HTML, don't go crazy, constrain your | ||
23 | * HTML to line breaks or you will breaks and maybe images or | ||
24 | * you will break things. | ||
25 | * | ||
26 | * Doubt -- if your not sure what effect your changes will have ASK SOMEBODY | ||
27 | * don't just make changes and hope things work | ||
28 | * | ||
29 | * Unicode -- sure, you can use unicode, just make sure you save the file with | ||
30 | * the appropriate character set. No BOM please. | ||
31 | * | ||
32 | * Comment -- leave the comments alone, and change the updated by comment at the | ||
33 | * top of this file when you make changes. | ||
34 | */ | ||
35 | |||
36 | var Strings = | ||
37 | { | ||
38 | // | ||
39 | // LANGUAGE FILE METADATA | ||
40 | // | ||
41 | // Language of the String File and version of the site this is for | ||
42 | // Neither of these are used yet but set them anyhow since they | ||
43 | // may be used later on. Use C-style locale codes for the language. | ||
44 | // | ||
45 | // Stars are perfectly acceptable in the version field but will only | ||
46 | // be matched to 3 places. | ||
47 | language : 'en_us', | ||
48 | version : '1.*', | ||
49 | |||
50 | // | ||
51 | // APPLICATION TITLE | ||
52 | // | ||
53 | // Displayed in the browser title bar | ||
54 | appTitle: 'Material Experience', | ||
55 | |||
56 | // | ||
57 | // WINDOW SIZE ERROR | ||
58 | // | ||
59 | // Displayed once when the window size is smaller than the minimum size | ||
60 | windowSize : "Your window size is smaller than the recommended 1024x768 resolution.<br/>" + | ||
61 | "Some items may be positioned outside the viewable area.", | ||
62 | |||
63 | // | ||
64 | // DATA LOADERS | ||
65 | // | ||
66 | // Displayed whenever an AJAX call is made and while it is waiting to load | ||
67 | loadingNoAnim : "Loading Data...", | ||
68 | loadingAnim : "Loading Data <img src='images/loader.gif'/>", | ||
69 | |||
70 | // | ||
71 | // PRINTING ERRORS | ||
72 | // | ||
73 | cantPrint : "Sorry Can't Print", // Displayed if the browser doesn't support printing | ||
74 | printClose : "Do you want to close this window?", // Displayed before we auto/close the print window | ||
75 | |||
76 | // | ||
77 | // CARD DATA ERRORS | ||
78 | // | ||
79 | // Displayed within a card when it fails to load correctly | ||
80 | cardErrorTitle : "Card Error", | ||
81 | cardErrorText : "We're very sorry, there was an error loading this card. Please try again later.", | ||
82 | |||
83 | // | ||
84 | // AJAX ERRORS | ||
85 | // | ||
86 | // Displayed when an AJAX call fails. | ||
87 | ajaxError : "Some Data Could Not Be Loaded.<br/> We're sorry, please refresh this page.", | ||
88 | |||
89 | // | ||
90 | // CARD BUTTON LABELS | ||
91 | // | ||
92 | // Labels shown next to the buttons in the card | ||
93 | addToSketchbook : "Add to Sketchbook", | ||
94 | sendToFriend : "Send to a Friend", | ||
95 | printCard : "Print this Card", | ||
96 | myHistory : "My History", | ||
97 | closeCard : "Close Card", | ||
98 | |||
99 | // | ||
100 | // CARD CONTENT | ||
101 | // | ||
102 | // Static content within the card that is hard-coded | ||
103 | moreInfo : "More Information", | ||
104 | skipButton : "Skip this Card", | ||
105 | |||
106 | // | ||
107 | // SKETCHBOOK ERRORS AND CONFIRMATIONS | ||
108 | // | ||
109 | // Confirmations and errors for sketchbook actions | ||
110 | sketchbook : "Sketchbook", | ||
111 | alreadyAddedSB : "Card already exists in your sketchbook.", | ||
112 | addedSketchbook : "Added to Sketchbook", | ||
113 | addedGuestSB : "Added to Guest Sketchbook<br/>Please visit the sketchbook to log in and save your items.", | ||
114 | |||
115 | // | ||
116 | // UTILITY BOX LINKS | ||
117 | // | ||
118 | // Links at the top right of the interface. | ||
119 | pleaseLogin : "Please Log In", | ||
120 | manageAccount : "Manage My Account", | ||
121 | logout : "Log Out" | ||
122 | }; \ No newline at end of file | ||
diff --git a/docroot/favicon.ico b/docroot/favicon.ico new file mode 100755 index 0000000..32168fd --- /dev/null +++ b/docroot/favicon.ico | |||
Binary files differ | |||
diff --git a/docroot/graphics/chip.psd b/docroot/graphics/chip.psd new file mode 100755 index 0000000..0f0828e --- /dev/null +++ b/docroot/graphics/chip.psd | |||
Binary files differ | |||
diff --git a/docroot/images/arrow_down.gif b/docroot/images/arrow_down.gif new file mode 100755 index 0000000..7395d65 --- /dev/null +++ b/docroot/images/arrow_down.gif | |||
Binary files differ | |||
diff --git a/docroot/images/arrow_right_grey.gif b/docroot/images/arrow_right_grey.gif new file mode 100755 index 0000000..bd0a087 --- /dev/null +++ b/docroot/images/arrow_right_grey.gif | |||
Binary files differ | |||
diff --git a/docroot/images/blank.gif b/docroot/images/blank.gif new file mode 100755 index 0000000..75b945d --- /dev/null +++ b/docroot/images/blank.gif | |||
Binary files differ | |||
diff --git a/docroot/images/close.gif b/docroot/images/close.gif new file mode 100755 index 0000000..b19fbac --- /dev/null +++ b/docroot/images/close.gif | |||
Binary files differ | |||
diff --git a/docroot/images/email.gif b/docroot/images/email.gif new file mode 100755 index 0000000..5d18b3c --- /dev/null +++ b/docroot/images/email.gif | |||
Binary files differ | |||
diff --git a/docroot/images/history.gif b/docroot/images/history.gif new file mode 100755 index 0000000..48347c0 --- /dev/null +++ b/docroot/images/history.gif | |||
Binary files differ | |||
diff --git a/docroot/images/loader.gif b/docroot/images/loader.gif new file mode 100755 index 0000000..cb91da8 --- /dev/null +++ b/docroot/images/loader.gif | |||
Binary files differ | |||
diff --git a/docroot/images/logo.gif b/docroot/images/logo.gif new file mode 100755 index 0000000..7e8779b --- /dev/null +++ b/docroot/images/logo.gif | |||
Binary files differ | |||
diff --git a/docroot/images/pill.gif b/docroot/images/pill.gif new file mode 100755 index 0000000..cc96b81 --- /dev/null +++ b/docroot/images/pill.gif | |||
Binary files differ | |||
diff --git a/docroot/images/plus.gif b/docroot/images/plus.gif new file mode 100755 index 0000000..95eca20 --- /dev/null +++ b/docroot/images/plus.gif | |||
Binary files differ | |||
diff --git a/docroot/images/print.gif b/docroot/images/print.gif new file mode 100755 index 0000000..3668f6a --- /dev/null +++ b/docroot/images/print.gif | |||
Binary files differ | |||
diff --git a/docroot/images/tagline.png b/docroot/images/tagline.png new file mode 100755 index 0000000..bda7a1d --- /dev/null +++ b/docroot/images/tagline.png | |||
Binary files differ | |||
diff --git a/docroot/images/trash_can.jpg b/docroot/images/trash_can.jpg new file mode 100755 index 0000000..5801655 --- /dev/null +++ b/docroot/images/trash_can.jpg | |||
Binary files differ | |||
diff --git a/docroot/index.html b/docroot/index.html new file mode 100755 index 0000000..d14eedc --- /dev/null +++ b/docroot/index.html | |||
@@ -0,0 +1,45 @@ | |||
1 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
2 | <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> | ||
3 | <!-- | ||
4 | * Material Experience - Index Page | ||
5 | * | ||
6 | * EYEMG - Interactive Media Group | ||
7 | * Created by Mike Crute (mcrute@eyemg.com) | ||
8 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
9 | --> | ||
10 | <head> | ||
11 | <title>Material Experience</title> | ||
12 | <style type="text/css">@import url('application.css');</style> | ||
13 | </head> | ||
14 | |||
15 | <body> | ||
16 | <img src="blank.gif" alt="Loading" style="display: none" /> | ||
17 | |||
18 | <div id="header"> | ||
19 | <a href="#tablehome"><img id="logo" src="images/logo.gif" alt="Material Experience Logo" title="Material Experience Logo" /></a> | ||
20 | <img src="images/tagline.png" id="tagline" alt="An ExxonMobil Chemical Initiative" title="An ExxonMobil Chemical Initiative" /> | ||
21 | <div class="history" style="display: none;"> | ||
22 | <a id="loginLink" href="#">Log In/Register</a> | | ||
23 | <a href="#" class="history">My Card History</a> | ||
24 | </div> | ||
25 | </div> | ||
26 | |||
27 | <noscript>It would appear that your browser does not have Javascript support. You will need to enable Javascript or upgrade your browser in order to view this site.</noscript> | ||
28 | <div style="display: none">It would appear that your browser doesn't have CSS support. You will need to enable CSS or upgrade your browser in order to view this site.</div> | ||
29 | |||
30 | <ul id="history"></ul> | ||
31 | |||
32 | <div id="footer"> | ||
33 | <p id="version"></p> | ||
34 | <p id="copyright"> | ||
35 | © #### Advanced Elastomer Systems, LP, an ExxonMobil Chemical affiliate. | ||
36 | <a href="http://www.santoprene.com/Privacy" target="_blank">Privacy</a>, | ||
37 | <a href="http://www.santoprene.com/Terms" target="_blank">Terms</a> and | ||
38 | <a href="http://www.santoprene.com/Legal" target="_blank">Legal</a> | ||
39 | </p> | ||
40 | </div> | ||
41 | <a href="http://www.santoprene.com/cms/designer_seo/d79655481a900999/index.html" style="display: none">Search Engine Version</a> | ||
42 | |||
43 | <script type="text/javascript" src="application.js"></script> | ||
44 | </body> | ||
45 | </html> | ||
diff --git a/docroot/lib/firebug/errorIcon.png b/docroot/lib/firebug/errorIcon.png new file mode 100755 index 0000000..2d75261 --- /dev/null +++ b/docroot/lib/firebug/errorIcon.png | |||
Binary files differ | |||
diff --git a/docroot/lib/firebug/firebug.css b/docroot/lib/firebug/firebug.css new file mode 100755 index 0000000..1f041c4 --- /dev/null +++ b/docroot/lib/firebug/firebug.css | |||
@@ -0,0 +1,209 @@ | |||
1 | |||
2 | html, body { | ||
3 | margin: 0; | ||
4 | background: #FFFFFF; | ||
5 | font-family: Lucida Grande, Tahoma, sans-serif; | ||
6 | font-size: 11px; | ||
7 | overflow: hidden; | ||
8 | } | ||
9 | |||
10 | a { | ||
11 | text-decoration: none; | ||
12 | } | ||
13 | |||
14 | a:hover { | ||
15 | text-decoration: underline; | ||
16 | } | ||
17 | |||
18 | .toolbar { | ||
19 | height: 14px; | ||
20 | border-top: 1px solid ThreeDHighlight; | ||
21 | border-bottom: 1px solid ThreeDShadow; | ||
22 | padding: 2px 6px; | ||
23 | background: ThreeDFace; | ||
24 | } | ||
25 | |||
26 | .toolbarRight { | ||
27 | position: absolute; | ||
28 | top: 4px; | ||
29 | right: 6px; | ||
30 | } | ||
31 | |||
32 | #log { | ||
33 | overflow: auto; | ||
34 | position: absolute; | ||
35 | left: 0; | ||
36 | width: 100%; | ||
37 | } | ||
38 | |||
39 | #commandLine { | ||
40 | position: absolute; | ||
41 | bottom: 0; | ||
42 | left: 0; | ||
43 | width: 100%; | ||
44 | height: 18px; | ||
45 | border: none; | ||
46 | border-top: 1px solid ThreeDShadow; | ||
47 | } | ||
48 | |||
49 | /************************************************************************************************/ | ||
50 | |||
51 | .logRow { | ||
52 | position: relative; | ||
53 | border-bottom: 1px solid #D7D7D7; | ||
54 | padding: 2px 4px 1px 6px; | ||
55 | background-color: #FFFFFF; | ||
56 | } | ||
57 | |||
58 | .logRow-command { | ||
59 | font-family: Monaco, monospace; | ||
60 | color: blue; | ||
61 | } | ||
62 | |||
63 | .objectBox-null { | ||
64 | padding: 0 2px; | ||
65 | border: 1px solid #666666; | ||
66 | background-color: #888888; | ||
67 | color: #FFFFFF; | ||
68 | } | ||
69 | |||
70 | .objectBox-string { | ||
71 | font-family: Monaco, monospace; | ||
72 | color: red; | ||
73 | white-space: pre; | ||
74 | } | ||
75 | |||
76 | .objectBox-number { | ||
77 | color: #000088; | ||
78 | } | ||
79 | |||
80 | .objectBox-function { | ||
81 | font-family: Monaco, monospace; | ||
82 | color: DarkGreen; | ||
83 | } | ||
84 | |||
85 | .objectBox-object { | ||
86 | color: DarkGreen; | ||
87 | font-weight: bold; | ||
88 | } | ||
89 | |||
90 | /************************************************************************************************/ | ||
91 | |||
92 | .logRow-info, | ||
93 | .logRow-error, | ||
94 | .logRow-warning { | ||
95 | background: #FFFFFF no-repeat 2px 2px; | ||
96 | padding-left: 20px; | ||
97 | padding-bottom: 3px; | ||
98 | } | ||
99 | |||
100 | .logRow-info { | ||
101 | background-image: url(infoIcon.png); | ||
102 | } | ||
103 | |||
104 | .logRow-warning { | ||
105 | background-color: cyan; | ||
106 | background-image: url(warningIcon.png); | ||
107 | } | ||
108 | |||
109 | .logRow-error { | ||
110 | background-color: LightYellow; | ||
111 | background-image: url(errorIcon.png); | ||
112 | } | ||
113 | |||
114 | .errorMessage { | ||
115 | vertical-align: top; | ||
116 | color: #FF0000; | ||
117 | } | ||
118 | |||
119 | .objectBox-sourceLink { | ||
120 | position: absolute; | ||
121 | right: 4px; | ||
122 | top: 2px; | ||
123 | padding-left: 8px; | ||
124 | font-family: Lucida Grande, sans-serif; | ||
125 | font-weight: bold; | ||
126 | color: #0000FF; | ||
127 | } | ||
128 | |||
129 | /************************************************************************************************/ | ||
130 | |||
131 | .logRow-group { | ||
132 | background: #EEEEEE; | ||
133 | border-bottom: none; | ||
134 | } | ||
135 | |||
136 | .logGroup { | ||
137 | background: #EEEEEE; | ||
138 | } | ||
139 | |||
140 | .logGroupBox { | ||
141 | margin-left: 24px; | ||
142 | border-top: 1px solid #D7D7D7; | ||
143 | border-left: 1px solid #D7D7D7; | ||
144 | } | ||
145 | |||
146 | /************************************************************************************************/ | ||
147 | |||
148 | .selectorTag, | ||
149 | .selectorId, | ||
150 | .selectorClass { | ||
151 | font-family: Monaco, monospace; | ||
152 | font-weight: normal; | ||
153 | } | ||
154 | |||
155 | .selectorTag { | ||
156 | color: #0000FF; | ||
157 | } | ||
158 | |||
159 | .selectorId { | ||
160 | color: DarkBlue; | ||
161 | } | ||
162 | |||
163 | .selectorClass { | ||
164 | color: red; | ||
165 | } | ||
166 | |||
167 | /************************************************************************************************/ | ||
168 | |||
169 | .objectBox-element { | ||
170 | font-family: Monaco, monospace; | ||
171 | color: #000088; | ||
172 | } | ||
173 | |||
174 | .nodeChildren { | ||
175 | margin-left: 16px; | ||
176 | } | ||
177 | |||
178 | .nodeTag { | ||
179 | color: blue; | ||
180 | } | ||
181 | |||
182 | .nodeValue { | ||
183 | color: #FF0000; | ||
184 | font-weight: normal; | ||
185 | } | ||
186 | |||
187 | .nodeText, | ||
188 | .nodeComment { | ||
189 | margin: 0 2px; | ||
190 | vertical-align: top; | ||
191 | } | ||
192 | |||
193 | .nodeText { | ||
194 | color: #333333; | ||
195 | } | ||
196 | |||
197 | .nodeComment { | ||
198 | color: DarkGreen; | ||
199 | } | ||
200 | |||
201 | /************************************************************************************************/ | ||
202 | |||
203 | .propertyNameCell { | ||
204 | vertical-align: top; | ||
205 | } | ||
206 | |||
207 | .propertyName { | ||
208 | font-weight: bold; | ||
209 | } | ||
diff --git a/docroot/lib/firebug/firebug.html b/docroot/lib/firebug/firebug.html new file mode 100755 index 0000000..861e639 --- /dev/null +++ b/docroot/lib/firebug/firebug.html | |||
@@ -0,0 +1,23 @@ | |||
1 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" | ||
2 | "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
3 | |||
4 | <html xmlns="http://www.w3.org/1999/xhtml"> | ||
5 | |||
6 | <head> | ||
7 | <title>Firebug</title> | ||
8 | <link rel="stylesheet" type="text/css" href="firebug.css"> | ||
9 | </head> | ||
10 | |||
11 | <body> | ||
12 | <div id="toolbar" class="toolbar"> | ||
13 | <a href="#" onclick="parent.console.clear()">Clear</a> | ||
14 | <span class="toolbarRight"> | ||
15 | <a href="#" onclick="parent.console.close()">Close</a> | ||
16 | </span> | ||
17 | </div> | ||
18 | <div id="log"></div> | ||
19 | <input type="text" id="commandLine"> | ||
20 | |||
21 | <script>parent.onFirebugReady(document);</script> | ||
22 | </body> | ||
23 | </html> | ||
diff --git a/docroot/lib/firebug/firebug.js b/docroot/lib/firebug/firebug.js new file mode 100755 index 0000000..eb853b8 --- /dev/null +++ b/docroot/lib/firebug/firebug.js | |||
@@ -0,0 +1,672 @@ | |||
1 | |||
2 | if (!("console" in window) || !("firebug" in console)) { | ||
3 | (function() | ||
4 | { | ||
5 | window.console = | ||
6 | { | ||
7 | log: function() | ||
8 | { | ||
9 | logFormatted(arguments, ""); | ||
10 | }, | ||
11 | |||
12 | debug: function() | ||
13 | { | ||
14 | logFormatted(arguments, "debug"); | ||
15 | }, | ||
16 | |||
17 | info: function() | ||
18 | { | ||
19 | logFormatted(arguments, "info"); | ||
20 | }, | ||
21 | |||
22 | warn: function() | ||
23 | { | ||
24 | logFormatted(arguments, "warning"); | ||
25 | }, | ||
26 | |||
27 | error: function() | ||
28 | { | ||
29 | logFormatted(arguments, "error"); | ||
30 | }, | ||
31 | |||
32 | assert: function(truth, message) | ||
33 | { | ||
34 | if (!truth) | ||
35 | { | ||
36 | var args = []; | ||
37 | for (var i = 1; i < arguments.length; ++i) | ||
38 | args.push(arguments[i]); | ||
39 | |||
40 | logFormatted(args.length ? args : ["Assertion Failure"], "error"); | ||
41 | throw message ? message : "Assertion Failure"; | ||
42 | } | ||
43 | }, | ||
44 | |||
45 | dir: function(object) | ||
46 | { | ||
47 | var html = []; | ||
48 | |||
49 | var pairs = []; | ||
50 | for (var name in object) | ||
51 | { | ||
52 | try | ||
53 | { | ||
54 | pairs.push([name, object[name]]); | ||
55 | } | ||
56 | catch (exc) | ||
57 | { | ||
58 | } | ||
59 | } | ||
60 | |||
61 | pairs.sort(function(a, b) { return a[0] < b[0] ? -1 : 1; }); | ||
62 | |||
63 | html.push('<table>'); | ||
64 | for (var i = 0; i < pairs.length; ++i) | ||
65 | { | ||
66 | var name = pairs[i][0], value = pairs[i][1]; | ||
67 | |||
68 | html.push('<tr>', | ||
69 | '<td class="propertyNameCell"><span class="propertyName">', | ||
70 | escapeHTML(name), '</span></td>', '<td><span class="propertyValue">'); | ||
71 | appendObject(value, html); | ||
72 | html.push('</span></td></tr>'); | ||
73 | } | ||
74 | html.push('</table>'); | ||
75 | |||
76 | logRow(html, "dir"); | ||
77 | }, | ||
78 | |||
79 | dirxml: function(node) | ||
80 | { | ||
81 | var html = []; | ||
82 | |||
83 | appendNode(node, html); | ||
84 | logRow(html, "dirxml"); | ||
85 | }, | ||
86 | |||
87 | group: function() | ||
88 | { | ||
89 | logRow(arguments, "group", pushGroup); | ||
90 | }, | ||
91 | |||
92 | groupEnd: function() | ||
93 | { | ||
94 | logRow(arguments, "", popGroup); | ||
95 | }, | ||
96 | |||
97 | time: function(name) | ||
98 | { | ||
99 | timeMap[name] = (new Date()).getTime(); | ||
100 | }, | ||
101 | |||
102 | timeEnd: function(name) | ||
103 | { | ||
104 | if (name in timeMap) | ||
105 | { | ||
106 | var delta = (new Date()).getTime() - timeMap[name]; | ||
107 | logFormatted([name+ ":", delta+"ms"]); | ||
108 | delete timeMap[name]; | ||
109 | } | ||
110 | }, | ||
111 | |||
112 | count: function() | ||
113 | { | ||
114 | this.warn(["count() not supported."]); | ||
115 | }, | ||
116 | |||
117 | trace: function() | ||
118 | { | ||
119 | this.warn(["trace() not supported."]); | ||
120 | }, | ||
121 | |||
122 | profile: function() | ||
123 | { | ||
124 | this.warn(["profile() not supported."]); | ||
125 | }, | ||
126 | |||
127 | profileEnd: function() | ||
128 | { | ||
129 | }, | ||
130 | |||
131 | clear: function() | ||
132 | { | ||
133 | consoleBody.innerHTML = ""; | ||
134 | }, | ||
135 | |||
136 | open: function() | ||
137 | { | ||
138 | toggleConsole(true); | ||
139 | }, | ||
140 | |||
141 | close: function() | ||
142 | { | ||
143 | if (frameVisible) | ||
144 | toggleConsole(); | ||
145 | } | ||
146 | }; | ||
147 | |||
148 | // ******************************************************************************************** | ||
149 | |||
150 | var consoleFrame = null; | ||
151 | var consoleBody = null; | ||
152 | var commandLine = null; | ||
153 | |||
154 | var frameVisible = false; | ||
155 | var messageQueue = []; | ||
156 | var groupStack = []; | ||
157 | var timeMap = {}; | ||
158 | |||
159 | var clPrefix = ">>> "; | ||
160 | |||
161 | var isFirefox = navigator.userAgent.indexOf("Firefox") != -1; | ||
162 | var isIE = navigator.userAgent.indexOf("MSIE") != -1; | ||
163 | var isOpera = navigator.userAgent.indexOf("Opera") != -1; | ||
164 | var isSafari = navigator.userAgent.indexOf("AppleWebKit") != -1; | ||
165 | |||
166 | // ******************************************************************************************** | ||
167 | |||
168 | function toggleConsole(forceOpen) | ||
169 | { | ||
170 | frameVisible = forceOpen || !frameVisible; | ||
171 | if (consoleFrame) | ||
172 | consoleFrame.style.visibility = frameVisible ? "visible" : "hidden"; | ||
173 | else | ||
174 | waitForBody(); | ||
175 | } | ||
176 | |||
177 | function focusCommandLine() | ||
178 | { | ||
179 | toggleConsole(true); | ||
180 | if (commandLine) | ||
181 | commandLine.focus(); | ||
182 | } | ||
183 | |||
184 | function waitForBody() | ||
185 | { | ||
186 | if (document.body) | ||
187 | createFrame(); | ||
188 | else | ||
189 | setTimeout(waitForBody, 200); | ||
190 | } | ||
191 | |||
192 | function createFrame() | ||
193 | { | ||
194 | if (consoleFrame) | ||
195 | return; | ||
196 | |||
197 | window.onFirebugReady = function(doc) | ||
198 | { | ||
199 | window.onFirebugReady = null; | ||
200 | |||
201 | var toolbar = doc.getElementById("toolbar"); | ||
202 | toolbar.onmousedown = onSplitterMouseDown; | ||
203 | |||
204 | commandLine = doc.getElementById("commandLine"); | ||
205 | addEvent(commandLine, "keydown", onCommandLineKeyDown); | ||
206 | |||
207 | addEvent(doc, isIE || isSafari ? "keydown" : "keypress", onKeyDown); | ||
208 | |||
209 | consoleBody = doc.getElementById("log"); | ||
210 | layout(); | ||
211 | flush(); | ||
212 | } | ||
213 | |||
214 | var baseURL = getFirebugURL(); | ||
215 | |||
216 | consoleFrame = document.createElement("iframe"); | ||
217 | consoleFrame.setAttribute("src", baseURL+"/firebug.html"); | ||
218 | consoleFrame.setAttribute("frameBorder", "0"); | ||
219 | consoleFrame.style.visibility = (frameVisible ? "visible" : "hidden"); | ||
220 | consoleFrame.style.zIndex = "2147483647"; | ||
221 | consoleFrame.style.position = "fixed"; | ||
222 | consoleFrame.style.width = "100%"; | ||
223 | consoleFrame.style.left = "0"; | ||
224 | consoleFrame.style.bottom = "0"; | ||
225 | consoleFrame.style.height = "200px"; | ||
226 | document.body.appendChild(consoleFrame); | ||
227 | } | ||
228 | |||
229 | function getFirebugURL() | ||
230 | { | ||
231 | var scripts = document.getElementsByTagName("script"); | ||
232 | for (var i = 0; i < scripts.length; ++i) | ||
233 | { | ||
234 | if (scripts[i].src.indexOf("firebug.js") != -1) | ||
235 | { | ||
236 | var lastSlash = scripts[i].src.lastIndexOf("/"); | ||
237 | return scripts[i].src.substr(0, lastSlash); | ||
238 | } | ||
239 | } | ||
240 | } | ||
241 | |||
242 | function evalCommandLine() | ||
243 | { | ||
244 | var text = commandLine.value; | ||
245 | commandLine.value = ""; | ||
246 | |||
247 | logRow([clPrefix, text], "command"); | ||
248 | |||
249 | var value; | ||
250 | try | ||
251 | { | ||
252 | value = eval(text); | ||
253 | } | ||
254 | catch (exc) | ||
255 | { | ||
256 | } | ||
257 | |||
258 | console.log(value); | ||
259 | } | ||
260 | |||
261 | function layout() | ||
262 | { | ||
263 | var toolbar = consoleBody.ownerDocument.getElementById("toolbar"); | ||
264 | var height = consoleFrame.offsetHeight - (toolbar.offsetHeight + commandLine.offsetHeight); | ||
265 | consoleBody.style.top = toolbar.offsetHeight + "px"; | ||
266 | consoleBody.style.height = height + "px"; | ||
267 | |||
268 | commandLine.style.top = (consoleFrame.offsetHeight - commandLine.offsetHeight) + "px"; | ||
269 | } | ||
270 | |||
271 | function logRow(message, className, handler) | ||
272 | { | ||
273 | if (consoleBody) | ||
274 | writeMessage(message, className, handler); | ||
275 | else | ||
276 | { | ||
277 | messageQueue.push([message, className, handler]); | ||
278 | waitForBody(); | ||
279 | } | ||
280 | } | ||
281 | |||
282 | function flush() | ||
283 | { | ||
284 | var queue = messageQueue; | ||
285 | messageQueue = []; | ||
286 | |||
287 | for (var i = 0; i < queue.length; ++i) | ||
288 | writeMessage(queue[i][0], queue[i][1], queue[i][2]); | ||
289 | } | ||
290 | |||
291 | function writeMessage(message, className, handler) | ||
292 | { | ||
293 | var isScrolledToBottom = | ||
294 | consoleBody.scrollTop + consoleBody.offsetHeight >= consoleBody.scrollHeight; | ||
295 | |||
296 | if (!handler) | ||
297 | handler = writeRow; | ||
298 | |||
299 | handler(message, className); | ||
300 | |||
301 | if (isScrolledToBottom) | ||
302 | consoleBody.scrollTop = consoleBody.scrollHeight - consoleBody.offsetHeight; | ||
303 | } | ||
304 | |||
305 | function appendRow(row) | ||
306 | { | ||
307 | var container = groupStack.length ? groupStack[groupStack.length-1] : consoleBody; | ||
308 | container.appendChild(row); | ||
309 | } | ||
310 | |||
311 | function writeRow(message, className) | ||
312 | { | ||
313 | var row = consoleBody.ownerDocument.createElement("div"); | ||
314 | row.className = "logRow" + (className ? " logRow-"+className : ""); | ||
315 | row.innerHTML = message.join(""); | ||
316 | appendRow(row); | ||
317 | } | ||
318 | |||
319 | function pushGroup(message, className) | ||
320 | { | ||
321 | logFormatted(message, className); | ||
322 | |||
323 | var groupRow = consoleBody.ownerDocument.createElement("div"); | ||
324 | groupRow.className = "logGroup"; | ||
325 | var groupRowBox = consoleBody.ownerDocument.createElement("div"); | ||
326 | groupRowBox.className = "logGroupBox"; | ||
327 | groupRow.appendChild(groupRowBox); | ||
328 | appendRow(groupRowBox); | ||
329 | groupStack.push(groupRowBox); | ||
330 | } | ||
331 | |||
332 | function popGroup() | ||
333 | { | ||
334 | groupStack.pop(); | ||
335 | } | ||
336 | |||
337 | // ******************************************************************************************** | ||
338 | |||
339 | function logFormatted(objects, className) | ||
340 | { | ||
341 | var html = []; | ||
342 | |||
343 | var format = objects[0]; | ||
344 | var objIndex = 0; | ||
345 | |||
346 | if (typeof(format) != "string") | ||
347 | { | ||
348 | format = ""; | ||
349 | objIndex = -1; | ||
350 | } | ||
351 | |||
352 | var parts = parseFormat(format); | ||
353 | for (var i = 0; i < parts.length; ++i) | ||
354 | { | ||
355 | var part = parts[i]; | ||
356 | if (part && typeof(part) == "object") | ||
357 | { | ||
358 | var object = objects[++objIndex]; | ||
359 | part.appender(object, html); | ||
360 | } | ||
361 | else | ||
362 | appendText(part, html); | ||
363 | } | ||
364 | |||
365 | for (var i = objIndex+1; i < objects.length; ++i) | ||
366 | { | ||
367 | appendText(" ", html); | ||
368 | |||
369 | var object = objects[i]; | ||
370 | if (typeof(object) == "string") | ||
371 | appendText(object, html); | ||
372 | else | ||
373 | appendObject(object, html); | ||
374 | } | ||
375 | |||
376 | logRow(html, className); | ||
377 | } | ||
378 | |||
379 | function parseFormat(format) | ||
380 | { | ||
381 | var parts = []; | ||
382 | |||
383 | var reg = /((^%|[^\\]%)(\d+)?(\.)([a-zA-Z]))|((^%|[^\\]%)([a-zA-Z]))/; | ||
384 | var appenderMap = {s: appendText, d: appendInteger, i: appendInteger, f: appendFloat}; | ||
385 | |||
386 | for (var m = reg.exec(format); m; m = reg.exec(format)) | ||
387 | { | ||
388 | var type = m[8] ? m[8] : m[5]; | ||
389 | var appender = type in appenderMap ? appenderMap[type] : appendObject; | ||
390 | var precision = m[3] ? parseInt(m[3]) : (m[4] == "." ? -1 : 0); | ||
391 | |||
392 | parts.push(format.substr(0, m[0][0] == "%" ? m.index : m.index+1)); | ||
393 | parts.push({appender: appender, precision: precision}); | ||
394 | |||
395 | format = format.substr(m.index+m[0].length); | ||
396 | } | ||
397 | |||
398 | parts.push(format); | ||
399 | |||
400 | return parts; | ||
401 | } | ||
402 | |||
403 | function escapeHTML(value) | ||
404 | { | ||
405 | function replaceChars(ch) | ||
406 | { | ||
407 | switch (ch) | ||
408 | { | ||
409 | case "<": | ||
410 | return "<"; | ||
411 | case ">": | ||
412 | return ">"; | ||
413 | case "&": | ||
414 | return "&"; | ||
415 | case "'": | ||
416 | return "'"; | ||
417 | case '"': | ||
418 | return """; | ||
419 | } | ||
420 | return "?"; | ||
421 | }; | ||
422 | return String(value).replace(/[<>&"']/g, replaceChars); | ||
423 | } | ||
424 | |||
425 | function objectToString(object) | ||
426 | { | ||
427 | try | ||
428 | { | ||
429 | return object+""; | ||
430 | } | ||
431 | catch (exc) | ||
432 | { | ||
433 | return null; | ||
434 | } | ||
435 | } | ||
436 | |||
437 | // ******************************************************************************************** | ||
438 | |||
439 | function appendText(object, html) | ||
440 | { | ||
441 | html.push(escapeHTML(objectToString(object))); | ||
442 | } | ||
443 | |||
444 | function appendNull(object, html) | ||
445 | { | ||
446 | html.push('<span class="objectBox-null">', escapeHTML(objectToString(object)), '</span>'); | ||
447 | } | ||
448 | |||
449 | function appendString(object, html) | ||
450 | { | ||
451 | html.push('<span class="objectBox-string">"', escapeHTML(objectToString(object)), | ||
452 | '"</span>'); | ||
453 | } | ||
454 | |||
455 | function appendInteger(object, html) | ||
456 | { | ||
457 | html.push('<span class="objectBox-number">', escapeHTML(objectToString(object)), '</span>'); | ||
458 | } | ||
459 | |||
460 | function appendFloat(object, html) | ||
461 | { | ||
462 | html.push('<span class="objectBox-number">', escapeHTML(objectToString(object)), '</span>'); | ||
463 | } | ||
464 | |||
465 | function appendFunction(object, html) | ||
466 | { | ||
467 | var reName = /function ?(.*?)\(/; | ||
468 | var m = reName.exec(objectToString(object)); | ||
469 | var name = m ? m[1] : "function"; | ||
470 | html.push('<span class="objectBox-function">', escapeHTML(name), '()</span>'); | ||
471 | } | ||
472 | |||
473 | function appendObject(object, html) | ||
474 | { | ||
475 | try | ||
476 | { | ||
477 | if (object == undefined) | ||
478 | appendNull("undefined", html); | ||
479 | else if (object == null) | ||
480 | appendNull("null", html); | ||
481 | else if (typeof object == "string") | ||
482 | appendString(object, html); | ||
483 | else if (typeof object == "number") | ||
484 | appendInteger(object, html); | ||
485 | else if (typeof object == "function") | ||
486 | appendFunction(object, html); | ||
487 | else if (object.nodeType == 1) | ||
488 | appendSelector(object, html); | ||
489 | else if (typeof object == "object") | ||
490 | appendObjectFormatted(object, html); | ||
491 | else | ||
492 | appendText(object, html); | ||
493 | } | ||
494 | catch (exc) | ||
495 | { | ||
496 | } | ||
497 | } | ||
498 | |||
499 | function appendObjectFormatted(object, html) | ||
500 | { | ||
501 | var text = objectToString(object); | ||
502 | var reObject = /\[object (.*?)\]/; | ||
503 | |||
504 | var m = reObject.exec(text); | ||
505 | html.push('<span class="objectBox-object">', m ? m[1] : text, '</span>') | ||
506 | } | ||
507 | |||
508 | function appendSelector(object, html) | ||
509 | { | ||
510 | html.push('<span class="objectBox-selector">'); | ||
511 | |||
512 | html.push('<span class="selectorTag">', escapeHTML(object.nodeName.toLowerCase()), '</span>'); | ||
513 | if (object.id) | ||
514 | html.push('<span class="selectorId">#', escapeHTML(object.id), '</span>'); | ||
515 | if (object.className) | ||
516 | html.push('<span class="selectorClass">.', escapeHTML(object.className), '</span>'); | ||
517 | |||
518 | html.push('</span>'); | ||
519 | } | ||
520 | |||
521 | function appendNode(node, html) | ||
522 | { | ||
523 | if (node.nodeType == 1) | ||
524 | { | ||
525 | html.push( | ||
526 | '<div class="objectBox-element">', | ||
527 | '<<span class="nodeTag">', node.nodeName.toLowerCase(), '</span>'); | ||
528 | |||
529 | for (var i = 0; i < node.attributes.length; ++i) | ||
530 | { | ||
531 | var attr = node.attributes[i]; | ||
532 | if (!attr.specified) | ||
533 | continue; | ||
534 | |||
535 | html.push(' <span class="nodeName">', attr.nodeName.toLowerCase(), | ||
536 | '</span>="<span class="nodeValue">', escapeHTML(attr.nodeValue), | ||
537 | '</span>"') | ||
538 | } | ||
539 | |||
540 | if (node.firstChild) | ||
541 | { | ||
542 | html.push('></div><div class="nodeChildren">'); | ||
543 | |||
544 | for (var child = node.firstChild; child; child = child.nextSibling) | ||
545 | appendNode(child, html); | ||
546 | |||
547 | html.push('</div><div class="objectBox-element"></<span class="nodeTag">', | ||
548 | node.nodeName.toLowerCase(), '></span></div>'); | ||
549 | } | ||
550 | else | ||
551 | html.push('/></div>'); | ||
552 | } | ||
553 | else if (node.nodeType == 3) | ||
554 | { | ||
555 | html.push('<div class="nodeText">', escapeHTML(node.nodeValue), | ||
556 | '</div>'); | ||
557 | } | ||
558 | } | ||
559 | |||
560 | // ******************************************************************************************** | ||
561 | |||
562 | function addEvent(object, name, handler) | ||
563 | { | ||
564 | if (document.all) | ||
565 | object.attachEvent("on"+name, handler); | ||
566 | else | ||
567 | object.addEventListener(name, handler, false); | ||
568 | } | ||
569 | |||
570 | function removeEvent(object, name, handler) | ||
571 | { | ||
572 | if (document.all) | ||
573 | object.detachEvent("on"+name, handler); | ||
574 | else | ||
575 | object.removeEventListener(name, handler, false); | ||
576 | } | ||
577 | |||
578 | function cancelEvent(event) | ||
579 | { | ||
580 | if (document.all) | ||
581 | event.cancelBubble = true; | ||
582 | else | ||
583 | event.stopPropagation(); | ||
584 | } | ||
585 | |||
586 | function onError(msg, href, lineNo) | ||
587 | { | ||
588 | var html = []; | ||
589 | |||
590 | var lastSlash = href.lastIndexOf("/"); | ||
591 | var fileName = lastSlash == -1 ? href : href.substr(lastSlash+1); | ||
592 | |||
593 | html.push( | ||
594 | '<span class="errorMessage">', msg, '</span>', | ||
595 | '<div class="objectBox-sourceLink">', fileName, ' (line ', lineNo, ')</div>' | ||
596 | ); | ||
597 | |||
598 | logRow(html, "error"); | ||
599 | }; | ||
600 | |||
601 | function onKeyDown(event) | ||
602 | { | ||
603 | if (event.keyCode == 123) | ||
604 | toggleConsole(); | ||
605 | else if ((event.keyCode == 108 || event.keyCode == 76) && event.shiftKey | ||
606 | && (event.metaKey || event.ctrlKey)) | ||
607 | focusCommandLine(); | ||
608 | else | ||
609 | return; | ||
610 | |||
611 | cancelEvent(event); | ||
612 | } | ||
613 | |||
614 | function onSplitterMouseDown(event) | ||
615 | { | ||
616 | if (isSafari || isOpera) | ||
617 | return; | ||
618 | |||
619 | addEvent(document, "mousemove", onSplitterMouseMove); | ||
620 | addEvent(document, "mouseup", onSplitterMouseUp); | ||
621 | |||
622 | for (var i = 0; i < frames.length; ++i) | ||
623 | { | ||
624 | addEvent(frames[i].document, "mousemove", onSplitterMouseMove); | ||
625 | addEvent(frames[i].document, "mouseup", onSplitterMouseUp); | ||
626 | } | ||
627 | } | ||
628 | |||
629 | function onSplitterMouseMove(event) | ||
630 | { | ||
631 | var win = document.all | ||
632 | ? event.srcElement.ownerDocument.parentWindow | ||
633 | : event.target.ownerDocument.defaultView; | ||
634 | |||
635 | var clientY = event.clientY; | ||
636 | if (win != win.parent) | ||
637 | clientY += win.frameElement ? win.frameElement.offsetTop : 0; | ||
638 | |||
639 | var height = consoleFrame.offsetTop + consoleFrame.clientHeight; | ||
640 | var y = height - clientY; | ||
641 | |||
642 | consoleFrame.style.height = y + "px"; | ||
643 | layout(); | ||
644 | } | ||
645 | |||
646 | function onSplitterMouseUp(event) | ||
647 | { | ||
648 | removeEvent(document, "mousemove", onSplitterMouseMove); | ||
649 | removeEvent(document, "mouseup", onSplitterMouseUp); | ||
650 | |||
651 | for (var i = 0; i < frames.length; ++i) | ||
652 | { | ||
653 | removeEvent(frames[i].document, "mousemove", onSplitterMouseMove); | ||
654 | removeEvent(frames[i].document, "mouseup", onSplitterMouseUp); | ||
655 | } | ||
656 | } | ||
657 | |||
658 | function onCommandLineKeyDown(event) | ||
659 | { | ||
660 | if (event.keyCode == 13) | ||
661 | evalCommandLine(); | ||
662 | else if (event.keyCode == 27) | ||
663 | commandLine.value = ""; | ||
664 | } | ||
665 | |||
666 | window.onerror = onError; | ||
667 | addEvent(document, isIE || isSafari ? "keydown" : "keypress", onKeyDown); | ||
668 | |||
669 | if (document.documentElement.getAttribute("debug") == "true") | ||
670 | toggleConsole(true); | ||
671 | })(); | ||
672 | } | ||
diff --git a/docroot/lib/firebug/firebugx.js b/docroot/lib/firebug/firebugx.js new file mode 100755 index 0000000..5a467fc --- /dev/null +++ b/docroot/lib/firebug/firebugx.js | |||
@@ -0,0 +1,10 @@ | |||
1 | |||
2 | if (!("console" in window) || !("firebug" in console)) | ||
3 | { | ||
4 | var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml", | ||
5 | "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"]; | ||
6 | |||
7 | window.console = {}; | ||
8 | for (var i = 0; i < names.length; ++i) | ||
9 | window.console[names[i]] = function() {} | ||
10 | } \ No newline at end of file | ||
diff --git a/docroot/lib/firebug/infoIcon.png b/docroot/lib/firebug/infoIcon.png new file mode 100755 index 0000000..da1e533 --- /dev/null +++ b/docroot/lib/firebug/infoIcon.png | |||
Binary files differ | |||
diff --git a/docroot/lib/firebug/warningIcon.png b/docroot/lib/firebug/warningIcon.png new file mode 100755 index 0000000..de51084 --- /dev/null +++ b/docroot/lib/firebug/warningIcon.png | |||
Binary files differ | |||
diff --git a/docroot/lib/prototype.js b/docroot/lib/prototype.js new file mode 100755 index 0000000..5806f35 --- /dev/null +++ b/docroot/lib/prototype.js | |||
@@ -0,0 +1,3277 @@ | |||
1 | /* Prototype JavaScript framework, version 1.5.1.1 | ||
2 | * (c) 2005-2007 Sam Stephenson | ||
3 | * | ||
4 | * Prototype is freely distributable under the terms of an MIT-style license. | ||
5 | * For details, see the Prototype web site: http://www.prototypejs.org/ | ||
6 | * | ||
7 | /*--------------------------------------------------------------------------*/ | ||
8 | |||
9 | var Prototype = { | ||
10 | Version: '1.5.1.1', | ||
11 | |||
12 | Browser: { | ||
13 | IE: !!(window.attachEvent && !window.opera), | ||
14 | Opera: !!window.opera, | ||
15 | WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1, | ||
16 | Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1 | ||
17 | }, | ||
18 | |||
19 | BrowserFeatures: { | ||
20 | XPath: !!document.evaluate, | ||
21 | ElementExtensions: !!window.HTMLElement, | ||
22 | SpecificElementExtensions: | ||
23 | (document.createElement('div').__proto__ !== | ||
24 | document.createElement('form').__proto__) | ||
25 | }, | ||
26 | |||
27 | ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>', | ||
28 | JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/, | ||
29 | |||
30 | emptyFunction: function() { }, | ||
31 | K: function(x) { return x } | ||
32 | } | ||
33 | |||
34 | var Class = { | ||
35 | create: function() { | ||
36 | return function() { | ||
37 | this.initialize.apply(this, arguments); | ||
38 | } | ||
39 | } | ||
40 | } | ||
41 | |||
42 | var Abstract = new Object(); | ||
43 | |||
44 | Object.extend = function(destination, source) { | ||
45 | for (var property in source) { | ||
46 | destination[property] = source[property]; | ||
47 | } | ||
48 | return destination; | ||
49 | } | ||
50 | |||
51 | Object.extend(Object, { | ||
52 | inspect: function(object) { | ||
53 | try { | ||
54 | if (object === undefined) return 'undefined'; | ||
55 | if (object === null) return 'null'; | ||
56 | return object.inspect ? object.inspect() : object.toString(); | ||
57 | } catch (e) { | ||
58 | if (e instanceof RangeError) return '...'; | ||
59 | throw e; | ||
60 | } | ||
61 | }, | ||
62 | |||
63 | toJSON: function(object) { | ||
64 | var type = typeof object; | ||
65 | switch(type) { | ||
66 | case 'undefined': | ||
67 | case 'function': | ||
68 | case 'unknown': return; | ||
69 | case 'boolean': return object.toString(); | ||
70 | } | ||
71 | if (object === null) return 'null'; | ||
72 | if (object.toJSON) return object.toJSON(); | ||
73 | if (object.ownerDocument === document) return; | ||
74 | var results = []; | ||
75 | for (var property in object) { | ||
76 | var value = Object.toJSON(object[property]); | ||
77 | if (value !== undefined) | ||
78 | results.push(property.toJSON() + ': ' + value); | ||
79 | } | ||
80 | return '{' + results.join(', ') + '}'; | ||
81 | }, | ||
82 | |||
83 | keys: function(object) { | ||
84 | var keys = []; | ||
85 | for (var property in object) | ||
86 | keys.push(property); | ||
87 | return keys; | ||
88 | }, | ||
89 | |||
90 | values: function(object) { | ||
91 | var values = []; | ||
92 | for (var property in object) | ||
93 | values.push(object[property]); | ||
94 | return values; | ||
95 | }, | ||
96 | |||
97 | clone: function(object) { | ||
98 | return Object.extend({}, object); | ||
99 | } | ||
100 | }); | ||
101 | |||
102 | Function.prototype.bind = function() { | ||
103 | var __method = this, args = $A(arguments), object = args.shift(); | ||
104 | return function() { | ||
105 | return __method.apply(object, args.concat($A(arguments))); | ||
106 | } | ||
107 | } | ||
108 | |||
109 | Function.prototype.bindAsEventListener = function(object) { | ||
110 | var __method = this, args = $A(arguments), object = args.shift(); | ||
111 | return function(event) { | ||
112 | return __method.apply(object, [event || window.event].concat(args)); | ||
113 | } | ||
114 | } | ||
115 | |||
116 | Object.extend(Number.prototype, { | ||
117 | toColorPart: function() { | ||
118 | return this.toPaddedString(2, 16); | ||
119 | }, | ||
120 | |||
121 | succ: function() { | ||
122 | return this + 1; | ||
123 | }, | ||
124 | |||
125 | times: function(iterator) { | ||
126 | $R(0, this, true).each(iterator); | ||
127 | return this; | ||
128 | }, | ||
129 | |||
130 | toPaddedString: function(length, radix) { | ||
131 | var string = this.toString(radix || 10); | ||
132 | return '0'.times(length - string.length) + string; | ||
133 | }, | ||
134 | |||
135 | toJSON: function() { | ||
136 | return isFinite(this) ? this.toString() : 'null'; | ||
137 | } | ||
138 | }); | ||
139 | |||
140 | Date.prototype.toJSON = function() { | ||
141 | return '"' + this.getFullYear() + '-' + | ||
142 | (this.getMonth() + 1).toPaddedString(2) + '-' + | ||
143 | this.getDate().toPaddedString(2) + 'T' + | ||
144 | this.getHours().toPaddedString(2) + ':' + | ||
145 | this.getMinutes().toPaddedString(2) + ':' + | ||
146 | this.getSeconds().toPaddedString(2) + '"'; | ||
147 | }; | ||
148 | |||
149 | var Try = { | ||
150 | these: function() { | ||
151 | var returnValue; | ||
152 | |||
153 | for (var i = 0, length = arguments.length; i < length; i++) { | ||
154 | var lambda = arguments[i]; | ||
155 | try { | ||
156 | returnValue = lambda(); | ||
157 | break; | ||
158 | } catch (e) {} | ||
159 | } | ||
160 | |||
161 | return returnValue; | ||
162 | } | ||
163 | } | ||
164 | |||
165 | /*--------------------------------------------------------------------------*/ | ||
166 | |||
167 | var PeriodicalExecuter = Class.create(); | ||
168 | PeriodicalExecuter.prototype = { | ||
169 | initialize: function(callback, frequency) { | ||
170 | this.callback = callback; | ||
171 | this.frequency = frequency; | ||
172 | this.currentlyExecuting = false; | ||
173 | |||
174 | this.registerCallback(); | ||
175 | }, | ||
176 | |||
177 | registerCallback: function() { | ||
178 | this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); | ||
179 | }, | ||
180 | |||
181 | stop: function() { | ||
182 | if (!this.timer) return; | ||
183 | clearInterval(this.timer); | ||
184 | this.timer = null; | ||
185 | }, | ||
186 | |||
187 | onTimerEvent: function() { | ||
188 | if (!this.currentlyExecuting) { | ||
189 | try { | ||
190 | this.currentlyExecuting = true; | ||
191 | this.callback(this); | ||
192 | } finally { | ||
193 | this.currentlyExecuting = false; | ||
194 | } | ||
195 | } | ||
196 | } | ||
197 | } | ||
198 | Object.extend(String, { | ||
199 | interpret: function(value) { | ||
200 | return value == null ? '' : String(value); | ||
201 | }, | ||
202 | specialChar: { | ||
203 | '\b': '\\b', | ||
204 | '\t': '\\t', | ||
205 | '\n': '\\n', | ||
206 | '\f': '\\f', | ||
207 | '\r': '\\r', | ||
208 | '\\': '\\\\' | ||
209 | } | ||
210 | }); | ||
211 | |||
212 | Object.extend(String.prototype, { | ||
213 | gsub: function(pattern, replacement) { | ||
214 | var result = '', source = this, match; | ||
215 | replacement = arguments.callee.prepareReplacement(replacement); | ||
216 | |||
217 | while (source.length > 0) { | ||
218 | if (match = source.match(pattern)) { | ||
219 | result += source.slice(0, match.index); | ||
220 | result += String.interpret(replacement(match)); | ||
221 | source = source.slice(match.index + match[0].length); | ||
222 | } else { | ||
223 | result += source, source = ''; | ||
224 | } | ||
225 | } | ||
226 | return result; | ||
227 | }, | ||
228 | |||
229 | sub: function(pattern, replacement, count) { | ||
230 | replacement = this.gsub.prepareReplacement(replacement); | ||
231 | count = count === undefined ? 1 : count; | ||
232 | |||
233 | return this.gsub(pattern, function(match) { | ||
234 | if (--count < 0) return match[0]; | ||
235 | return replacement(match); | ||
236 | }); | ||
237 | }, | ||
238 | |||
239 | scan: function(pattern, iterator) { | ||
240 | this.gsub(pattern, iterator); | ||
241 | return this; | ||
242 | }, | ||
243 | |||
244 | truncate: function(length, truncation) { | ||
245 | length = length || 30; | ||
246 | truncation = truncation === undefined ? '...' : truncation; | ||
247 | return this.length > length ? | ||
248 | this.slice(0, length - truncation.length) + truncation : this; | ||
249 | }, | ||
250 | |||
251 | strip: function() { | ||
252 | return this.replace(/^\s+/, '').replace(/\s+$/, ''); | ||
253 | }, | ||
254 | |||
255 | stripTags: function() { | ||
256 | return this.replace(/<\/?[^>]+>/gi, ''); | ||
257 | }, | ||
258 | |||
259 | stripScripts: function() { | ||
260 | return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); | ||
261 | }, | ||
262 | |||
263 | extractScripts: function() { | ||
264 | var matchAll = new RegExp(Prototype.ScriptFragment, 'img'); | ||
265 | var matchOne = new RegExp(Prototype.ScriptFragment, 'im'); | ||
266 | return (this.match(matchAll) || []).map(function(scriptTag) { | ||
267 | return (scriptTag.match(matchOne) || ['', ''])[1]; | ||
268 | }); | ||
269 | }, | ||
270 | |||
271 | evalScripts: function() { | ||
272 | return this.extractScripts().map(function(script) { return eval(script) }); | ||
273 | }, | ||
274 | |||
275 | escapeHTML: function() { | ||
276 | var self = arguments.callee; | ||
277 | self.text.data = this; | ||
278 | return self.div.innerHTML; | ||
279 | }, | ||
280 | |||
281 | unescapeHTML: function() { | ||
282 | var div = document.createElement('div'); | ||
283 | div.innerHTML = this.stripTags(); | ||
284 | return div.childNodes[0] ? (div.childNodes.length > 1 ? | ||
285 | $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) : | ||
286 | div.childNodes[0].nodeValue) : ''; | ||
287 | }, | ||
288 | |||
289 | toQueryParams: function(separator) { | ||
290 | var match = this.strip().match(/([^?#]*)(#.*)?$/); | ||
291 | if (!match) return {}; | ||
292 | |||
293 | return match[1].split(separator || '&').inject({}, function(hash, pair) { | ||
294 | if ((pair = pair.split('='))[0]) { | ||
295 | var key = decodeURIComponent(pair.shift()); | ||
296 | var value = pair.length > 1 ? pair.join('=') : pair[0]; | ||
297 | if (value != undefined) value = decodeURIComponent(value); | ||
298 | |||
299 | if (key in hash) { | ||
300 | if (hash[key].constructor != Array) hash[key] = [hash[key]]; | ||
301 | hash[key].push(value); | ||
302 | } | ||
303 | else hash[key] = value; | ||
304 | } | ||
305 | return hash; | ||
306 | }); | ||
307 | }, | ||
308 | |||
309 | toArray: function() { | ||
310 | return this.split(''); | ||
311 | }, | ||
312 | |||
313 | succ: function() { | ||
314 | return this.slice(0, this.length - 1) + | ||
315 | String.fromCharCode(this.charCodeAt(this.length - 1) + 1); | ||
316 | }, | ||
317 | |||
318 | times: function(count) { | ||
319 | var result = ''; | ||
320 | for (var i = 0; i < count; i++) result += this; | ||
321 | return result; | ||
322 | }, | ||
323 | |||
324 | camelize: function() { | ||
325 | var parts = this.split('-'), len = parts.length; | ||
326 | if (len == 1) return parts[0]; | ||
327 | |||
328 | var camelized = this.charAt(0) == '-' | ||
329 | ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1) | ||
330 | : parts[0]; | ||
331 | |||
332 | for (var i = 1; i < len; i++) | ||
333 | camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1); | ||
334 | |||
335 | return camelized; | ||
336 | }, | ||
337 | |||
338 | capitalize: function() { | ||
339 | return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase(); | ||
340 | }, | ||
341 | |||
342 | underscore: function() { | ||
343 | return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase(); | ||
344 | }, | ||
345 | |||
346 | dasherize: function() { | ||
347 | return this.gsub(/_/,'-'); | ||
348 | }, | ||
349 | |||
350 | inspect: function(useDoubleQuotes) { | ||
351 | var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) { | ||
352 | var character = String.specialChar[match[0]]; | ||
353 | return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16); | ||
354 | }); | ||
355 | if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"'; | ||
356 | return "'" + escapedString.replace(/'/g, '\\\'') + "'"; | ||
357 | }, | ||
358 | |||
359 | toJSON: function() { | ||
360 | return this.inspect(true); | ||
361 | }, | ||
362 | |||
363 | unfilterJSON: function(filter) { | ||
364 | return this.sub(filter || Prototype.JSONFilter, '#{1}'); | ||
365 | }, | ||
366 | |||
367 | isJSON: function() { | ||
368 | var str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''); | ||
369 | return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str); | ||
370 | }, | ||
371 | |||
372 | evalJSON: function(sanitize) { | ||
373 | var json = this.unfilterJSON(); | ||
374 | try { | ||
375 | if (!sanitize || json.isJSON()) return eval('(' + json + ')'); | ||
376 | } catch (e) { } | ||
377 | throw new SyntaxError('Badly formed JSON string: ' + this.inspect()); | ||
378 | }, | ||
379 | |||
380 | include: function(pattern) { | ||
381 | return this.indexOf(pattern) > -1; | ||
382 | }, | ||
383 | |||
384 | startsWith: function(pattern) { | ||
385 | return this.indexOf(pattern) === 0; | ||
386 | }, | ||
387 | |||
388 | endsWith: function(pattern) { | ||
389 | var d = this.length - pattern.length; | ||
390 | return d >= 0 && this.lastIndexOf(pattern) === d; | ||
391 | }, | ||
392 | |||
393 | empty: function() { | ||
394 | return this == ''; | ||
395 | }, | ||
396 | |||
397 | blank: function() { | ||
398 | return /^\s*$/.test(this); | ||
399 | } | ||
400 | }); | ||
401 | |||
402 | if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, { | ||
403 | escapeHTML: function() { | ||
404 | return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); | ||
405 | }, | ||
406 | unescapeHTML: function() { | ||
407 | return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); | ||
408 | } | ||
409 | }); | ||
410 | |||
411 | String.prototype.gsub.prepareReplacement = function(replacement) { | ||
412 | if (typeof replacement == 'function') return replacement; | ||
413 | var template = new Template(replacement); | ||
414 | return function(match) { return template.evaluate(match) }; | ||
415 | } | ||
416 | |||
417 | String.prototype.parseQuery = String.prototype.toQueryParams; | ||
418 | |||
419 | Object.extend(String.prototype.escapeHTML, { | ||
420 | div: document.createElement('div'), | ||
421 | text: document.createTextNode('') | ||
422 | }); | ||
423 | |||
424 | with (String.prototype.escapeHTML) div.appendChild(text); | ||
425 | |||
426 | var Template = Class.create(); | ||
427 | Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; | ||
428 | Template.prototype = { | ||
429 | initialize: function(template, pattern) { | ||
430 | this.template = template.toString(); | ||
431 | this.pattern = pattern || Template.Pattern; | ||
432 | }, | ||
433 | |||
434 | evaluate: function(object) { | ||
435 | return this.template.gsub(this.pattern, function(match) { | ||
436 | var before = match[1]; | ||
437 | if (before == '\\') return match[2]; | ||
438 | return before + String.interpret(object[match[3]]); | ||
439 | }); | ||
440 | } | ||
441 | } | ||
442 | |||
443 | var $break = {}, $continue = new Error('"throw $continue" is deprecated, use "return" instead'); | ||
444 | |||
445 | var Enumerable = { | ||
446 | each: function(iterator) { | ||
447 | var index = 0; | ||
448 | try { | ||
449 | this._each(function(value) { | ||
450 | iterator(value, index++); | ||
451 | }); | ||
452 | } catch (e) { | ||
453 | if (e != $break) throw e; | ||
454 | } | ||
455 | return this; | ||
456 | }, | ||
457 | |||
458 | eachSlice: function(number, iterator) { | ||
459 | var index = -number, slices = [], array = this.toArray(); | ||
460 | while ((index += number) < array.length) | ||
461 | slices.push(array.slice(index, index+number)); | ||
462 | return slices.map(iterator); | ||
463 | }, | ||
464 | |||
465 | all: function(iterator) { | ||
466 | var result = true; | ||
467 | this.each(function(value, index) { | ||
468 | result = result && !!(iterator || Prototype.K)(value, index); | ||
469 | if (!result) throw $break; | ||
470 | }); | ||
471 | return result; | ||
472 | }, | ||
473 | |||
474 | any: function(iterator) { | ||
475 | var result = false; | ||
476 | this.each(function(value, index) { | ||
477 | if (result = !!(iterator || Prototype.K)(value, index)) | ||
478 | throw $break; | ||
479 | }); | ||
480 | return result; | ||
481 | }, | ||
482 | |||
483 | collect: function(iterator) { | ||
484 | var results = []; | ||
485 | this.each(function(value, index) { | ||
486 | results.push((iterator || Prototype.K)(value, index)); | ||
487 | }); | ||
488 | return results; | ||
489 | }, | ||
490 | |||
491 | detect: function(iterator) { | ||
492 | var result; | ||
493 | this.each(function(value, index) { | ||
494 | if (iterator(value, index)) { | ||
495 | result = value; | ||
496 | throw $break; | ||
497 | } | ||
498 | }); | ||
499 | return result; | ||
500 | }, | ||
501 | |||
502 | findAll: function(iterator) { | ||
503 | var results = []; | ||
504 | this.each(function(value, index) { | ||
505 | if (iterator(value, index)) | ||
506 | results.push(value); | ||
507 | }); | ||
508 | return results; | ||
509 | }, | ||
510 | |||
511 | grep: function(pattern, iterator) { | ||
512 | var results = []; | ||
513 | this.each(function(value, index) { | ||
514 | var stringValue = value.toString(); | ||
515 | if (stringValue.match(pattern)) | ||
516 | results.push((iterator || Prototype.K)(value, index)); | ||
517 | }) | ||
518 | return results; | ||
519 | }, | ||
520 | |||
521 | include: function(object) { | ||
522 | var found = false; | ||
523 | this.each(function(value) { | ||
524 | if (value == object) { | ||
525 | found = true; | ||
526 | throw $break; | ||
527 | } | ||
528 | }); | ||
529 | return found; | ||
530 | }, | ||
531 | |||
532 | inGroupsOf: function(number, fillWith) { | ||
533 | fillWith = fillWith === undefined ? null : fillWith; | ||
534 | return this.eachSlice(number, function(slice) { | ||
535 | while(slice.length < number) slice.push(fillWith); | ||
536 | return slice; | ||
537 | }); | ||
538 | }, | ||
539 | |||
540 | inject: function(memo, iterator) { | ||
541 | this.each(function(value, index) { | ||
542 | memo = iterator(memo, value, index); | ||
543 | }); | ||
544 | return memo; | ||
545 | }, | ||
546 | |||
547 | invoke: function(method) { | ||
548 | var args = $A(arguments).slice(1); | ||
549 | return this.map(function(value) { | ||
550 | return value[method].apply(value, args); | ||
551 | }); | ||
552 | }, | ||
553 | |||
554 | max: function(iterator) { | ||
555 | var result; | ||
556 | this.each(function(value, index) { | ||
557 | value = (iterator || Prototype.K)(value, index); | ||
558 | if (result == undefined || value >= result) | ||
559 | result = value; | ||
560 | }); | ||
561 | return result; | ||
562 | }, | ||
563 | |||
564 | min: function(iterator) { | ||
565 | var result; | ||
566 | this.each(function(value, index) { | ||
567 | value = (iterator || Prototype.K)(value, index); | ||
568 | if (result == undefined || value < result) | ||
569 | result = value; | ||
570 | }); | ||
571 | return result; | ||
572 | }, | ||
573 | |||
574 | partition: function(iterator) { | ||
575 | var trues = [], falses = []; | ||
576 | this.each(function(value, index) { | ||
577 | ((iterator || Prototype.K)(value, index) ? | ||
578 | trues : falses).push(value); | ||
579 | }); | ||
580 | return [trues, falses]; | ||
581 | }, | ||
582 | |||
583 | pluck: function(property) { | ||
584 | var results = []; | ||
585 | this.each(function(value, index) { | ||
586 | results.push(value[property]); | ||
587 | }); | ||
588 | return results; | ||
589 | }, | ||
590 | |||
591 | reject: function(iterator) { | ||
592 | var results = []; | ||
593 | this.each(function(value, index) { | ||
594 | if (!iterator(value, index)) | ||
595 | results.push(value); | ||
596 | }); | ||
597 | return results; | ||
598 | }, | ||
599 | |||
600 | sortBy: function(iterator) { | ||
601 | return this.map(function(value, index) { | ||
602 | return {value: value, criteria: iterator(value, index)}; | ||
603 | }).sort(function(left, right) { | ||
604 | var a = left.criteria, b = right.criteria; | ||
605 | return a < b ? -1 : a > b ? 1 : 0; | ||
606 | }).pluck('value'); | ||
607 | }, | ||
608 | |||
609 | toArray: function() { | ||
610 | return this.map(); | ||
611 | }, | ||
612 | |||
613 | zip: function() { | ||
614 | var iterator = Prototype.K, args = $A(arguments); | ||
615 | if (typeof args.last() == 'function') | ||
616 | iterator = args.pop(); | ||
617 | |||
618 | var collections = [this].concat(args).map($A); | ||
619 | return this.map(function(value, index) { | ||
620 | return iterator(collections.pluck(index)); | ||
621 | }); | ||
622 | }, | ||
623 | |||
624 | size: function() { | ||
625 | return this.toArray().length; | ||
626 | }, | ||
627 | |||
628 | inspect: function() { | ||
629 | return '#<Enumerable:' + this.toArray().inspect() + '>'; | ||
630 | } | ||
631 | } | ||
632 | |||
633 | Object.extend(Enumerable, { | ||
634 | map: Enumerable.collect, | ||
635 | find: Enumerable.detect, | ||
636 | select: Enumerable.findAll, | ||
637 | member: Enumerable.include, | ||
638 | entries: Enumerable.toArray | ||
639 | }); | ||
640 | var $A = Array.from = function(iterable) { | ||
641 | if (!iterable) return []; | ||
642 | if (iterable.toArray) { | ||
643 | return iterable.toArray(); | ||
644 | } else { | ||
645 | var results = []; | ||
646 | for (var i = 0, length = iterable.length; i < length; i++) | ||
647 | results.push(iterable[i]); | ||
648 | return results; | ||
649 | } | ||
650 | } | ||
651 | |||
652 | if (Prototype.Browser.WebKit) { | ||
653 | $A = Array.from = function(iterable) { | ||
654 | if (!iterable) return []; | ||
655 | if (!(typeof iterable == 'function' && iterable == '[object NodeList]') && | ||
656 | iterable.toArray) { | ||
657 | return iterable.toArray(); | ||
658 | } else { | ||
659 | var results = []; | ||
660 | for (var i = 0, length = iterable.length; i < length; i++) | ||
661 | results.push(iterable[i]); | ||
662 | return results; | ||
663 | } | ||
664 | } | ||
665 | } | ||
666 | |||
667 | Object.extend(Array.prototype, Enumerable); | ||
668 | |||
669 | if (!Array.prototype._reverse) | ||
670 | Array.prototype._reverse = Array.prototype.reverse; | ||
671 | |||
672 | Object.extend(Array.prototype, { | ||
673 | _each: function(iterator) { | ||
674 | for (var i = 0, length = this.length; i < length; i++) | ||
675 | iterator(this[i]); | ||
676 | }, | ||
677 | |||
678 | clear: function() { | ||
679 | this.length = 0; | ||
680 | return this; | ||
681 | }, | ||
682 | |||
683 | first: function() { | ||
684 | return this[0]; | ||
685 | }, | ||
686 | |||
687 | last: function() { | ||
688 | return this[this.length - 1]; | ||
689 | }, | ||
690 | |||
691 | compact: function() { | ||
692 | return this.select(function(value) { | ||
693 | return value != null; | ||
694 | }); | ||
695 | }, | ||
696 | |||
697 | flatten: function() { | ||
698 | return this.inject([], function(array, value) { | ||
699 | return array.concat(value && value.constructor == Array ? | ||
700 | value.flatten() : [value]); | ||
701 | }); | ||
702 | }, | ||
703 | |||
704 | without: function() { | ||
705 | var values = $A(arguments); | ||
706 | return this.select(function(value) { | ||
707 | return !values.include(value); | ||
708 | }); | ||
709 | }, | ||
710 | |||
711 | indexOf: function(object) { | ||
712 | for (var i = 0, length = this.length; i < length; i++) | ||
713 | if (this[i] == object) return i; | ||
714 | return -1; | ||
715 | }, | ||
716 | |||
717 | reverse: function(inline) { | ||
718 | return (inline !== false ? this : this.toArray())._reverse(); | ||
719 | }, | ||
720 | |||
721 | reduce: function() { | ||
722 | return this.length > 1 ? this : this[0]; | ||
723 | }, | ||
724 | |||
725 | uniq: function(sorted) { | ||
726 | return this.inject([], function(array, value, index) { | ||
727 | if (0 == index || (sorted ? array.last() != value : !array.include(value))) | ||
728 | array.push(value); | ||
729 | return array; | ||
730 | }); | ||
731 | }, | ||
732 | |||
733 | clone: function() { | ||
734 | return [].concat(this); | ||
735 | }, | ||
736 | |||
737 | size: function() { | ||
738 | return this.length; | ||
739 | }, | ||
740 | |||
741 | inspect: function() { | ||
742 | return '[' + this.map(Object.inspect).join(', ') + ']'; | ||
743 | }, | ||
744 | |||
745 | toJSON: function() { | ||
746 | var results = []; | ||
747 | this.each(function(object) { | ||
748 | var value = Object.toJSON(object); | ||
749 | if (value !== undefined) results.push(value); | ||
750 | }); | ||
751 | return '[' + results.join(', ') + ']'; | ||
752 | } | ||
753 | }); | ||
754 | |||
755 | Array.prototype.toArray = Array.prototype.clone; | ||
756 | |||
757 | function $w(string) { | ||
758 | string = string.strip(); | ||
759 | return string ? string.split(/\s+/) : []; | ||
760 | } | ||
761 | |||
762 | if (Prototype.Browser.Opera){ | ||
763 | Array.prototype.concat = function() { | ||
764 | var array = []; | ||
765 | for (var i = 0, length = this.length; i < length; i++) array.push(this[i]); | ||
766 | for (var i = 0, length = arguments.length; i < length; i++) { | ||
767 | if (arguments[i].constructor == Array) { | ||
768 | for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++) | ||
769 | array.push(arguments[i][j]); | ||
770 | } else { | ||
771 | array.push(arguments[i]); | ||
772 | } | ||
773 | } | ||
774 | return array; | ||
775 | } | ||
776 | } | ||
777 | var Hash = function(object) { | ||
778 | if (object instanceof Hash) this.merge(object); | ||
779 | else Object.extend(this, object || {}); | ||
780 | }; | ||
781 | |||
782 | Object.extend(Hash, { | ||
783 | toQueryString: function(obj) { | ||
784 | var parts = []; | ||
785 | parts.add = arguments.callee.addPair; | ||
786 | |||
787 | this.prototype._each.call(obj, function(pair) { | ||
788 | if (!pair.key) return; | ||
789 | var value = pair.value; | ||
790 | |||
791 | if (value && typeof value == 'object') { | ||
792 | if (value.constructor == Array) value.each(function(value) { | ||
793 | parts.add(pair.key, value); | ||
794 | }); | ||
795 | return; | ||
796 | } | ||
797 | parts.add(pair.key, value); | ||
798 | }); | ||
799 | |||
800 | return parts.join('&'); | ||
801 | }, | ||
802 | |||
803 | toJSON: function(object) { | ||
804 | var results = []; | ||
805 | this.prototype._each.call(object, function(pair) { | ||
806 | var value = Object.toJSON(pair.value); | ||
807 | if (value !== undefined) results.push(pair.key.toJSON() + ': ' + value); | ||
808 | }); | ||
809 | return '{' + results.join(', ') + '}'; | ||
810 | } | ||
811 | }); | ||
812 | |||
813 | Hash.toQueryString.addPair = function(key, value, prefix) { | ||
814 | key = encodeURIComponent(key); | ||
815 | if (value === undefined) this.push(key); | ||
816 | else this.push(key + '=' + (value == null ? '' : encodeURIComponent(value))); | ||
817 | } | ||
818 | |||
819 | Object.extend(Hash.prototype, Enumerable); | ||
820 | Object.extend(Hash.prototype, { | ||
821 | _each: function(iterator) { | ||
822 | for (var key in this) { | ||
823 | var value = this[key]; | ||
824 | if (value && value == Hash.prototype[key]) continue; | ||
825 | |||
826 | var pair = [key, value]; | ||
827 | pair.key = key; | ||
828 | pair.value = value; | ||
829 | iterator(pair); | ||
830 | } | ||
831 | }, | ||
832 | |||
833 | keys: function() { | ||
834 | return this.pluck('key'); | ||
835 | }, | ||
836 | |||
837 | values: function() { | ||
838 | return this.pluck('value'); | ||
839 | }, | ||
840 | |||
841 | merge: function(hash) { | ||
842 | return $H(hash).inject(this, function(mergedHash, pair) { | ||
843 | mergedHash[pair.key] = pair.value; | ||
844 | return mergedHash; | ||
845 | }); | ||
846 | }, | ||
847 | |||
848 | remove: function() { | ||
849 | var result; | ||
850 | for(var i = 0, length = arguments.length; i < length; i++) { | ||
851 | var value = this[arguments[i]]; | ||
852 | if (value !== undefined){ | ||
853 | if (result === undefined) result = value; | ||
854 | else { | ||
855 | if (result.constructor != Array) result = [result]; | ||
856 | result.push(value) | ||
857 | } | ||
858 | } | ||
859 | delete this[arguments[i]]; | ||
860 | } | ||
861 | return result; | ||
862 | }, | ||
863 | |||
864 | toQueryString: function() { | ||
865 | return Hash.toQueryString(this); | ||
866 | }, | ||
867 | |||
868 | inspect: function() { | ||
869 | return '#<Hash:{' + this.map(function(pair) { | ||
870 | return pair.map(Object.inspect).join(': '); | ||
871 | }).join(', ') + '}>'; | ||
872 | }, | ||
873 | |||
874 | toJSON: function() { | ||
875 | return Hash.toJSON(this); | ||
876 | } | ||
877 | }); | ||
878 | |||
879 | function $H(object) { | ||
880 | if (object instanceof Hash) return object; | ||
881 | return new Hash(object); | ||
882 | }; | ||
883 | |||
884 | // Safari iterates over shadowed properties | ||
885 | if (function() { | ||
886 | var i = 0, Test = function(value) { this.key = value }; | ||
887 | Test.prototype.key = 'foo'; | ||
888 | for (var property in new Test('bar')) i++; | ||
889 | return i > 1; | ||
890 | }()) Hash.prototype._each = function(iterator) { | ||
891 | var cache = []; | ||
892 | for (var key in this) { | ||
893 | var value = this[key]; | ||
894 | if ((value && value == Hash.prototype[key]) || cache.include(key)) continue; | ||
895 | cache.push(key); | ||
896 | var pair = [key, value]; | ||
897 | pair.key = key; | ||
898 | pair.value = value; | ||
899 | iterator(pair); | ||
900 | } | ||
901 | }; | ||
902 | ObjectRange = Class.create(); | ||
903 | Object.extend(ObjectRange.prototype, Enumerable); | ||
904 | Object.extend(ObjectRange.prototype, { | ||
905 | initialize: function(start, end, exclusive) { | ||
906 | this.start = start; | ||
907 | this.end = end; | ||
908 | this.exclusive = exclusive; | ||
909 | }, | ||
910 | |||
911 | _each: function(iterator) { | ||
912 | var value = this.start; | ||
913 | while (this.include(value)) { | ||
914 | iterator(value); | ||
915 | value = value.succ(); | ||
916 | } | ||
917 | }, | ||
918 | |||
919 | include: function(value) { | ||
920 | if (value < this.start) | ||
921 | return false; | ||
922 | if (this.exclusive) | ||
923 | return value < this.end; | ||
924 | return value <= this.end; | ||
925 | } | ||
926 | }); | ||
927 | |||
928 | var $R = function(start, end, exclusive) { | ||
929 | return new ObjectRange(start, end, exclusive); | ||
930 | } | ||
931 | |||
932 | var Ajax = { | ||
933 | getTransport: function() { | ||
934 | return Try.these( | ||
935 | function() {return new XMLHttpRequest()}, | ||
936 | function() {return new ActiveXObject('Msxml2.XMLHTTP')}, | ||
937 | function() {return new ActiveXObject('Microsoft.XMLHTTP')} | ||
938 | ) || false; | ||
939 | }, | ||
940 | |||
941 | activeRequestCount: 0 | ||
942 | } | ||
943 | |||
944 | Ajax.Responders = { | ||
945 | responders: [], | ||
946 | |||
947 | _each: function(iterator) { | ||
948 | this.responders._each(iterator); | ||
949 | }, | ||
950 | |||
951 | register: function(responder) { | ||
952 | if (!this.include(responder)) | ||
953 | this.responders.push(responder); | ||
954 | }, | ||
955 | |||
956 | unregister: function(responder) { | ||
957 | this.responders = this.responders.without(responder); | ||
958 | }, | ||
959 | |||
960 | dispatch: function(callback, request, transport, json) { | ||
961 | this.each(function(responder) { | ||
962 | if (typeof responder[callback] == 'function') { | ||
963 | try { | ||
964 | responder[callback].apply(responder, [request, transport, json]); | ||
965 | } catch (e) {} | ||
966 | } | ||
967 | }); | ||
968 | } | ||
969 | }; | ||
970 | |||
971 | Object.extend(Ajax.Responders, Enumerable); | ||
972 | |||
973 | Ajax.Responders.register({ | ||
974 | onCreate: function() { | ||
975 | Ajax.activeRequestCount++; | ||
976 | }, | ||
977 | onComplete: function() { | ||
978 | Ajax.activeRequestCount--; | ||
979 | } | ||
980 | }); | ||
981 | |||
982 | Ajax.Base = function() {}; | ||
983 | Ajax.Base.prototype = { | ||
984 | setOptions: function(options) { | ||
985 | this.options = { | ||
986 | method: 'post', | ||
987 | asynchronous: true, | ||
988 | contentType: 'application/x-www-form-urlencoded', | ||
989 | encoding: 'UTF-8', | ||
990 | parameters: '' | ||
991 | } | ||
992 | Object.extend(this.options, options || {}); | ||
993 | |||
994 | this.options.method = this.options.method.toLowerCase(); | ||
995 | if (typeof this.options.parameters == 'string') | ||
996 | this.options.parameters = this.options.parameters.toQueryParams(); | ||
997 | } | ||
998 | } | ||
999 | |||
1000 | Ajax.Request = Class.create(); | ||
1001 | Ajax.Request.Events = | ||
1002 | ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; | ||
1003 | |||
1004 | Ajax.Request.prototype = Object.extend(new Ajax.Base(), { | ||
1005 | _complete: false, | ||
1006 | |||
1007 | initialize: function(url, options) { | ||
1008 | this.transport = Ajax.getTransport(); | ||
1009 | this.setOptions(options); | ||
1010 | this.request(url); | ||
1011 | }, | ||
1012 | |||
1013 | request: function(url) { | ||
1014 | this.url = url; | ||
1015 | this.method = this.options.method; | ||
1016 | var params = Object.clone(this.options.parameters); | ||
1017 | |||
1018 | if (!['get', 'post'].include(this.method)) { | ||
1019 | // simulate other verbs over post | ||
1020 | params['_method'] = this.method; | ||
1021 | this.method = 'post'; | ||
1022 | } | ||
1023 | |||
1024 | this.parameters = params; | ||
1025 | |||
1026 | if (params = Hash.toQueryString(params)) { | ||
1027 | // when GET, append parameters to URL | ||
1028 | if (this.method == 'get') | ||
1029 | this.url += (this.url.include('?') ? '&' : '?') + params; | ||
1030 | else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) | ||
1031 | params += '&_='; | ||
1032 | } | ||
1033 | |||
1034 | try { | ||
1035 | if (this.options.onCreate) this.options.onCreate(this.transport); | ||
1036 | Ajax.Responders.dispatch('onCreate', this, this.transport); | ||
1037 | |||
1038 | this.transport.open(this.method.toUpperCase(), this.url, | ||
1039 | this.options.asynchronous); | ||
1040 | |||
1041 | if (this.options.asynchronous) | ||
1042 | setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10); | ||
1043 | |||
1044 | this.transport.onreadystatechange = this.onStateChange.bind(this); | ||
1045 | this.setRequestHeaders(); | ||
1046 | |||
1047 | this.body = this.method == 'post' ? (this.options.postBody || params) : null; | ||
1048 | this.transport.send(this.body); | ||
1049 | |||
1050 | /* Force Firefox to handle ready state 4 for synchronous requests */ | ||
1051 | if (!this.options.asynchronous && this.transport.overrideMimeType) | ||
1052 | this.onStateChange(); | ||
1053 | |||
1054 | } | ||
1055 | catch (e) { | ||
1056 | this.dispatchException(e); | ||
1057 | } | ||
1058 | }, | ||
1059 | |||
1060 | onStateChange: function() { | ||
1061 | var readyState = this.transport.readyState; | ||
1062 | if (readyState > 1 && !((readyState == 4) && this._complete)) | ||
1063 | this.respondToReadyState(this.transport.readyState); | ||
1064 | }, | ||
1065 | |||
1066 | setRequestHeaders: function() { | ||
1067 | var headers = { | ||
1068 | 'X-Requested-With': 'XMLHttpRequest', | ||
1069 | 'X-Prototype-Version': Prototype.Version, | ||
1070 | 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*' | ||
1071 | }; | ||
1072 | |||
1073 | if (this.method == 'post') { | ||
1074 | headers['Content-type'] = this.options.contentType + | ||
1075 | (this.options.encoding ? '; charset=' + this.options.encoding : ''); | ||
1076 | |||
1077 | /* Force "Connection: close" for older Mozilla browsers to work | ||
1078 | * around a bug where XMLHttpRequest sends an incorrect | ||
1079 | * Content-length header. See Mozilla Bugzilla #246651. | ||
1080 | */ | ||
1081 | if (this.transport.overrideMimeType && | ||
1082 | (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005) | ||
1083 | headers['Connection'] = 'close'; | ||
1084 | } | ||
1085 | |||
1086 | // user-defined headers | ||
1087 | if (typeof this.options.requestHeaders == 'object') { | ||
1088 | var extras = this.options.requestHeaders; | ||
1089 | |||
1090 | if (typeof extras.push == 'function') | ||
1091 | for (var i = 0, length = extras.length; i < length; i += 2) | ||
1092 | headers[extras[i]] = extras[i+1]; | ||
1093 | else | ||
1094 | $H(extras).each(function(pair) { headers[pair.key] = pair.value }); | ||
1095 | } | ||
1096 | |||
1097 | for (var name in headers) | ||
1098 | this.transport.setRequestHeader(name, headers[name]); | ||
1099 | }, | ||
1100 | |||
1101 | success: function() { | ||
1102 | return !this.transport.status | ||
1103 | || (this.transport.status >= 200 && this.transport.status < 300); | ||
1104 | }, | ||
1105 | |||
1106 | respondToReadyState: function(readyState) { | ||
1107 | var state = Ajax.Request.Events[readyState]; | ||
1108 | var transport = this.transport, json = this.evalJSON(); | ||
1109 | |||
1110 | if (state == 'Complete') { | ||
1111 | try { | ||
1112 | this._complete = true; | ||
1113 | (this.options['on' + this.transport.status] | ||
1114 | || this.options['on' + (this.success() ? 'Success' : 'Failure')] | ||
1115 | || Prototype.emptyFunction)(transport, json); | ||
1116 | } catch (e) { | ||
1117 | this.dispatchException(e); | ||
1118 | } | ||
1119 | |||
1120 | var contentType = this.getHeader('Content-type'); | ||
1121 | if (contentType && contentType.strip(). | ||
1122 | match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i)) | ||
1123 | this.evalResponse(); | ||
1124 | } | ||
1125 | |||
1126 | try { | ||
1127 | (this.options['on' + state] || Prototype.emptyFunction)(transport, json); | ||
1128 | Ajax.Responders.dispatch('on' + state, this, transport, json); | ||
1129 | } catch (e) { | ||
1130 | this.dispatchException(e); | ||
1131 | } | ||
1132 | |||
1133 | if (state == 'Complete') { | ||
1134 | // avoid memory leak in MSIE: clean up | ||
1135 | this.transport.onreadystatechange = Prototype.emptyFunction; | ||
1136 | } | ||
1137 | }, | ||
1138 | |||
1139 | getHeader: function(name) { | ||
1140 | try { | ||
1141 | return this.transport.getResponseHeader(name); | ||
1142 | } catch (e) { return null } | ||
1143 | }, | ||
1144 | |||
1145 | evalJSON: function() { | ||
1146 | try { | ||
1147 | var json = this.getHeader('X-JSON'); | ||
1148 | return json ? json.evalJSON() : null; | ||
1149 | } catch (e) { return null } | ||
1150 | }, | ||
1151 | |||
1152 | evalResponse: function() { | ||
1153 | try { | ||
1154 | return eval((this.transport.responseText || '').unfilterJSON()); | ||
1155 | } catch (e) { | ||
1156 | this.dispatchException(e); | ||
1157 | } | ||
1158 | }, | ||
1159 | |||
1160 | dispatchException: function(exception) { | ||
1161 | (this.options.onException || Prototype.emptyFunction)(this, exception); | ||
1162 | Ajax.Responders.dispatch('onException', this, exception); | ||
1163 | } | ||
1164 | }); | ||
1165 | |||
1166 | Ajax.Updater = Class.create(); | ||
1167 | |||
1168 | Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), { | ||
1169 | initialize: function(container, url, options) { | ||
1170 | this.container = { | ||
1171 | success: (container.success || container), | ||
1172 | failure: (container.failure || (container.success ? null : container)) | ||
1173 | } | ||
1174 | |||
1175 | this.transport = Ajax.getTransport(); | ||
1176 | this.setOptions(options); | ||
1177 | |||
1178 | var onComplete = this.options.onComplete || Prototype.emptyFunction; | ||
1179 | this.options.onComplete = (function(transport, param) { | ||
1180 | this.updateContent(); | ||
1181 | onComplete(transport, param); | ||
1182 | }).bind(this); | ||
1183 | |||
1184 | this.request(url); | ||
1185 | }, | ||
1186 | |||
1187 | updateContent: function() { | ||
1188 | var receiver = this.container[this.success() ? 'success' : 'failure']; | ||
1189 | var response = this.transport.responseText; | ||
1190 | |||
1191 | if (!this.options.evalScripts) response = response.stripScripts(); | ||
1192 | |||
1193 | if (receiver = $(receiver)) { | ||
1194 | if (this.options.insertion) | ||
1195 | new this.options.insertion(receiver, response); | ||
1196 | else | ||
1197 | receiver.update(response); | ||
1198 | } | ||
1199 | |||
1200 | if (this.success()) { | ||
1201 | if (this.onComplete) | ||
1202 | setTimeout(this.onComplete.bind(this), 10); | ||
1203 | } | ||
1204 | } | ||
1205 | }); | ||
1206 | |||
1207 | Ajax.PeriodicalUpdater = Class.create(); | ||
1208 | Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), { | ||
1209 | initialize: function(container, url, options) { | ||
1210 | this.setOptions(options); | ||
1211 | this.onComplete = this.options.onComplete; | ||
1212 | |||
1213 | this.frequency = (this.options.frequency || 2); | ||
1214 | this.decay = (this.options.decay || 1); | ||
1215 | |||
1216 | this.updater = {}; | ||
1217 | this.container = container; | ||
1218 | this.url = url; | ||
1219 | |||
1220 | this.start(); | ||
1221 | }, | ||
1222 | |||
1223 | start: function() { | ||
1224 | this.options.onComplete = this.updateComplete.bind(this); | ||
1225 | this.onTimerEvent(); | ||
1226 | }, | ||
1227 | |||
1228 | stop: function() { | ||
1229 | this.updater.options.onComplete = undefined; | ||
1230 | clearTimeout(this.timer); | ||
1231 | (this.onComplete || Prototype.emptyFunction).apply(this, arguments); | ||
1232 | }, | ||
1233 | |||
1234 | updateComplete: function(request) { | ||
1235 | if (this.options.decay) { | ||
1236 | this.decay = (request.responseText == this.lastText ? | ||
1237 | this.decay * this.options.decay : 1); | ||
1238 | |||
1239 | this.lastText = request.responseText; | ||
1240 | } | ||
1241 | this.timer = setTimeout(this.onTimerEvent.bind(this), | ||
1242 | this.decay * this.frequency * 1000); | ||
1243 | }, | ||
1244 | |||
1245 | onTimerEvent: function() { | ||
1246 | this.updater = new Ajax.Updater(this.container, this.url, this.options); | ||
1247 | } | ||
1248 | }); | ||
1249 | function $(element) { | ||
1250 | if (arguments.length > 1) { | ||
1251 | for (var i = 0, elements = [], length = arguments.length; i < length; i++) | ||
1252 | elements.push($(arguments[i])); | ||
1253 | return elements; | ||
1254 | } | ||
1255 | if (typeof element == 'string') | ||
1256 | element = document.getElementById(element); | ||
1257 | return Element.extend(element); | ||
1258 | } | ||
1259 | |||
1260 | if (Prototype.BrowserFeatures.XPath) { | ||
1261 | document._getElementsByXPath = function(expression, parentElement) { | ||
1262 | var results = []; | ||
1263 | var query = document.evaluate(expression, $(parentElement) || document, | ||
1264 | null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); | ||
1265 | for (var i = 0, length = query.snapshotLength; i < length; i++) | ||
1266 | results.push(query.snapshotItem(i)); | ||
1267 | return results; | ||
1268 | }; | ||
1269 | |||
1270 | document.getElementsByClassName = function(className, parentElement) { | ||
1271 | var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]"; | ||
1272 | return document._getElementsByXPath(q, parentElement); | ||
1273 | } | ||
1274 | |||
1275 | } else document.getElementsByClassName = function(className, parentElement) { | ||
1276 | var children = ($(parentElement) || document.body).getElementsByTagName('*'); | ||
1277 | var elements = [], child, pattern = new RegExp("(^|\\s)" + className + "(\\s|$)"); | ||
1278 | for (var i = 0, length = children.length; i < length; i++) { | ||
1279 | child = children[i]; | ||
1280 | var elementClassName = child.className; | ||
1281 | if (elementClassName.length == 0) continue; | ||
1282 | if (elementClassName == className || elementClassName.match(pattern)) | ||
1283 | elements.push(Element.extend(child)); | ||
1284 | } | ||
1285 | return elements; | ||
1286 | }; | ||
1287 | |||
1288 | /*--------------------------------------------------------------------------*/ | ||
1289 | |||
1290 | if (!window.Element) var Element = {}; | ||
1291 | |||
1292 | Element.extend = function(element) { | ||
1293 | var F = Prototype.BrowserFeatures; | ||
1294 | if (!element || !element.tagName || element.nodeType == 3 || | ||
1295 | element._extended || F.SpecificElementExtensions || element == window) | ||
1296 | return element; | ||
1297 | |||
1298 | var methods = {}, tagName = element.tagName, cache = Element.extend.cache, | ||
1299 | T = Element.Methods.ByTag; | ||
1300 | |||
1301 | // extend methods for all tags (Safari doesn't need this) | ||
1302 | if (!F.ElementExtensions) { | ||
1303 | Object.extend(methods, Element.Methods), | ||
1304 | Object.extend(methods, Element.Methods.Simulated); | ||
1305 | } | ||
1306 | |||
1307 | // extend methods for specific tags | ||
1308 | if (T[tagName]) Object.extend(methods, T[tagName]); | ||
1309 | |||
1310 | for (var property in methods) { | ||
1311 | var value = methods[property]; | ||
1312 | if (typeof value == 'function' && !(property in element)) | ||
1313 | element[property] = cache.findOrStore(value); | ||
1314 | } | ||
1315 | |||
1316 | element._extended = Prototype.emptyFunction; | ||
1317 | return element; | ||
1318 | }; | ||
1319 | |||
1320 | Element.extend.cache = { | ||
1321 | findOrStore: function(value) { | ||
1322 | return this[value] = this[value] || function() { | ||
1323 | return value.apply(null, [this].concat($A(arguments))); | ||
1324 | } | ||
1325 | } | ||
1326 | }; | ||
1327 | |||
1328 | Element.Methods = { | ||
1329 | visible: function(element) { | ||
1330 | return $(element).style.display != 'none'; | ||
1331 | }, | ||
1332 | |||
1333 | toggle: function(element) { | ||
1334 | element = $(element); | ||
1335 | Element[Element.visible(element) ? 'hide' : 'show'](element); | ||
1336 | return element; | ||
1337 | }, | ||
1338 | |||
1339 | hide: function(element) { | ||
1340 | $(element).style.display = 'none'; | ||
1341 | return element; | ||
1342 | }, | ||
1343 | |||
1344 | show: function(element) { | ||
1345 | $(element).style.display = ''; | ||
1346 | return element; | ||
1347 | }, | ||
1348 | |||
1349 | remove: function(element) { | ||
1350 | element = $(element); | ||
1351 | element.parentNode.removeChild(element); | ||
1352 | return element; | ||
1353 | }, | ||
1354 | |||
1355 | update: function(element, html) { | ||
1356 | html = typeof html == 'undefined' ? '' : html.toString(); | ||
1357 | $(element).innerHTML = html.stripScripts(); | ||
1358 | setTimeout(function() {html.evalScripts()}, 10); | ||
1359 | return element; | ||
1360 | }, | ||
1361 | |||
1362 | replace: function(element, html) { | ||
1363 | element = $(element); | ||
1364 | html = typeof html == 'undefined' ? '' : html.toString(); | ||
1365 | if (element.outerHTML) { | ||
1366 | element.outerHTML = html.stripScripts(); | ||
1367 | } else { | ||
1368 | var range = element.ownerDocument.createRange(); | ||
1369 | range.selectNodeContents(element); | ||
1370 | element.parentNode.replaceChild( | ||
1371 | range.createContextualFragment(html.stripScripts()), element); | ||
1372 | } | ||
1373 | setTimeout(function() {html.evalScripts()}, 10); | ||
1374 | return element; | ||
1375 | }, | ||
1376 | |||
1377 | inspect: function(element) { | ||
1378 | element = $(element); | ||
1379 | var result = '<' + element.tagName.toLowerCase(); | ||
1380 | $H({'id': 'id', 'className': 'class'}).each(function(pair) { | ||
1381 | var property = pair.first(), attribute = pair.last(); | ||
1382 | var value = (element[property] || '').toString(); | ||
1383 | if (value) result += ' ' + attribute + '=' + value.inspect(true); | ||
1384 | }); | ||
1385 | return result + '>'; | ||
1386 | }, | ||
1387 | |||
1388 | recursivelyCollect: function(element, property) { | ||
1389 | element = $(element); | ||
1390 | var elements = []; | ||
1391 | while (element = element[property]) | ||
1392 | if (element.nodeType == 1) | ||
1393 | elements.push(Element.extend(element)); | ||
1394 | return elements; | ||
1395 | }, | ||
1396 | |||
1397 | ancestors: function(element) { | ||
1398 | return $(element).recursivelyCollect('parentNode'); | ||
1399 | }, | ||
1400 | |||
1401 | descendants: function(element) { | ||
1402 | return $A($(element).getElementsByTagName('*')).each(Element.extend); | ||
1403 | }, | ||
1404 | |||
1405 | firstDescendant: function(element) { | ||
1406 | element = $(element).firstChild; | ||
1407 | while (element && element.nodeType != 1) element = element.nextSibling; | ||
1408 | return $(element); | ||
1409 | }, | ||
1410 | |||
1411 | immediateDescendants: function(element) { | ||
1412 | if (!(element = $(element).firstChild)) return []; | ||
1413 | while (element && element.nodeType != 1) element = element.nextSibling; | ||
1414 | if (element) return [element].concat($(element).nextSiblings()); | ||
1415 | return []; | ||
1416 | }, | ||
1417 | |||
1418 | previousSiblings: function(element) { | ||
1419 | return $(element).recursivelyCollect('previousSibling'); | ||
1420 | }, | ||
1421 | |||
1422 | nextSiblings: function(element) { | ||
1423 | return $(element).recursivelyCollect('nextSibling'); | ||
1424 | }, | ||
1425 | |||
1426 | siblings: function(element) { | ||
1427 | element = $(element); | ||
1428 | return element.previousSiblings().reverse().concat(element.nextSiblings()); | ||
1429 | }, | ||
1430 | |||
1431 | match: function(element, selector) { | ||
1432 | if (typeof selector == 'string') | ||
1433 | selector = new Selector(selector); | ||
1434 | return selector.match($(element)); | ||
1435 | }, | ||
1436 | |||
1437 | up: function(element, expression, index) { | ||
1438 | element = $(element); | ||
1439 | if (arguments.length == 1) return $(element.parentNode); | ||
1440 | var ancestors = element.ancestors(); | ||
1441 | return expression ? Selector.findElement(ancestors, expression, index) : | ||
1442 | ancestors[index || 0]; | ||
1443 | }, | ||
1444 | |||
1445 | down: function(element, expression, index) { | ||
1446 | element = $(element); | ||
1447 | if (arguments.length == 1) return element.firstDescendant(); | ||
1448 | var descendants = element.descendants(); | ||
1449 | return expression ? Selector.findElement(descendants, expression, index) : | ||
1450 | descendants[index || 0]; | ||
1451 | }, | ||
1452 | |||
1453 | previous: function(element, expression, index) { | ||
1454 | element = $(element); | ||
1455 | if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element)); | ||
1456 | var previousSiblings = element.previousSiblings(); | ||
1457 | return expression ? Selector.findElement(previousSiblings, expression, index) : | ||
1458 | previousSiblings[index || 0]; | ||
1459 | }, | ||
1460 | |||
1461 | next: function(element, expression, index) { | ||
1462 | element = $(element); | ||
1463 | if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element)); | ||
1464 | var nextSiblings = element.nextSiblings(); | ||
1465 | return expression ? Selector.findElement(nextSiblings, expression, index) : | ||
1466 | nextSiblings[index || 0]; | ||
1467 | }, | ||
1468 | |||
1469 | getElementsBySelector: function() { | ||
1470 | var args = $A(arguments), element = $(args.shift()); | ||
1471 | return Selector.findChildElements(element, args); | ||
1472 | }, | ||
1473 | |||
1474 | getElementsByClassName: function(element, className) { | ||
1475 | return document.getElementsByClassName(className, element); | ||
1476 | }, | ||
1477 | |||
1478 | readAttribute: function(element, name) { | ||
1479 | element = $(element); | ||
1480 | if (Prototype.Browser.IE) { | ||
1481 | if (!element.attributes) return null; | ||
1482 | var t = Element._attributeTranslations; | ||
1483 | if (t.values[name]) return t.values[name](element, name); | ||
1484 | if (t.names[name]) name = t.names[name]; | ||
1485 | var attribute = element.attributes[name]; | ||
1486 | return attribute ? attribute.nodeValue : null; | ||
1487 | } | ||
1488 | return element.getAttribute(name); | ||
1489 | }, | ||
1490 | |||
1491 | getHeight: function(element) { | ||
1492 | return $(element).getDimensions().height; | ||
1493 | }, | ||
1494 | |||
1495 | getWidth: function(element) { | ||
1496 | return $(element).getDimensions().width; | ||
1497 | }, | ||
1498 | |||
1499 | classNames: function(element) { | ||
1500 | return new Element.ClassNames(element); | ||
1501 | }, | ||
1502 | |||
1503 | hasClassName: function(element, className) { | ||
1504 | if (!(element = $(element))) return; | ||
1505 | var elementClassName = element.className; | ||
1506 | if (elementClassName.length == 0) return false; | ||
1507 | if (elementClassName == className || | ||
1508 | elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)"))) | ||
1509 | return true; | ||
1510 | return false; | ||
1511 | }, | ||
1512 | |||
1513 | addClassName: function(element, className) { | ||
1514 | if (!(element = $(element))) return; | ||
1515 | Element.classNames(element).add(className); | ||
1516 | return element; | ||
1517 | }, | ||
1518 | |||
1519 | removeClassName: function(element, className) { | ||
1520 | if (!(element = $(element))) return; | ||
1521 | Element.classNames(element).remove(className); | ||
1522 | return element; | ||
1523 | }, | ||
1524 | |||
1525 | toggleClassName: function(element, className) { | ||
1526 | if (!(element = $(element))) return; | ||
1527 | Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className); | ||
1528 | return element; | ||
1529 | }, | ||
1530 | |||
1531 | observe: function() { | ||
1532 | Event.observe.apply(Event, arguments); | ||
1533 | return $A(arguments).first(); | ||
1534 | }, | ||
1535 | |||
1536 | stopObserving: function() { | ||
1537 | Event.stopObserving.apply(Event, arguments); | ||
1538 | return $A(arguments).first(); | ||
1539 | }, | ||
1540 | |||
1541 | // removes whitespace-only text node children | ||
1542 | cleanWhitespace: function(element) { | ||
1543 | element = $(element); | ||
1544 | var node = element.firstChild; | ||
1545 | while (node) { | ||
1546 | var nextNode = node.nextSibling; | ||
1547 | if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) | ||
1548 | element.removeChild(node); | ||
1549 | node = nextNode; | ||
1550 | } | ||
1551 | return element; | ||
1552 | }, | ||
1553 | |||
1554 | empty: function(element) { | ||
1555 | return $(element).innerHTML.blank(); | ||
1556 | }, | ||
1557 | |||
1558 | descendantOf: function(element, ancestor) { | ||
1559 | element = $(element), ancestor = $(ancestor); | ||
1560 | while (element = element.parentNode) | ||
1561 | if (element == ancestor) return true; | ||
1562 | return false; | ||
1563 | }, | ||
1564 | |||
1565 | scrollTo: function(element) { | ||
1566 | element = $(element); | ||
1567 | var pos = Position.cumulativeOffset(element); | ||
1568 | window.scrollTo(pos[0], pos[1]); | ||
1569 | return element; | ||
1570 | }, | ||
1571 | |||
1572 | getStyle: function(element, style) { | ||
1573 | element = $(element); | ||
1574 | style = style == 'float' ? 'cssFloat' : style.camelize(); | ||
1575 | var value = element.style[style]; | ||
1576 | if (!value) { | ||
1577 | var css = document.defaultView.getComputedStyle(element, null); | ||
1578 | value = css ? css[style] : null; | ||
1579 | } | ||
1580 | if (style == 'opacity') return value ? parseFloat(value) : 1.0; | ||
1581 | return value == 'auto' ? null : value; | ||
1582 | }, | ||
1583 | |||
1584 | getOpacity: function(element) { | ||
1585 | return $(element).getStyle('opacity'); | ||
1586 | }, | ||
1587 | |||
1588 | setStyle: function(element, styles, camelized) { | ||
1589 | element = $(element); | ||
1590 | var elementStyle = element.style; | ||
1591 | |||
1592 | for (var property in styles) | ||
1593 | if (property == 'opacity') element.setOpacity(styles[property]) | ||
1594 | else | ||
1595 | elementStyle[(property == 'float' || property == 'cssFloat') ? | ||
1596 | (elementStyle.styleFloat === undefined ? 'cssFloat' : 'styleFloat') : | ||
1597 | (camelized ? property : property.camelize())] = styles[property]; | ||
1598 | |||
1599 | return element; | ||
1600 | }, | ||
1601 | |||
1602 | setOpacity: function(element, value) { | ||
1603 | element = $(element); | ||
1604 | element.style.opacity = (value == 1 || value === '') ? '' : | ||
1605 | (value < 0.00001) ? 0 : value; | ||
1606 | return element; | ||
1607 | }, | ||
1608 | |||
1609 | getDimensions: function(element) { | ||
1610 | element = $(element); | ||
1611 | var display = $(element).getStyle('display'); | ||
1612 | if (display != 'none' && display != null) // Safari bug | ||
1613 | return {width: element.offsetWidth, height: element.offsetHeight}; | ||
1614 | |||
1615 | // All *Width and *Height properties give 0 on elements with display none, | ||
1616 | // so enable the element temporarily | ||
1617 | var els = element.style; | ||
1618 | var originalVisibility = els.visibility; | ||
1619 | var originalPosition = els.position; | ||
1620 | var originalDisplay = els.display; | ||
1621 | els.visibility = 'hidden'; | ||
1622 | els.position = 'absolute'; | ||
1623 | els.display = 'block'; | ||
1624 | var originalWidth = element.clientWidth; | ||
1625 | var originalHeight = element.clientHeight; | ||
1626 | els.display = originalDisplay; | ||
1627 | els.position = originalPosition; | ||
1628 | els.visibility = originalVisibility; | ||
1629 | return {width: originalWidth, height: originalHeight}; | ||
1630 | }, | ||
1631 | |||
1632 | makePositioned: function(element) { | ||
1633 | element = $(element); | ||
1634 | var pos = Element.getStyle(element, 'position'); | ||
1635 | if (pos == 'static' || !pos) { | ||
1636 | element._madePositioned = true; | ||
1637 | element.style.position = 'relative'; | ||
1638 | // Opera returns the offset relative to the positioning context, when an | ||
1639 | // element is position relative but top and left have not been defined | ||
1640 | if (window.opera) { | ||
1641 | element.style.top = 0; | ||
1642 | element.style.left = 0; | ||
1643 | } | ||
1644 | } | ||
1645 | return element; | ||
1646 | }, | ||
1647 | |||
1648 | undoPositioned: function(element) { | ||
1649 | element = $(element); | ||
1650 | if (element._madePositioned) { | ||
1651 | element._madePositioned = undefined; | ||
1652 | element.style.position = | ||
1653 | element.style.top = | ||
1654 | element.style.left = | ||
1655 | element.style.bottom = | ||
1656 | element.style.right = ''; | ||
1657 | } | ||
1658 | return element; | ||
1659 | }, | ||
1660 | |||
1661 | makeClipping: function(element) { | ||
1662 | element = $(element); | ||
1663 | if (element._overflow) return element; | ||
1664 | element._overflow = element.style.overflow || 'auto'; | ||
1665 | if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden') | ||
1666 | element.style.overflow = 'hidden'; | ||
1667 | return element; | ||
1668 | }, | ||
1669 | |||
1670 | undoClipping: function(element) { | ||
1671 | element = $(element); | ||
1672 | if (!element._overflow) return element; | ||
1673 | element.style.overflow = element._overflow == 'auto' ? '' : element._overflow; | ||
1674 | element._overflow = null; | ||
1675 | return element; | ||
1676 | } | ||
1677 | }; | ||
1678 | |||
1679 | Object.extend(Element.Methods, { | ||
1680 | childOf: Element.Methods.descendantOf, | ||
1681 | childElements: Element.Methods.immediateDescendants | ||
1682 | }); | ||
1683 | |||
1684 | if (Prototype.Browser.Opera) { | ||
1685 | Element.Methods._getStyle = Element.Methods.getStyle; | ||
1686 | Element.Methods.getStyle = function(element, style) { | ||
1687 | switch(style) { | ||
1688 | case 'left': | ||
1689 | case 'top': | ||
1690 | case 'right': | ||
1691 | case 'bottom': | ||
1692 | if (Element._getStyle(element, 'position') == 'static') return null; | ||
1693 | default: return Element._getStyle(element, style); | ||
1694 | } | ||
1695 | }; | ||
1696 | } | ||
1697 | else if (Prototype.Browser.IE) { | ||
1698 | Element.Methods.getStyle = function(element, style) { | ||
1699 | element = $(element); | ||
1700 | style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize(); | ||
1701 | var value = element.style[style]; | ||
1702 | if (!value && element.currentStyle) value = element.currentStyle[style]; | ||
1703 | |||
1704 | if (style == 'opacity') { | ||
1705 | if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/)) | ||
1706 | if (value[1]) return parseFloat(value[1]) / 100; | ||
1707 | return 1.0; | ||
1708 | } | ||
1709 | |||
1710 | if (value == 'auto') { | ||
1711 | if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none')) | ||
1712 | return element['offset'+style.capitalize()] + 'px'; | ||
1713 | return null; | ||
1714 | } | ||
1715 | return value; | ||
1716 | }; | ||
1717 | |||
1718 | Element.Methods.setOpacity = function(element, value) { | ||
1719 | element = $(element); | ||
1720 | var filter = element.getStyle('filter'), style = element.style; | ||
1721 | if (value == 1 || value === '') { | ||
1722 | style.filter = filter.replace(/alpha\([^\)]*\)/gi,''); | ||
1723 | return element; | ||
1724 | } else if (value < 0.00001) value = 0; | ||
1725 | style.filter = filter.replace(/alpha\([^\)]*\)/gi, '') + | ||
1726 | 'alpha(opacity=' + (value * 100) + ')'; | ||
1727 | return element; | ||
1728 | }; | ||
1729 | |||
1730 | // IE is missing .innerHTML support for TABLE-related elements | ||
1731 | Element.Methods.update = function(element, html) { | ||
1732 | element = $(element); | ||
1733 | html = typeof html == 'undefined' ? '' : html.toString(); | ||
1734 | var tagName = element.tagName.toUpperCase(); | ||
1735 | if (['THEAD','TBODY','TR','TD'].include(tagName)) { | ||
1736 | var div = document.createElement('div'); | ||
1737 | switch (tagName) { | ||
1738 | case 'THEAD': | ||
1739 | case 'TBODY': | ||
1740 | div.innerHTML = '<table><tbody>' + html.stripScripts() + '</tbody></table>'; | ||
1741 | depth = 2; | ||
1742 | break; | ||
1743 | case 'TR': | ||
1744 | div.innerHTML = '<table><tbody><tr>' + html.stripScripts() + '</tr></tbody></table>'; | ||
1745 | depth = 3; | ||
1746 | break; | ||
1747 | case 'TD': | ||
1748 | div.innerHTML = '<table><tbody><tr><td>' + html.stripScripts() + '</td></tr></tbody></table>'; | ||
1749 | depth = 4; | ||
1750 | } | ||
1751 | $A(element.childNodes).each(function(node) { element.removeChild(node) }); | ||
1752 | depth.times(function() { div = div.firstChild }); | ||
1753 | $A(div.childNodes).each(function(node) { element.appendChild(node) }); | ||
1754 | } else { | ||
1755 | element.innerHTML = html.stripScripts(); | ||
1756 | } | ||
1757 | setTimeout(function() { html.evalScripts() }, 10); | ||
1758 | return element; | ||
1759 | } | ||
1760 | } | ||
1761 | else if (Prototype.Browser.Gecko) { | ||
1762 | Element.Methods.setOpacity = function(element, value) { | ||
1763 | element = $(element); | ||
1764 | element.style.opacity = (value == 1) ? 0.999999 : | ||
1765 | (value === '') ? '' : (value < 0.00001) ? 0 : value; | ||
1766 | return element; | ||
1767 | }; | ||
1768 | } | ||
1769 | |||
1770 | Element._attributeTranslations = { | ||
1771 | names: { | ||
1772 | colspan: "colSpan", | ||
1773 | rowspan: "rowSpan", | ||
1774 | valign: "vAlign", | ||
1775 | datetime: "dateTime", | ||
1776 | accesskey: "accessKey", | ||
1777 | tabindex: "tabIndex", | ||
1778 | enctype: "encType", | ||
1779 | maxlength: "maxLength", | ||
1780 | readonly: "readOnly", | ||
1781 | longdesc: "longDesc" | ||
1782 | }, | ||
1783 | values: { | ||
1784 | _getAttr: function(element, attribute) { | ||
1785 | return element.getAttribute(attribute, 2); | ||
1786 | }, | ||
1787 | _flag: function(element, attribute) { | ||
1788 | return $(element).hasAttribute(attribute) ? attribute : null; | ||
1789 | }, | ||
1790 | style: function(element) { | ||
1791 | return element.style.cssText.toLowerCase(); | ||
1792 | }, | ||
1793 | title: function(element) { | ||
1794 | var node = element.getAttributeNode('title'); | ||
1795 | return node.specified ? node.nodeValue : null; | ||
1796 | } | ||
1797 | } | ||
1798 | }; | ||
1799 | |||
1800 | (function() { | ||
1801 | Object.extend(this, { | ||
1802 | href: this._getAttr, | ||
1803 | src: this._getAttr, | ||
1804 | type: this._getAttr, | ||
1805 | disabled: this._flag, | ||
1806 | checked: this._flag, | ||
1807 | readonly: this._flag, | ||
1808 | multiple: this._flag | ||
1809 | }); | ||
1810 | }).call(Element._attributeTranslations.values); | ||
1811 | |||
1812 | Element.Methods.Simulated = { | ||
1813 | hasAttribute: function(element, attribute) { | ||
1814 | var t = Element._attributeTranslations, node; | ||
1815 | attribute = t.names[attribute] || attribute; | ||
1816 | node = $(element).getAttributeNode(attribute); | ||
1817 | return node && node.specified; | ||
1818 | } | ||
1819 | }; | ||
1820 | |||
1821 | Element.Methods.ByTag = {}; | ||
1822 | |||
1823 | Object.extend(Element, Element.Methods); | ||
1824 | |||
1825 | if (!Prototype.BrowserFeatures.ElementExtensions && | ||
1826 | document.createElement('div').__proto__) { | ||
1827 | window.HTMLElement = {}; | ||
1828 | window.HTMLElement.prototype = document.createElement('div').__proto__; | ||
1829 | Prototype.BrowserFeatures.ElementExtensions = true; | ||
1830 | } | ||
1831 | |||
1832 | Element.hasAttribute = function(element, attribute) { | ||
1833 | if (element.hasAttribute) return element.hasAttribute(attribute); | ||
1834 | return Element.Methods.Simulated.hasAttribute(element, attribute); | ||
1835 | }; | ||
1836 | |||
1837 | Element.addMethods = function(methods) { | ||
1838 | var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag; | ||
1839 | |||
1840 | if (!methods) { | ||
1841 | Object.extend(Form, Form.Methods); | ||
1842 | Object.extend(Form.Element, Form.Element.Methods); | ||
1843 | Object.extend(Element.Methods.ByTag, { | ||
1844 | "FORM": Object.clone(Form.Methods), | ||
1845 | "INPUT": Object.clone(Form.Element.Methods), | ||
1846 | "SELECT": Object.clone(Form.Element.Methods), | ||
1847 | "TEXTAREA": Object.clone(Form.Element.Methods) | ||
1848 | }); | ||
1849 | } | ||
1850 | |||
1851 | if (arguments.length == 2) { | ||
1852 | var tagName = methods; | ||
1853 | methods = arguments[1]; | ||
1854 | } | ||
1855 | |||
1856 | if (!tagName) Object.extend(Element.Methods, methods || {}); | ||
1857 | else { | ||
1858 | if (tagName.constructor == Array) tagName.each(extend); | ||
1859 | else extend(tagName); | ||
1860 | } | ||
1861 | |||
1862 | function extend(tagName) { | ||
1863 | tagName = tagName.toUpperCase(); | ||
1864 | if (!Element.Methods.ByTag[tagName]) | ||
1865 | Element.Methods.ByTag[tagName] = {}; | ||
1866 | Object.extend(Element.Methods.ByTag[tagName], methods); | ||
1867 | } | ||
1868 | |||
1869 | function copy(methods, destination, onlyIfAbsent) { | ||
1870 | onlyIfAbsent = onlyIfAbsent || false; | ||
1871 | var cache = Element.extend.cache; | ||
1872 | for (var property in methods) { | ||
1873 | var value = methods[property]; | ||
1874 | if (!onlyIfAbsent || !(property in destination)) | ||
1875 | destination[property] = cache.findOrStore(value); | ||
1876 | } | ||
1877 | } | ||
1878 | |||
1879 | function findDOMClass(tagName) { | ||
1880 | var klass; | ||
1881 | var trans = { | ||
1882 | "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph", | ||
1883 | "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList", | ||
1884 | "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading", | ||
1885 | "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote", | ||
1886 | "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION": | ||
1887 | "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD": | ||
1888 | "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR": | ||
1889 | "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET": | ||
1890 | "FrameSet", "IFRAME": "IFrame" | ||
1891 | }; | ||
1892 | if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element'; | ||
1893 | if (window[klass]) return window[klass]; | ||
1894 | klass = 'HTML' + tagName + 'Element'; | ||
1895 | if (window[klass]) return window[klass]; | ||
1896 | klass = 'HTML' + tagName.capitalize() + 'Element'; | ||
1897 | if (window[klass]) return window[klass]; | ||
1898 | |||
1899 | window[klass] = {}; | ||
1900 | window[klass].prototype = document.createElement(tagName).__proto__; | ||
1901 | return window[klass]; | ||
1902 | } | ||
1903 | |||
1904 | if (F.ElementExtensions) { | ||
1905 | copy(Element.Methods, HTMLElement.prototype); | ||
1906 | copy(Element.Methods.Simulated, HTMLElement.prototype, true); | ||
1907 | } | ||
1908 | |||
1909 | if (F.SpecificElementExtensions) { | ||
1910 | for (var tag in Element.Methods.ByTag) { | ||
1911 | var klass = findDOMClass(tag); | ||
1912 | if (typeof klass == "undefined") continue; | ||
1913 | copy(T[tag], klass.prototype); | ||
1914 | } | ||
1915 | } | ||
1916 | |||
1917 | Object.extend(Element, Element.Methods); | ||
1918 | delete Element.ByTag; | ||
1919 | }; | ||
1920 | |||
1921 | var Toggle = { display: Element.toggle }; | ||
1922 | |||
1923 | /*--------------------------------------------------------------------------*/ | ||
1924 | |||
1925 | Abstract.Insertion = function(adjacency) { | ||
1926 | this.adjacency = adjacency; | ||
1927 | } | ||
1928 | |||
1929 | Abstract.Insertion.prototype = { | ||
1930 | initialize: function(element, content) { | ||
1931 | this.element = $(element); | ||
1932 | this.content = content.stripScripts(); | ||
1933 | |||
1934 | if (this.adjacency && this.element.insertAdjacentHTML) { | ||
1935 | try { | ||
1936 | this.element.insertAdjacentHTML(this.adjacency, this.content); | ||
1937 | } catch (e) { | ||
1938 | var tagName = this.element.tagName.toUpperCase(); | ||
1939 | if (['TBODY', 'TR'].include(tagName)) { | ||
1940 | this.insertContent(this.contentFromAnonymousTable()); | ||
1941 | } else { | ||
1942 | throw e; | ||
1943 | } | ||
1944 | } | ||
1945 | } else { | ||
1946 | this.range = this.element.ownerDocument.createRange(); | ||
1947 | if (this.initializeRange) this.initializeRange(); | ||
1948 | this.insertContent([this.range.createContextualFragment(this.content)]); | ||
1949 | } | ||
1950 | |||
1951 | setTimeout(function() {content.evalScripts()}, 10); | ||
1952 | }, | ||
1953 | |||
1954 | contentFromAnonymousTable: function() { | ||
1955 | var div = document.createElement('div'); | ||
1956 | div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>'; | ||
1957 | return $A(div.childNodes[0].childNodes[0].childNodes); | ||
1958 | } | ||
1959 | } | ||
1960 | |||
1961 | var Insertion = new Object(); | ||
1962 | |||
1963 | Insertion.Before = Class.create(); | ||
1964 | Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), { | ||
1965 | initializeRange: function() { | ||
1966 | this.range.setStartBefore(this.element); | ||
1967 | }, | ||
1968 | |||
1969 | insertContent: function(fragments) { | ||
1970 | fragments.each((function(fragment) { | ||
1971 | this.element.parentNode.insertBefore(fragment, this.element); | ||
1972 | }).bind(this)); | ||
1973 | } | ||
1974 | }); | ||
1975 | |||
1976 | Insertion.Top = Class.create(); | ||
1977 | Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), { | ||
1978 | initializeRange: function() { | ||
1979 | this.range.selectNodeContents(this.element); | ||
1980 | this.range.collapse(true); | ||
1981 | }, | ||
1982 | |||
1983 | insertContent: function(fragments) { | ||
1984 | fragments.reverse(false).each((function(fragment) { | ||
1985 | this.element.insertBefore(fragment, this.element.firstChild); | ||
1986 | }).bind(this)); | ||
1987 | } | ||
1988 | }); | ||
1989 | |||
1990 | Insertion.Bottom = Class.create(); | ||
1991 | Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), { | ||
1992 | initializeRange: function() { | ||
1993 | this.range.selectNodeContents(this.element); | ||
1994 | this.range.collapse(this.element); | ||
1995 | }, | ||
1996 | |||
1997 | insertContent: function(fragments) { | ||
1998 | fragments.each((function(fragment) { | ||
1999 | this.element.appendChild(fragment); | ||
2000 | }).bind(this)); | ||
2001 | } | ||
2002 | }); | ||
2003 | |||
2004 | Insertion.After = Class.create(); | ||
2005 | Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), { | ||
2006 | initializeRange: function() { | ||
2007 | this.range.setStartAfter(this.element); | ||
2008 | }, | ||
2009 | |||
2010 | insertContent: function(fragments) { | ||
2011 | fragments.each((function(fragment) { | ||
2012 | this.element.parentNode.insertBefore(fragment, | ||
2013 | this.element.nextSibling); | ||
2014 | }).bind(this)); | ||
2015 | } | ||
2016 | }); | ||
2017 | |||
2018 | /*--------------------------------------------------------------------------*/ | ||
2019 | |||
2020 | Element.ClassNames = Class.create(); | ||
2021 | Element.ClassNames.prototype = { | ||
2022 | initialize: function(element) { | ||
2023 | this.element = $(element); | ||
2024 | }, | ||
2025 | |||
2026 | _each: function(iterator) { | ||
2027 | this.element.className.split(/\s+/).select(function(name) { | ||
2028 | return name.length > 0; | ||
2029 | })._each(iterator); | ||
2030 | }, | ||
2031 | |||
2032 | set: function(className) { | ||
2033 | this.element.className = className; | ||
2034 | }, | ||
2035 | |||
2036 | add: function(classNameToAdd) { | ||
2037 | if (this.include(classNameToAdd)) return; | ||
2038 | this.set($A(this).concat(classNameToAdd).join(' ')); | ||
2039 | }, | ||
2040 | |||
2041 | remove: function(classNameToRemove) { | ||
2042 | if (!this.include(classNameToRemove)) return; | ||
2043 | this.set($A(this).without(classNameToRemove).join(' ')); | ||
2044 | }, | ||
2045 | |||
2046 | toString: function() { | ||
2047 | return $A(this).join(' '); | ||
2048 | } | ||
2049 | }; | ||
2050 | |||
2051 | Object.extend(Element.ClassNames.prototype, Enumerable); | ||
2052 | /* Portions of the Selector class are derived from Jack Slocum’s DomQuery, | ||
2053 | * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style | ||
2054 | * license. Please see http://www.yui-ext.com/ for more information. */ | ||
2055 | |||
2056 | var Selector = Class.create(); | ||
2057 | |||
2058 | Selector.prototype = { | ||
2059 | initialize: function(expression) { | ||
2060 | this.expression = expression.strip(); | ||
2061 | this.compileMatcher(); | ||
2062 | }, | ||
2063 | |||
2064 | compileMatcher: function() { | ||
2065 | // Selectors with namespaced attributes can't use the XPath version | ||
2066 | if (Prototype.BrowserFeatures.XPath && !(/\[[\w-]*?:/).test(this.expression)) | ||
2067 | return this.compileXPathMatcher(); | ||
2068 | |||
2069 | var e = this.expression, ps = Selector.patterns, h = Selector.handlers, | ||
2070 | c = Selector.criteria, le, p, m; | ||
2071 | |||
2072 | if (Selector._cache[e]) { | ||
2073 | this.matcher = Selector._cache[e]; return; | ||
2074 | } | ||
2075 | this.matcher = ["this.matcher = function(root) {", | ||
2076 | "var r = root, h = Selector.handlers, c = false, n;"]; | ||
2077 | |||
2078 | while (e && le != e && (/\S/).test(e)) { | ||
2079 | le = e; | ||
2080 | for (var i in ps) { | ||
2081 | p = ps[i]; | ||
2082 | if (m = e.match(p)) { | ||
2083 | this.matcher.push(typeof c[i] == 'function' ? c[i](m) : | ||
2084 | new Template(c[i]).evaluate(m)); | ||
2085 | e = e.replace(m[0], ''); | ||
2086 | break; | ||
2087 | } | ||
2088 | } | ||
2089 | } | ||
2090 | |||
2091 | this.matcher.push("return h.unique(n);\n}"); | ||
2092 | eval(this.matcher.join('\n')); | ||
2093 | Selector._cache[this.expression] = this.matcher; | ||
2094 | }, | ||
2095 | |||
2096 | compileXPathMatcher: function() { | ||
2097 | var e = this.expression, ps = Selector.patterns, | ||
2098 | x = Selector.xpath, le, m; | ||
2099 | |||
2100 | if (Selector._cache[e]) { | ||
2101 | this.xpath = Selector._cache[e]; return; | ||
2102 | } | ||
2103 | |||
2104 | this.matcher = ['.//*']; | ||
2105 | while (e && le != e && (/\S/).test(e)) { | ||
2106 | le = e; | ||
2107 | for (var i in ps) { | ||
2108 | if (m = e.match(ps[i])) { | ||
2109 | this.matcher.push(typeof x[i] == 'function' ? x[i](m) : | ||
2110 | new Template(x[i]).evaluate(m)); | ||
2111 | e = e.replace(m[0], ''); | ||
2112 | break; | ||
2113 | } | ||
2114 | } | ||
2115 | } | ||
2116 | |||
2117 | this.xpath = this.matcher.join(''); | ||
2118 | Selector._cache[this.expression] = this.xpath; | ||
2119 | }, | ||
2120 | |||
2121 | findElements: function(root) { | ||
2122 | root = root || document; | ||
2123 | if (this.xpath) return document._getElementsByXPath(this.xpath, root); | ||
2124 | return this.matcher(root); | ||
2125 | }, | ||
2126 | |||
2127 | match: function(element) { | ||
2128 | return this.findElements(document).include(element); | ||
2129 | }, | ||
2130 | |||
2131 | toString: function() { | ||
2132 | return this.expression; | ||
2133 | }, | ||
2134 | |||
2135 | inspect: function() { | ||
2136 | return "#<Selector:" + this.expression.inspect() + ">"; | ||
2137 | } | ||
2138 | }; | ||
2139 | |||
2140 | Object.extend(Selector, { | ||
2141 | _cache: {}, | ||
2142 | |||
2143 | xpath: { | ||
2144 | descendant: "//*", | ||
2145 | child: "/*", | ||
2146 | adjacent: "/following-sibling::*[1]", | ||
2147 | laterSibling: '/following-sibling::*', | ||
2148 | tagName: function(m) { | ||
2149 | if (m[1] == '*') return ''; | ||
2150 | return "[local-name()='" + m[1].toLowerCase() + | ||
2151 | "' or local-name()='" + m[1].toUpperCase() + "']"; | ||
2152 | }, | ||
2153 | className: "[contains(concat(' ', @class, ' '), ' #{1} ')]", | ||
2154 | id: "[@id='#{1}']", | ||
2155 | attrPresence: "[@#{1}]", | ||
2156 | attr: function(m) { | ||
2157 | m[3] = m[5] || m[6]; | ||
2158 | return new Template(Selector.xpath.operators[m[2]]).evaluate(m); | ||
2159 | }, | ||
2160 | pseudo: function(m) { | ||
2161 | var h = Selector.xpath.pseudos[m[1]]; | ||
2162 | if (!h) return ''; | ||
2163 | if (typeof h === 'function') return h(m); | ||
2164 | return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m); | ||
2165 | }, | ||
2166 | operators: { | ||
2167 | '=': "[@#{1}='#{3}']", | ||
2168 | '!=': "[@#{1}!='#{3}']", | ||
2169 | '^=': "[starts-with(@#{1}, '#{3}')]", | ||
2170 | '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']", | ||
2171 | '*=': "[contains(@#{1}, '#{3}')]", | ||
2172 | '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]", | ||
2173 | '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]" | ||
2174 | }, | ||
2175 | pseudos: { | ||
2176 | 'first-child': '[not(preceding-sibling::*)]', | ||
2177 | 'last-child': '[not(following-sibling::*)]', | ||
2178 | 'only-child': '[not(preceding-sibling::* or following-sibling::*)]', | ||
2179 | 'empty': "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]", | ||
2180 | 'checked': "[@checked]", | ||
2181 | 'disabled': "[@disabled]", | ||
2182 | 'enabled': "[not(@disabled)]", | ||
2183 | 'not': function(m) { | ||
2184 | var e = m[6], p = Selector.patterns, | ||
2185 | x = Selector.xpath, le, m, v; | ||
2186 | |||
2187 | var exclusion = []; | ||
2188 | while (e && le != e && (/\S/).test(e)) { | ||
2189 | le = e; | ||
2190 | for (var i in p) { | ||
2191 | if (m = e.match(p[i])) { | ||
2192 | v = typeof x[i] == 'function' ? x[i](m) : new Template(x[i]).evaluate(m); | ||
2193 | exclusion.push("(" + v.substring(1, v.length - 1) + ")"); | ||
2194 | e = e.replace(m[0], ''); | ||
2195 | break; | ||
2196 | } | ||
2197 | } | ||
2198 | } | ||
2199 | return "[not(" + exclusion.join(" and ") + ")]"; | ||
2200 | }, | ||
2201 | 'nth-child': function(m) { | ||
2202 | return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m); | ||
2203 | }, | ||
2204 | 'nth-last-child': function(m) { | ||
2205 | return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m); | ||
2206 | }, | ||
2207 | 'nth-of-type': function(m) { | ||
2208 | return Selector.xpath.pseudos.nth("position() ", m); | ||
2209 | }, | ||
2210 | 'nth-last-of-type': function(m) { | ||
2211 | return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m); | ||
2212 | }, | ||
2213 | 'first-of-type': function(m) { | ||
2214 | m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m); | ||
2215 | }, | ||
2216 | 'last-of-type': function(m) { | ||
2217 | m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m); | ||
2218 | }, | ||
2219 | 'only-of-type': function(m) { | ||
2220 | var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m); | ||
2221 | }, | ||
2222 | nth: function(fragment, m) { | ||
2223 | var mm, formula = m[6], predicate; | ||
2224 | if (formula == 'even') formula = '2n+0'; | ||
2225 | if (formula == 'odd') formula = '2n+1'; | ||
2226 | if (mm = formula.match(/^(\d+)$/)) // digit only | ||
2227 | return '[' + fragment + "= " + mm[1] + ']'; | ||
2228 | if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b | ||
2229 | if (mm[1] == "-") mm[1] = -1; | ||
2230 | var a = mm[1] ? Number(mm[1]) : 1; | ||
2231 | var b = mm[2] ? Number(mm[2]) : 0; | ||
2232 | predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " + | ||
2233 | "((#{fragment} - #{b}) div #{a} >= 0)]"; | ||
2234 | return new Template(predicate).evaluate({ | ||
2235 | fragment: fragment, a: a, b: b }); | ||
2236 | } | ||
2237 | } | ||
2238 | } | ||
2239 | }, | ||
2240 | |||
2241 | criteria: { | ||
2242 | tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;', | ||
2243 | className: 'n = h.className(n, r, "#{1}", c); c = false;', | ||
2244 | id: 'n = h.id(n, r, "#{1}", c); c = false;', | ||
2245 | attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;', | ||
2246 | attr: function(m) { | ||
2247 | m[3] = (m[5] || m[6]); | ||
2248 | return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m); | ||
2249 | }, | ||
2250 | pseudo: function(m) { | ||
2251 | if (m[6]) m[6] = m[6].replace(/"/g, '\\"'); | ||
2252 | return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m); | ||
2253 | }, | ||
2254 | descendant: 'c = "descendant";', | ||
2255 | child: 'c = "child";', | ||
2256 | adjacent: 'c = "adjacent";', | ||
2257 | laterSibling: 'c = "laterSibling";' | ||
2258 | }, | ||
2259 | |||
2260 | patterns: { | ||
2261 | // combinators must be listed first | ||
2262 | // (and descendant needs to be last combinator) | ||
2263 | laterSibling: /^\s*~\s*/, | ||
2264 | child: /^\s*>\s*/, | ||
2265 | adjacent: /^\s*\+\s*/, | ||
2266 | descendant: /^\s/, | ||
2267 | |||
2268 | // selectors follow | ||
2269 | tagName: /^\s*(\*|[\w\-]+)(\b|$)?/, | ||
2270 | id: /^#([\w\-\*]+)(\b|$)/, | ||
2271 | className: /^\.([\w\-\*]+)(\b|$)/, | ||
2272 | pseudo: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|\s|(?=:))/, | ||
2273 | attrPresence: /^\[([\w]+)\]/, | ||
2274 | attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\]]*?)\4|([^'"][^\]]*?)))?\]/ | ||
2275 | }, | ||
2276 | |||
2277 | handlers: { | ||
2278 | // UTILITY FUNCTIONS | ||
2279 | // joins two collections | ||
2280 | concat: function(a, b) { | ||
2281 | for (var i = 0, node; node = b[i]; i++) | ||
2282 | a.push(node); | ||
2283 | return a; | ||
2284 | }, | ||
2285 | |||
2286 | // marks an array of nodes for counting | ||
2287 | mark: function(nodes) { | ||
2288 | for (var i = 0, node; node = nodes[i]; i++) | ||
2289 | node._counted = true; | ||
2290 | return nodes; | ||
2291 | }, | ||
2292 | |||
2293 | unmark: function(nodes) { | ||
2294 | for (var i = 0, node; node = nodes[i]; i++) | ||
2295 | node._counted = undefined; | ||
2296 | return nodes; | ||
2297 | }, | ||
2298 | |||
2299 | // mark each child node with its position (for nth calls) | ||
2300 | // "ofType" flag indicates whether we're indexing for nth-of-type | ||
2301 | // rather than nth-child | ||
2302 | index: function(parentNode, reverse, ofType) { | ||
2303 | parentNode._counted = true; | ||
2304 | if (reverse) { | ||
2305 | for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) { | ||
2306 | node = nodes[i]; | ||
2307 | if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++; | ||
2308 | } | ||
2309 | } else { | ||
2310 | for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++) | ||
2311 | if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++; | ||
2312 | } | ||
2313 | }, | ||
2314 | |||
2315 | // filters out duplicates and extends all nodes | ||
2316 | unique: function(nodes) { | ||
2317 | if (nodes.length == 0) return nodes; | ||
2318 | var results = [], n; | ||
2319 | for (var i = 0, l = nodes.length; i < l; i++) | ||
2320 | if (!(n = nodes[i])._counted) { | ||
2321 | n._counted = true; | ||
2322 | results.push(Element.extend(n)); | ||
2323 | } | ||
2324 | return Selector.handlers.unmark(results); | ||
2325 | }, | ||
2326 | |||
2327 | // COMBINATOR FUNCTIONS | ||
2328 | descendant: function(nodes) { | ||
2329 | var h = Selector.handlers; | ||
2330 | for (var i = 0, results = [], node; node = nodes[i]; i++) | ||
2331 | h.concat(results, node.getElementsByTagName('*')); | ||
2332 | return results; | ||
2333 | }, | ||
2334 | |||
2335 | child: function(nodes) { | ||
2336 | var h = Selector.handlers; | ||
2337 | for (var i = 0, results = [], node; node = nodes[i]; i++) { | ||
2338 | for (var j = 0, children = [], child; child = node.childNodes[j]; j++) | ||
2339 | if (child.nodeType == 1 && child.tagName != '!') results.push(child); | ||
2340 | } | ||
2341 | return results; | ||
2342 | }, | ||
2343 | |||
2344 | adjacent: function(nodes) { | ||
2345 | for (var i = 0, results = [], node; node = nodes[i]; i++) { | ||
2346 | var next = this.nextElementSibling(node); | ||
2347 | if (next) results.push(next); | ||
2348 | } | ||
2349 | return results; | ||
2350 | }, | ||
2351 | |||
2352 | laterSibling: function(nodes) { | ||
2353 | var h = Selector.handlers; | ||
2354 | for (var i = 0, results = [], node; node = nodes[i]; i++) | ||
2355 | h.concat(results, Element.nextSiblings(node)); | ||
2356 | return results; | ||
2357 | }, | ||
2358 | |||
2359 | nextElementSibling: function(node) { | ||
2360 | while (node = node.nextSibling) | ||
2361 | if (node.nodeType == 1) return node; | ||
2362 | return null; | ||
2363 | }, | ||
2364 | |||
2365 | previousElementSibling: function(node) { | ||
2366 | while (node = node.previousSibling) | ||
2367 | if (node.nodeType == 1) return node; | ||
2368 | return null; | ||
2369 | }, | ||
2370 | |||
2371 | // TOKEN FUNCTIONS | ||
2372 | tagName: function(nodes, root, tagName, combinator) { | ||
2373 | tagName = tagName.toUpperCase(); | ||
2374 | var results = [], h = Selector.handlers; | ||
2375 | if (nodes) { | ||
2376 | if (combinator) { | ||
2377 | // fastlane for ordinary descendant combinators | ||
2378 | if (combinator == "descendant") { | ||
2379 | for (var i = 0, node; node = nodes[i]; i++) | ||
2380 | h.concat(results, node.getElementsByTagName(tagName)); | ||
2381 | return results; | ||
2382 | } else nodes = this[combinator](nodes); | ||
2383 | if (tagName == "*") return nodes; | ||
2384 | } | ||
2385 | for (var i = 0, node; node = nodes[i]; i++) | ||
2386 | if (node.tagName.toUpperCase() == tagName) results.push(node); | ||
2387 | return results; | ||
2388 | } else return root.getElementsByTagName(tagName); | ||
2389 | }, | ||
2390 | |||
2391 | id: function(nodes, root, id, combinator) { | ||
2392 | var targetNode = $(id), h = Selector.handlers; | ||
2393 | if (!nodes && root == document) return targetNode ? [targetNode] : []; | ||
2394 | if (nodes) { | ||
2395 | if (combinator) { | ||
2396 | if (combinator == 'child') { | ||
2397 | for (var i = 0, node; node = nodes[i]; i++) | ||
2398 | if (targetNode.parentNode == node) return [targetNode]; | ||
2399 | } else if (combinator == 'descendant') { | ||
2400 | for (var i = 0, node; node = nodes[i]; i++) | ||
2401 | if (Element.descendantOf(targetNode, node)) return [targetNode]; | ||
2402 | } else if (combinator == 'adjacent') { | ||
2403 | for (var i = 0, node; node = nodes[i]; i++) | ||
2404 | if (Selector.handlers.previousElementSibling(targetNode) == node) | ||
2405 | return [targetNode]; | ||
2406 | } else nodes = h[combinator](nodes); | ||
2407 | } | ||
2408 | for (var i = 0, node; node = nodes[i]; i++) | ||
2409 | if (node == targetNode) return [targetNode]; | ||
2410 | return []; | ||
2411 | } | ||
2412 | return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : []; | ||
2413 | }, | ||
2414 | |||
2415 | className: function(nodes, root, className, combinator) { | ||
2416 | if (nodes && combinator) nodes = this[combinator](nodes); | ||
2417 | return Selector.handlers.byClassName(nodes, root, className); | ||
2418 | }, | ||
2419 | |||
2420 | byClassName: function(nodes, root, className) { | ||
2421 | if (!nodes) nodes = Selector.handlers.descendant([root]); | ||
2422 | var needle = ' ' + className + ' '; | ||
2423 | for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) { | ||
2424 | nodeClassName = node.className; | ||
2425 | if (nodeClassName.length == 0) continue; | ||
2426 | if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle)) | ||
2427 | results.push(node); | ||
2428 | } | ||
2429 | return results; | ||
2430 | }, | ||
2431 | |||
2432 | attrPresence: function(nodes, root, attr) { | ||
2433 | var results = []; | ||
2434 | for (var i = 0, node; node = nodes[i]; i++) | ||
2435 | if (Element.hasAttribute(node, attr)) results.push(node); | ||
2436 | return results; | ||
2437 | }, | ||
2438 | |||
2439 | attr: function(nodes, root, attr, value, operator) { | ||
2440 | if (!nodes) nodes = root.getElementsByTagName("*"); | ||
2441 | var handler = Selector.operators[operator], results = []; | ||
2442 | for (var i = 0, node; node = nodes[i]; i++) { | ||
2443 | var nodeValue = Element.readAttribute(node, attr); | ||
2444 | if (nodeValue === null) continue; | ||
2445 | if (handler(nodeValue, value)) results.push(node); | ||
2446 | } | ||
2447 | return results; | ||
2448 | }, | ||
2449 | |||
2450 | pseudo: function(nodes, name, value, root, combinator) { | ||
2451 | if (nodes && combinator) nodes = this[combinator](nodes); | ||
2452 | if (!nodes) nodes = root.getElementsByTagName("*"); | ||
2453 | return Selector.pseudos[name](nodes, value, root); | ||
2454 | } | ||
2455 | }, | ||
2456 | |||
2457 | pseudos: { | ||
2458 | 'first-child': function(nodes, value, root) { | ||
2459 | for (var i = 0, results = [], node; node = nodes[i]; i++) { | ||
2460 | if (Selector.handlers.previousElementSibling(node)) continue; | ||
2461 | results.push(node); | ||
2462 | } | ||
2463 | return results; | ||
2464 | }, | ||
2465 | 'last-child': function(nodes, value, root) { | ||
2466 | for (var i = 0, results = [], node; node = nodes[i]; i++) { | ||
2467 | if (Selector.handlers.nextElementSibling(node)) continue; | ||
2468 | results.push(node); | ||
2469 | } | ||
2470 | return results; | ||
2471 | }, | ||
2472 | 'only-child': function(nodes, value, root) { | ||
2473 | var h = Selector.handlers; | ||
2474 | for (var i = 0, results = [], node; node = nodes[i]; i++) | ||
2475 | if (!h.previousElementSibling(node) && !h.nextElementSibling(node)) | ||
2476 | results.push(node); | ||
2477 | return results; | ||
2478 | }, | ||
2479 | 'nth-child': function(nodes, formula, root) { | ||
2480 | return Selector.pseudos.nth(nodes, formula, root); | ||
2481 | }, | ||
2482 | 'nth-last-child': function(nodes, formula, root) { | ||
2483 | return Selector.pseudos.nth(nodes, formula, root, true); | ||
2484 | }, | ||
2485 | 'nth-of-type': function(nodes, formula, root) { | ||
2486 | return Selector.pseudos.nth(nodes, formula, root, false, true); | ||
2487 | }, | ||
2488 | 'nth-last-of-type': function(nodes, formula, root) { | ||
2489 | return Selector.pseudos.nth(nodes, formula, root, true, true); | ||
2490 | }, | ||
2491 | 'first-of-type': function(nodes, formula, root) { | ||
2492 | return Selector.pseudos.nth(nodes, "1", root, false, true); | ||
2493 | }, | ||
2494 | 'last-of-type': function(nodes, formula, root) { | ||
2495 | return Selector.pseudos.nth(nodes, "1", root, true, true); | ||
2496 | }, | ||
2497 | 'only-of-type': function(nodes, formula, root) { | ||
2498 | var p = Selector.pseudos; | ||
2499 | return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root); | ||
2500 | }, | ||
2501 | |||
2502 | // handles the an+b logic | ||
2503 | getIndices: function(a, b, total) { | ||
2504 | if (a == 0) return b > 0 ? [b] : []; | ||
2505 | return $R(1, total).inject([], function(memo, i) { | ||
2506 | if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i); | ||
2507 | return memo; | ||
2508 | }); | ||
2509 | }, | ||
2510 | |||
2511 | // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type | ||
2512 | nth: function(nodes, formula, root, reverse, ofType) { | ||
2513 | if (nodes.length == 0) return []; | ||
2514 | if (formula == 'even') formula = '2n+0'; | ||
2515 | if (formula == 'odd') formula = '2n+1'; | ||
2516 | var h = Selector.handlers, results = [], indexed = [], m; | ||
2517 | h.mark(nodes); | ||
2518 | for (var i = 0, node; node = nodes[i]; i++) { | ||
2519 | if (!node.parentNode._counted) { | ||
2520 | h.index(node.parentNode, reverse, ofType); | ||
2521 | indexed.push(node.parentNode); | ||
2522 | } | ||
2523 | } | ||
2524 | if (formula.match(/^\d+$/)) { // just a number | ||
2525 | formula = Number(formula); | ||
2526 | for (var i = 0, node; node = nodes[i]; i++) | ||
2527 | if (node.nodeIndex == formula) results.push(node); | ||
2528 | } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b | ||
2529 | if (m[1] == "-") m[1] = -1; | ||
2530 | var a = m[1] ? Number(m[1]) : 1; | ||
2531 | var b = m[2] ? Number(m[2]) : 0; | ||
2532 | var indices = Selector.pseudos.getIndices(a, b, nodes.length); | ||
2533 | for (var i = 0, node, l = indices.length; node = nodes[i]; i++) { | ||
2534 | for (var j = 0; j < l; j++) | ||
2535 | if (node.nodeIndex == indices[j]) results.push(node); | ||
2536 | } | ||
2537 | } | ||
2538 | h.unmark(nodes); | ||
2539 | h.unmark(indexed); | ||
2540 | return results; | ||
2541 | }, | ||
2542 | |||
2543 | 'empty': function(nodes, value, root) { | ||
2544 | for (var i = 0, results = [], node; node = nodes[i]; i++) { | ||
2545 | // IE treats comments as element nodes | ||
2546 | if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue; | ||
2547 | results.push(node); | ||
2548 | } | ||
2549 | return results; | ||
2550 | }, | ||
2551 | |||
2552 | 'not': function(nodes, selector, root) { | ||
2553 | var h = Selector.handlers, selectorType, m; | ||
2554 | var exclusions = new Selector(selector).findElements(root); | ||
2555 | h.mark(exclusions); | ||
2556 | for (var i = 0, results = [], node; node = nodes[i]; i++) | ||
2557 | if (!node._counted) results.push(node); | ||
2558 | h.unmark(exclusions); | ||
2559 | return results; | ||
2560 | }, | ||
2561 | |||
2562 | 'enabled': function(nodes, value, root) { | ||
2563 | for (var i = 0, results = [], node; node = nodes[i]; i++) | ||
2564 | if (!node.disabled) results.push(node); | ||
2565 | return results; | ||
2566 | }, | ||
2567 | |||
2568 | 'disabled': function(nodes, value, root) { | ||
2569 | for (var i = 0, results = [], node; node = nodes[i]; i++) | ||
2570 | if (node.disabled) results.push(node); | ||
2571 | return results; | ||
2572 | }, | ||
2573 | |||
2574 | 'checked': function(nodes, value, root) { | ||
2575 | for (var i = 0, results = [], node; node = nodes[i]; i++) | ||
2576 | if (node.checked) results.push(node); | ||
2577 | return results; | ||
2578 | } | ||
2579 | }, | ||
2580 | |||
2581 | operators: { | ||
2582 | '=': function(nv, v) { return nv == v; }, | ||
2583 | '!=': function(nv, v) { return nv != v; }, | ||
2584 | '^=': function(nv, v) { return nv.startsWith(v); }, | ||
2585 | '$=': function(nv, v) { return nv.endsWith(v); }, | ||
2586 | '*=': function(nv, v) { return nv.include(v); }, | ||
2587 | '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); }, | ||
2588 | '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); } | ||
2589 | }, | ||
2590 | |||
2591 | matchElements: function(elements, expression) { | ||
2592 | var matches = new Selector(expression).findElements(), h = Selector.handlers; | ||
2593 | h.mark(matches); | ||
2594 | for (var i = 0, results = [], element; element = elements[i]; i++) | ||
2595 | if (element._counted) results.push(element); | ||
2596 | h.unmark(matches); | ||
2597 | return results; | ||
2598 | }, | ||
2599 | |||
2600 | findElement: function(elements, expression, index) { | ||
2601 | if (typeof expression == 'number') { | ||
2602 | index = expression; expression = false; | ||
2603 | } | ||
2604 | return Selector.matchElements(elements, expression || '*')[index || 0]; | ||
2605 | }, | ||
2606 | |||
2607 | findChildElements: function(element, expressions) { | ||
2608 | var exprs = expressions.join(','), expressions = []; | ||
2609 | exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) { | ||
2610 | expressions.push(m[1].strip()); | ||
2611 | }); | ||
2612 | var results = [], h = Selector.handlers; | ||
2613 | for (var i = 0, l = expressions.length, selector; i < l; i++) { | ||
2614 | selector = new Selector(expressions[i].strip()); | ||
2615 | h.concat(results, selector.findElements(element)); | ||
2616 | } | ||
2617 | return (l > 1) ? h.unique(results) : results; | ||
2618 | } | ||
2619 | }); | ||
2620 | |||
2621 | function $$() { | ||
2622 | return Selector.findChildElements(document, $A(arguments)); | ||
2623 | } | ||
2624 | var Form = { | ||
2625 | reset: function(form) { | ||
2626 | $(form).reset(); | ||
2627 | return form; | ||
2628 | }, | ||
2629 | |||
2630 | serializeElements: function(elements, getHash) { | ||
2631 | var data = elements.inject({}, function(result, element) { | ||
2632 | if (!element.disabled && element.name) { | ||
2633 | var key = element.name, value = $(element).getValue(); | ||
2634 | if (value != null) { | ||
2635 | if (key in result) { | ||
2636 | if (result[key].constructor != Array) result[key] = [result[key]]; | ||
2637 | result[key].push(value); | ||
2638 | } | ||
2639 | else result[key] = value; | ||
2640 | } | ||
2641 | } | ||
2642 | return result; | ||
2643 | }); | ||
2644 | |||
2645 | return getHash ? data : Hash.toQueryString(data); | ||
2646 | } | ||
2647 | }; | ||
2648 | |||
2649 | Form.Methods = { | ||
2650 | serialize: function(form, getHash) { | ||
2651 | return Form.serializeElements(Form.getElements(form), getHash); | ||
2652 | }, | ||
2653 | |||
2654 | getElements: function(form) { | ||
2655 | return $A($(form).getElementsByTagName('*')).inject([], | ||
2656 | function(elements, child) { | ||
2657 | if (Form.Element.Serializers[child.tagName.toLowerCase()]) | ||
2658 | elements.push(Element.extend(child)); | ||
2659 | return elements; | ||
2660 | } | ||
2661 | ); | ||
2662 | }, | ||
2663 | |||
2664 | getInputs: function(form, typeName, name) { | ||
2665 | form = $(form); | ||
2666 | var inputs = form.getElementsByTagName('input'); | ||
2667 | |||
2668 | if (!typeName && !name) return $A(inputs).map(Element.extend); | ||
2669 | |||
2670 | for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) { | ||
2671 | var input = inputs[i]; | ||
2672 | if ((typeName && input.type != typeName) || (name && input.name != name)) | ||
2673 | continue; | ||
2674 | matchingInputs.push(Element.extend(input)); | ||
2675 | } | ||
2676 | |||
2677 | return matchingInputs; | ||
2678 | }, | ||
2679 | |||
2680 | disable: function(form) { | ||
2681 | form = $(form); | ||
2682 | Form.getElements(form).invoke('disable'); | ||
2683 | return form; | ||
2684 | }, | ||
2685 | |||
2686 | enable: function(form) { | ||
2687 | form = $(form); | ||
2688 | Form.getElements(form).invoke('enable'); | ||
2689 | return form; | ||
2690 | }, | ||
2691 | |||
2692 | findFirstElement: function(form) { | ||
2693 | return $(form).getElements().find(function(element) { | ||
2694 | return element.type != 'hidden' && !element.disabled && | ||
2695 | ['input', 'select', 'textarea'].include(element.tagName.toLowerCase()); | ||
2696 | }); | ||
2697 | }, | ||
2698 | |||
2699 | focusFirstElement: function(form) { | ||
2700 | form = $(form); | ||
2701 | form.findFirstElement().activate(); | ||
2702 | return form; | ||
2703 | }, | ||
2704 | |||
2705 | request: function(form, options) { | ||
2706 | form = $(form), options = Object.clone(options || {}); | ||
2707 | |||
2708 | var params = options.parameters; | ||
2709 | options.parameters = form.serialize(true); | ||
2710 | |||
2711 | if (params) { | ||
2712 | if (typeof params == 'string') params = params.toQueryParams(); | ||
2713 | Object.extend(options.parameters, params); | ||
2714 | } | ||
2715 | |||
2716 | if (form.hasAttribute('method') && !options.method) | ||
2717 | options.method = form.method; | ||
2718 | |||
2719 | return new Ajax.Request(form.readAttribute('action'), options); | ||
2720 | } | ||
2721 | } | ||
2722 | |||
2723 | /*--------------------------------------------------------------------------*/ | ||
2724 | |||
2725 | Form.Element = { | ||
2726 | focus: function(element) { | ||
2727 | $(element).focus(); | ||
2728 | return element; | ||
2729 | }, | ||
2730 | |||
2731 | select: function(element) { | ||
2732 | $(element).select(); | ||
2733 | return element; | ||
2734 | } | ||
2735 | } | ||
2736 | |||
2737 | Form.Element.Methods = { | ||
2738 | serialize: function(element) { | ||
2739 | element = $(element); | ||
2740 | if (!element.disabled && element.name) { | ||
2741 | var value = element.getValue(); | ||
2742 | if (value != undefined) { | ||
2743 | var pair = {}; | ||
2744 | pair[element.name] = value; | ||
2745 | return Hash.toQueryString(pair); | ||
2746 | } | ||
2747 | } | ||
2748 | return ''; | ||
2749 | }, | ||
2750 | |||
2751 | getValue: function(element) { | ||
2752 | element = $(element); | ||
2753 | var method = element.tagName.toLowerCase(); | ||
2754 | return Form.Element.Serializers[method](element); | ||
2755 | }, | ||
2756 | |||
2757 | clear: function(element) { | ||
2758 | $(element).value = ''; | ||
2759 | return element; | ||
2760 | }, | ||
2761 | |||
2762 | present: function(element) { | ||
2763 | return $(element).value != ''; | ||
2764 | }, | ||
2765 | |||
2766 | activate: function(element) { | ||
2767 | element = $(element); | ||
2768 | try { | ||
2769 | element.focus(); | ||
2770 | if (element.select && (element.tagName.toLowerCase() != 'input' || | ||
2771 | !['button', 'reset', 'submit'].include(element.type))) | ||
2772 | element.select(); | ||
2773 | } catch (e) {} | ||
2774 | return element; | ||
2775 | }, | ||
2776 | |||
2777 | disable: function(element) { | ||
2778 | element = $(element); | ||
2779 | element.blur(); | ||
2780 | element.disabled = true; | ||
2781 | return element; | ||
2782 | }, | ||
2783 | |||
2784 | enable: function(element) { | ||
2785 | element = $(element); | ||
2786 | element.disabled = false; | ||
2787 | return element; | ||
2788 | } | ||
2789 | } | ||
2790 | |||
2791 | /*--------------------------------------------------------------------------*/ | ||
2792 | |||
2793 | var Field = Form.Element; | ||
2794 | var $F = Form.Element.Methods.getValue; | ||
2795 | |||
2796 | /*--------------------------------------------------------------------------*/ | ||
2797 | |||
2798 | Form.Element.Serializers = { | ||
2799 | input: function(element) { | ||
2800 | switch (element.type.toLowerCase()) { | ||
2801 | case 'checkbox': | ||
2802 | case 'radio': | ||
2803 | return Form.Element.Serializers.inputSelector(element); | ||
2804 | default: | ||
2805 | return Form.Element.Serializers.textarea(element); | ||
2806 | } | ||
2807 | }, | ||
2808 | |||
2809 | inputSelector: function(element) { | ||
2810 | return element.checked ? element.value : null; | ||
2811 | }, | ||
2812 | |||
2813 | textarea: function(element) { | ||
2814 | return element.value; | ||
2815 | }, | ||
2816 | |||
2817 | select: function(element) { | ||
2818 | return this[element.type == 'select-one' ? | ||
2819 | 'selectOne' : 'selectMany'](element); | ||
2820 | }, | ||
2821 | |||
2822 | selectOne: function(element) { | ||
2823 | var index = element.selectedIndex; | ||
2824 | return index >= 0 ? this.optionValue(element.options[index]) : null; | ||
2825 | }, | ||
2826 | |||
2827 | selectMany: function(element) { | ||
2828 | var values, length = element.length; | ||
2829 | if (!length) return null; | ||
2830 | |||
2831 | for (var i = 0, values = []; i < length; i++) { | ||
2832 | var opt = element.options[i]; | ||
2833 | if (opt.selected) values.push(this.optionValue(opt)); | ||
2834 | } | ||
2835 | return values; | ||
2836 | }, | ||
2837 | |||
2838 | optionValue: function(opt) { | ||
2839 | // extend element because hasAttribute may not be native | ||
2840 | return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text; | ||
2841 | } | ||
2842 | } | ||
2843 | |||
2844 | /*--------------------------------------------------------------------------*/ | ||
2845 | |||
2846 | Abstract.TimedObserver = function() {} | ||
2847 | Abstract.TimedObserver.prototype = { | ||
2848 | initialize: function(element, frequency, callback) { | ||
2849 | this.frequency = frequency; | ||
2850 | this.element = $(element); | ||
2851 | this.callback = callback; | ||
2852 | |||
2853 | this.lastValue = this.getValue(); | ||
2854 | this.registerCallback(); | ||
2855 | }, | ||
2856 | |||
2857 | registerCallback: function() { | ||
2858 | setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); | ||
2859 | }, | ||
2860 | |||
2861 | onTimerEvent: function() { | ||
2862 | var value = this.getValue(); | ||
2863 | var changed = ('string' == typeof this.lastValue && 'string' == typeof value | ||
2864 | ? this.lastValue != value : String(this.lastValue) != String(value)); | ||
2865 | if (changed) { | ||
2866 | this.callback(this.element, value); | ||
2867 | this.lastValue = value; | ||
2868 | } | ||
2869 | } | ||
2870 | } | ||
2871 | |||
2872 | Form.Element.Observer = Class.create(); | ||
2873 | Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { | ||
2874 | getValue: function() { | ||
2875 | return Form.Element.getValue(this.element); | ||
2876 | } | ||
2877 | }); | ||
2878 | |||
2879 | Form.Observer = Class.create(); | ||
2880 | Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { | ||
2881 | getValue: function() { | ||
2882 | return Form.serialize(this.element); | ||
2883 | } | ||
2884 | }); | ||
2885 | |||
2886 | /*--------------------------------------------------------------------------*/ | ||
2887 | |||
2888 | Abstract.EventObserver = function() {} | ||
2889 | Abstract.EventObserver.prototype = { | ||
2890 | initialize: function(element, callback) { | ||
2891 | this.element = $(element); | ||
2892 | this.callback = callback; | ||
2893 | |||
2894 | this.lastValue = this.getValue(); | ||
2895 | if (this.element.tagName.toLowerCase() == 'form') | ||
2896 | this.registerFormCallbacks(); | ||
2897 | else | ||
2898 | this.registerCallback(this.element); | ||
2899 | }, | ||
2900 | |||
2901 | onElementEvent: function() { | ||
2902 | var value = this.getValue(); | ||
2903 | if (this.lastValue != value) { | ||
2904 | this.callback(this.element, value); | ||
2905 | this.lastValue = value; | ||
2906 | } | ||
2907 | }, | ||
2908 | |||
2909 | registerFormCallbacks: function() { | ||
2910 | Form.getElements(this.element).each(this.registerCallback.bind(this)); | ||
2911 | }, | ||
2912 | |||
2913 | registerCallback: function(element) { | ||
2914 | if (element.type) { | ||
2915 | switch (element.type.toLowerCase()) { | ||
2916 | case 'checkbox': | ||
2917 | case 'radio': | ||
2918 | Event.observe(element, 'click', this.onElementEvent.bind(this)); | ||
2919 | break; | ||
2920 | default: | ||
2921 | Event.observe(element, 'change', this.onElementEvent.bind(this)); | ||
2922 | break; | ||
2923 | } | ||
2924 | } | ||
2925 | } | ||
2926 | } | ||
2927 | |||
2928 | Form.Element.EventObserver = Class.create(); | ||
2929 | Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { | ||
2930 | getValue: function() { | ||
2931 | return Form.Element.getValue(this.element); | ||
2932 | } | ||
2933 | }); | ||
2934 | |||
2935 | Form.EventObserver = Class.create(); | ||
2936 | Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { | ||
2937 | getValue: function() { | ||
2938 | return Form.serialize(this.element); | ||
2939 | } | ||
2940 | }); | ||
2941 | if (!window.Event) { | ||
2942 | var Event = new Object(); | ||
2943 | } | ||
2944 | |||
2945 | Object.extend(Event, { | ||
2946 | KEY_BACKSPACE: 8, | ||
2947 | KEY_TAB: 9, | ||
2948 | KEY_RETURN: 13, | ||
2949 | KEY_ESC: 27, | ||
2950 | KEY_LEFT: 37, | ||
2951 | KEY_UP: 38, | ||
2952 | KEY_RIGHT: 39, | ||
2953 | KEY_DOWN: 40, | ||
2954 | KEY_DELETE: 46, | ||
2955 | KEY_HOME: 36, | ||
2956 | KEY_END: 35, | ||
2957 | KEY_PAGEUP: 33, | ||
2958 | KEY_PAGEDOWN: 34, | ||
2959 | |||
2960 | element: function(event) { | ||
2961 | return $(event.target || event.srcElement); | ||
2962 | }, | ||
2963 | |||
2964 | isLeftClick: function(event) { | ||
2965 | return (((event.which) && (event.which == 1)) || | ||
2966 | ((event.button) && (event.button == 1))); | ||
2967 | }, | ||
2968 | |||
2969 | pointerX: function(event) { | ||
2970 | return event.pageX || (event.clientX + | ||
2971 | (document.documentElement.scrollLeft || document.body.scrollLeft)); | ||
2972 | }, | ||
2973 | |||
2974 | pointerY: function(event) { | ||
2975 | return event.pageY || (event.clientY + | ||
2976 | (document.documentElement.scrollTop || document.body.scrollTop)); | ||
2977 | }, | ||
2978 | |||
2979 | stop: function(event) { | ||
2980 | if (event.preventDefault) { | ||
2981 | event.preventDefault(); | ||
2982 | event.stopPropagation(); | ||
2983 | } else { | ||
2984 | event.returnValue = false; | ||
2985 | event.cancelBubble = true; | ||
2986 | } | ||
2987 | }, | ||
2988 | |||
2989 | // find the first node with the given tagName, starting from the | ||
2990 | // node the event was triggered on; traverses the DOM upwards | ||
2991 | findElement: function(event, tagName) { | ||
2992 | var element = Event.element(event); | ||
2993 | while (element.parentNode && (!element.tagName || | ||
2994 | (element.tagName.toUpperCase() != tagName.toUpperCase()))) | ||
2995 | element = element.parentNode; | ||
2996 | return element; | ||
2997 | }, | ||
2998 | |||
2999 | observers: false, | ||
3000 | |||
3001 | _observeAndCache: function(element, name, observer, useCapture) { | ||
3002 | if (!this.observers) this.observers = []; | ||
3003 | if (element.addEventListener) { | ||
3004 | this.observers.push([element, name, observer, useCapture]); | ||
3005 | element.addEventListener(name, observer, useCapture); | ||
3006 | } else if (element.attachEvent) { | ||
3007 | this.observers.push([element, name, observer, useCapture]); | ||
3008 | element.attachEvent('on' + name, observer); | ||
3009 | } | ||
3010 | }, | ||
3011 | |||
3012 | unloadCache: function() { | ||
3013 | if (!Event.observers) return; | ||
3014 | for (var i = 0, length = Event.observers.length; i < length; i++) { | ||
3015 | Event.stopObserving.apply(this, Event.observers[i]); | ||
3016 | Event.observers[i][0] = null; | ||
3017 | } | ||
3018 | Event.observers = false; | ||
3019 | }, | ||
3020 | |||
3021 | observe: function(element, name, observer, useCapture) { | ||
3022 | element = $(element); | ||
3023 | useCapture = useCapture || false; | ||
3024 | |||
3025 | if (name == 'keypress' && | ||
3026 | (Prototype.Browser.WebKit || element.attachEvent)) | ||
3027 | name = 'keydown'; | ||
3028 | |||
3029 | Event._observeAndCache(element, name, observer, useCapture); | ||
3030 | }, | ||
3031 | |||
3032 | stopObserving: function(element, name, observer, useCapture) { | ||
3033 | element = $(element); | ||
3034 | useCapture = useCapture || false; | ||
3035 | |||
3036 | if (name == 'keypress' && | ||
3037 | (Prototype.Browser.WebKit || element.attachEvent)) | ||
3038 | name = 'keydown'; | ||
3039 | |||
3040 | if (element.removeEventListener) { | ||
3041 | element.removeEventListener(name, observer, useCapture); | ||
3042 | } else if (element.detachEvent) { | ||
3043 | try { | ||
3044 | element.detachEvent('on' + name, observer); | ||
3045 | } catch (e) {} | ||
3046 | } | ||
3047 | } | ||
3048 | }); | ||
3049 | |||
3050 | /* prevent memory leaks in IE */ | ||
3051 | if (Prototype.Browser.IE) | ||
3052 | Event.observe(window, 'unload', Event.unloadCache, false); | ||
3053 | var Position = { | ||
3054 | // set to true if needed, warning: firefox performance problems | ||
3055 | // NOT neeeded for page scrolling, only if draggable contained in | ||
3056 | // scrollable elements | ||
3057 | includeScrollOffsets: false, | ||
3058 | |||
3059 | // must be called before calling withinIncludingScrolloffset, every time the | ||
3060 | // page is scrolled | ||
3061 | prepare: function() { | ||
3062 | this.deltaX = window.pageXOffset | ||
3063 | || document.documentElement.scrollLeft | ||
3064 | || document.body.scrollLeft | ||
3065 | || 0; | ||
3066 | this.deltaY = window.pageYOffset | ||
3067 | || document.documentElement.scrollTop | ||
3068 | || document.body.scrollTop | ||
3069 | || 0; | ||
3070 | }, | ||
3071 | |||
3072 | realOffset: function(element) { | ||
3073 | var valueT = 0, valueL = 0; | ||
3074 | do { | ||
3075 | valueT += element.scrollTop || 0; | ||
3076 | valueL += element.scrollLeft || 0; | ||
3077 | element = element.parentNode; | ||
3078 | } while (element); | ||
3079 | return [valueL, valueT]; | ||
3080 | }, | ||
3081 | |||
3082 | cumulativeOffset: function(element) { | ||
3083 | var valueT = 0, valueL = 0; | ||
3084 | do { | ||
3085 | valueT += element.offsetTop || 0; | ||
3086 | valueL += element.offsetLeft || 0; | ||
3087 | element = element.offsetParent; | ||
3088 | } while (element); | ||
3089 | return [valueL, valueT]; | ||
3090 | }, | ||
3091 | |||
3092 | positionedOffset: function(element) { | ||
3093 | var valueT = 0, valueL = 0; | ||
3094 | do { | ||
3095 | valueT += element.offsetTop || 0; | ||
3096 | valueL += element.offsetLeft || 0; | ||
3097 | element = element.offsetParent; | ||
3098 | if (element) { | ||
3099 | if(element.tagName=='BODY') break; | ||
3100 | var p = Element.getStyle(element, 'position'); | ||
3101 | if (p == 'relative' || p == 'absolute') break; | ||
3102 | } | ||
3103 | } while (element); | ||
3104 | return [valueL, valueT]; | ||
3105 | }, | ||
3106 | |||
3107 | offsetParent: function(element) { | ||
3108 | if (element.offsetParent) return element.offsetParent; | ||
3109 | if (element == document.body) return element; | ||
3110 | |||
3111 | while ((element = element.parentNode) && element != document.body) | ||
3112 | if (Element.getStyle(element, 'position') != 'static') | ||
3113 | return element; | ||
3114 | |||
3115 | return document.body; | ||
3116 | }, | ||
3117 | |||
3118 | // caches x/y coordinate pair to use with overlap | ||
3119 | within: function(element, x, y) { | ||
3120 | if (this.includeScrollOffsets) | ||
3121 | return this.withinIncludingScrolloffsets(element, x, y); | ||
3122 | this.xcomp = x; | ||
3123 | this.ycomp = y; | ||
3124 | this.offset = this.cumulativeOffset(element); | ||
3125 | |||
3126 | return (y >= this.offset[1] && | ||
3127 | y < this.offset[1] + element.offsetHeight && | ||
3128 | x >= this.offset[0] && | ||
3129 | x < this.offset[0] + element.offsetWidth); | ||
3130 | }, | ||
3131 | |||
3132 | withinIncludingScrolloffsets: function(element, x, y) { | ||
3133 | var offsetcache = this.realOffset(element); | ||
3134 | |||
3135 | this.xcomp = x + offsetcache[0] - this.deltaX; | ||
3136 | this.ycomp = y + offsetcache[1] - this.deltaY; | ||
3137 | this.offset = this.cumulativeOffset(element); | ||
3138 | |||
3139 | return (this.ycomp >= this.offset[1] && | ||
3140 | this.ycomp < this.offset[1] + element.offsetHeight && | ||
3141 | this.xcomp >= this.offset[0] && | ||
3142 | this.xcomp < this.offset[0] + element.offsetWidth); | ||
3143 | }, | ||
3144 | |||
3145 | // within must be called directly before | ||
3146 | overlap: function(mode, element) { | ||
3147 | if (!mode) return 0; | ||
3148 | if (mode == 'vertical') | ||
3149 | return ((this.offset[1] + element.offsetHeight) - this.ycomp) / | ||
3150 | element.offsetHeight; | ||
3151 | if (mode == 'horizontal') | ||
3152 | return ((this.offset[0] + element.offsetWidth) - this.xcomp) / | ||
3153 | element.offsetWidth; | ||
3154 | }, | ||
3155 | |||
3156 | page: function(forElement) { | ||
3157 | var valueT = 0, valueL = 0; | ||
3158 | |||
3159 | var element = forElement; | ||
3160 | do { | ||
3161 | valueT += element.offsetTop || 0; | ||
3162 | valueL += element.offsetLeft || 0; | ||
3163 | |||
3164 | // Safari fix | ||
3165 | if (element.offsetParent == document.body) | ||
3166 | if (Element.getStyle(element,'position')=='absolute') break; | ||
3167 | |||
3168 | } while (element = element.offsetParent); | ||
3169 | |||
3170 | element = forElement; | ||
3171 | do { | ||
3172 | if (!window.opera || element.tagName=='BODY') { | ||
3173 | valueT -= element.scrollTop || 0; | ||
3174 | valueL -= element.scrollLeft || 0; | ||
3175 | } | ||
3176 | } while (element = element.parentNode); | ||
3177 | |||
3178 | return [valueL, valueT]; | ||
3179 | }, | ||
3180 | |||
3181 | clone: function(source, target) { | ||
3182 | var options = Object.extend({ | ||
3183 | setLeft: true, | ||
3184 | setTop: true, | ||
3185 | setWidth: true, | ||
3186 | setHeight: true, | ||
3187 | offsetTop: 0, | ||
3188 | offsetLeft: 0 | ||
3189 | }, arguments[2] || {}) | ||
3190 | |||
3191 | // find page position of source | ||
3192 | source = $(source); | ||
3193 | var p = Position.page(source); | ||
3194 | |||
3195 | // find coordinate system to use | ||
3196 | target = $(target); | ||
3197 | var delta = [0, 0]; | ||
3198 | var parent = null; | ||
3199 | // delta [0,0] will do fine with position: fixed elements, | ||
3200 | // position:absolute needs offsetParent deltas | ||
3201 | if (Element.getStyle(target,'position') == 'absolute') { | ||
3202 | parent = Position.offsetParent(target); | ||
3203 | delta = Position.page(parent); | ||
3204 | } | ||
3205 | |||
3206 | // correct by body offsets (fixes Safari) | ||
3207 | if (parent == document.body) { | ||
3208 | delta[0] -= document.body.offsetLeft; | ||
3209 | delta[1] -= document.body.offsetTop; | ||
3210 | } | ||
3211 | |||
3212 | // set position | ||
3213 | if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px'; | ||
3214 | if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px'; | ||
3215 | if(options.setWidth) target.style.width = source.offsetWidth + 'px'; | ||
3216 | if(options.setHeight) target.style.height = source.offsetHeight + 'px'; | ||
3217 | }, | ||
3218 | |||
3219 | absolutize: function(element) { | ||
3220 | element = $(element); | ||
3221 | if (element.style.position == 'absolute') return; | ||
3222 | Position.prepare(); | ||
3223 | |||
3224 | var offsets = Position.positionedOffset(element); | ||
3225 | var top = offsets[1]; | ||
3226 | var left = offsets[0]; | ||
3227 | var width = element.clientWidth; | ||
3228 | var height = element.clientHeight; | ||
3229 | |||
3230 | element._originalLeft = left - parseFloat(element.style.left || 0); | ||
3231 | element._originalTop = top - parseFloat(element.style.top || 0); | ||
3232 | element._originalWidth = element.style.width; | ||
3233 | element._originalHeight = element.style.height; | ||
3234 | |||
3235 | element.style.position = 'absolute'; | ||
3236 | element.style.top = top + 'px'; | ||
3237 | element.style.left = left + 'px'; | ||
3238 | element.style.width = width + 'px'; | ||
3239 | element.style.height = height + 'px'; | ||
3240 | }, | ||
3241 | |||
3242 | relativize: function(element) { | ||
3243 | element = $(element); | ||
3244 | if (element.style.position == 'relative') return; | ||
3245 | Position.prepare(); | ||
3246 | |||
3247 | element.style.position = 'relative'; | ||
3248 | var top = parseFloat(element.style.top || 0) - (element._originalTop || 0); | ||
3249 | var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0); | ||
3250 | |||
3251 | element.style.top = top + 'px'; | ||
3252 | element.style.left = left + 'px'; | ||
3253 | element.style.height = element._originalHeight; | ||
3254 | element.style.width = element._originalWidth; | ||
3255 | } | ||
3256 | } | ||
3257 | |||
3258 | // Safari returns margins on body which is incorrect if the child is absolutely | ||
3259 | // positioned. For performance reasons, redefine Position.cumulativeOffset for | ||
3260 | // KHTML/WebKit only. | ||
3261 | if (Prototype.Browser.WebKit) { | ||
3262 | Position.cumulativeOffset = function(element) { | ||
3263 | var valueT = 0, valueL = 0; | ||
3264 | do { | ||
3265 | valueT += element.offsetTop || 0; | ||
3266 | valueL += element.offsetLeft || 0; | ||
3267 | if (element.offsetParent == document.body) | ||
3268 | if (Element.getStyle(element, 'position') == 'absolute') break; | ||
3269 | |||
3270 | element = element.offsetParent; | ||
3271 | } while (element); | ||
3272 | |||
3273 | return [valueL, valueT]; | ||
3274 | } | ||
3275 | } | ||
3276 | |||
3277 | Element.addMethods(); \ No newline at end of file | ||
diff --git a/docroot/lib/scriptaculous/builder.js b/docroot/lib/scriptaculous/builder.js new file mode 100755 index 0000000..5b4ce87 --- /dev/null +++ b/docroot/lib/scriptaculous/builder.js | |||
@@ -0,0 +1,136 @@ | |||
1 | // script.aculo.us builder.js v1.7.1_beta3, Fri May 25 17:19:41 +0200 2007 | ||
2 | |||
3 | // Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) | ||
4 | // | ||
5 | // script.aculo.us is freely distributable under the terms of an MIT-style license. | ||
6 | // For details, see the script.aculo.us web site: http://script.aculo.us/ | ||
7 | |||
8 | var Builder = { | ||
9 | NODEMAP: { | ||
10 | AREA: 'map', | ||
11 | CAPTION: 'table', | ||
12 | COL: 'table', | ||
13 | COLGROUP: 'table', | ||
14 | LEGEND: 'fieldset', | ||
15 | OPTGROUP: 'select', | ||
16 | OPTION: 'select', | ||
17 | PARAM: 'object', | ||
18 | TBODY: 'table', | ||
19 | TD: 'table', | ||
20 | TFOOT: 'table', | ||
21 | TH: 'table', | ||
22 | THEAD: 'table', | ||
23 | TR: 'table' | ||
24 | }, | ||
25 | // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken, | ||
26 | // due to a Firefox bug | ||
27 | node: function(elementName) { | ||
28 | elementName = elementName.toUpperCase(); | ||
29 | |||
30 | // try innerHTML approach | ||
31 | var parentTag = this.NODEMAP[elementName] || 'div'; | ||
32 | var parentElement = document.createElement(parentTag); | ||
33 | try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707 | ||
34 | parentElement.innerHTML = "<" + elementName + "></" + elementName + ">"; | ||
35 | } catch(e) {} | ||
36 | var element = parentElement.firstChild || null; | ||
37 | |||
38 | // see if browser added wrapping tags | ||
39 | if(element && (element.tagName.toUpperCase() != elementName)) | ||
40 | element = element.getElementsByTagName(elementName)[0]; | ||
41 | |||
42 | // fallback to createElement approach | ||
43 | if(!element) element = document.createElement(elementName); | ||
44 | |||
45 | // abort if nothing could be created | ||
46 | if(!element) return; | ||
47 | |||
48 | // attributes (or text) | ||
49 | if(arguments[1]) | ||
50 | if(this._isStringOrNumber(arguments[1]) || | ||
51 | (arguments[1] instanceof Array) || | ||
52 | arguments[1].tagName) { | ||
53 | this._children(element, arguments[1]); | ||
54 | } else { | ||
55 | var attrs = this._attributes(arguments[1]); | ||
56 | if(attrs.length) { | ||
57 | try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707 | ||
58 | parentElement.innerHTML = "<" +elementName + " " + | ||
59 | attrs + "></" + elementName + ">"; | ||
60 | } catch(e) {} | ||
61 | element = parentElement.firstChild || null; | ||
62 | // workaround firefox 1.0.X bug | ||
63 | if(!element) { | ||
64 | element = document.createElement(elementName); | ||
65 | for(attr in arguments[1]) | ||
66 | element[attr == 'class' ? 'className' : attr] = arguments[1][attr]; | ||
67 | } | ||
68 | if(element.tagName.toUpperCase() != elementName) | ||
69 | element = parentElement.getElementsByTagName(elementName)[0]; | ||
70 | } | ||
71 | } | ||
72 | |||
73 | // text, or array of children | ||
74 | if(arguments[2]) | ||
75 | this._children(element, arguments[2]); | ||
76 | |||
77 | return element; | ||
78 | }, | ||
79 | _text: function(text) { | ||
80 | return document.createTextNode(text); | ||
81 | }, | ||
82 | |||
83 | ATTR_MAP: { | ||
84 | 'className': 'class', | ||
85 | 'htmlFor': 'for' | ||
86 | }, | ||
87 | |||
88 | _attributes: function(attributes) { | ||
89 | var attrs = []; | ||
90 | for(attribute in attributes) | ||
91 | attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) + | ||
92 | '="' + attributes[attribute].toString().escapeHTML().gsub(/"/,'"') + '"'); | ||
93 | return attrs.join(" "); | ||
94 | }, | ||
95 | _children: function(element, children) { | ||
96 | if(children.tagName) { | ||
97 | element.appendChild(children); | ||
98 | return; | ||
99 | } | ||
100 | if(typeof children=='object') { // array can hold nodes and text | ||
101 | children.flatten().each( function(e) { | ||
102 | if(typeof e=='object') | ||
103 | element.appendChild(e) | ||
104 | else | ||
105 | if(Builder._isStringOrNumber(e)) | ||
106 | element.appendChild(Builder._text(e)); | ||
107 | }); | ||
108 | } else | ||
109 | if(Builder._isStringOrNumber(children)) | ||
110 | element.appendChild(Builder._text(children)); | ||
111 | }, | ||
112 | _isStringOrNumber: function(param) { | ||
113 | return(typeof param=='string' || typeof param=='number'); | ||
114 | }, | ||
115 | build: function(html) { | ||
116 | var element = this.node('div'); | ||
117 | $(element).update(html.strip()); | ||
118 | return element.down(); | ||
119 | }, | ||
120 | dump: function(scope) { | ||
121 | if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope | ||
122 | |||
123 | var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " + | ||
124 | "BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " + | ||
125 | "FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+ | ||
126 | "KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+ | ||
127 | "PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+ | ||
128 | "TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/); | ||
129 | |||
130 | tags.each( function(tag){ | ||
131 | scope[tag] = function() { | ||
132 | return Builder.node.apply(Builder, [tag].concat($A(arguments))); | ||
133 | } | ||
134 | }); | ||
135 | } | ||
136 | } | ||
diff --git a/docroot/lib/scriptaculous/controls.js b/docroot/lib/scriptaculous/controls.js new file mode 100755 index 0000000..6783bd0 --- /dev/null +++ b/docroot/lib/scriptaculous/controls.js | |||
@@ -0,0 +1,875 @@ | |||
1 | // script.aculo.us controls.js v1.7.1_beta3, Fri May 25 17:19:41 +0200 2007 | ||
2 | |||
3 | // Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) | ||
4 | // (c) 2005-2007 Ivan Krstic (http://blogs.law.harvard.edu/ivan) | ||
5 | // (c) 2005-2007 Jon Tirsen (http://www.tirsen.com) | ||
6 | // Contributors: | ||
7 | // Richard Livsey | ||
8 | // Rahul Bhargava | ||
9 | // Rob Wills | ||
10 | // | ||
11 | // script.aculo.us is freely distributable under the terms of an MIT-style license. | ||
12 | // For details, see the script.aculo.us web site: http://script.aculo.us/ | ||
13 | |||
14 | // Autocompleter.Base handles all the autocompletion functionality | ||
15 | // that's independent of the data source for autocompletion. This | ||
16 | // includes drawing the autocompletion menu, observing keyboard | ||
17 | // and mouse events, and similar. | ||
18 | // | ||
19 | // Specific autocompleters need to provide, at the very least, | ||
20 | // a getUpdatedChoices function that will be invoked every time | ||
21 | // the text inside the monitored textbox changes. This method | ||
22 | // should get the text for which to provide autocompletion by | ||
23 | // invoking this.getToken(), NOT by directly accessing | ||
24 | // this.element.value. This is to allow incremental tokenized | ||
25 | // autocompletion. Specific auto-completion logic (AJAX, etc) | ||
26 | // belongs in getUpdatedChoices. | ||
27 | // | ||
28 | // Tokenized incremental autocompletion is enabled automatically | ||
29 | // when an autocompleter is instantiated with the 'tokens' option | ||
30 | // in the options parameter, e.g.: | ||
31 | // new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' }); | ||
32 | // will incrementally autocomplete with a comma as the token. | ||
33 | // Additionally, ',' in the above example can be replaced with | ||
34 | // a token array, e.g. { tokens: [',', '\n'] } which | ||
35 | // enables autocompletion on multiple tokens. This is most | ||
36 | // useful when one of the tokens is \n (a newline), as it | ||
37 | // allows smart autocompletion after linebreaks. | ||
38 | |||
39 | if(typeof Effect == 'undefined') | ||
40 | throw("controls.js requires including script.aculo.us' effects.js library"); | ||
41 | |||
42 | var Autocompleter = {} | ||
43 | Autocompleter.Base = function() {}; | ||
44 | Autocompleter.Base.prototype = { | ||
45 | baseInitialize: function(element, update, options) { | ||
46 | element = $(element) | ||
47 | this.element = element; | ||
48 | this.update = $(update); | ||
49 | this.hasFocus = false; | ||
50 | this.changed = false; | ||
51 | this.active = false; | ||
52 | this.index = 0; | ||
53 | this.entryCount = 0; | ||
54 | |||
55 | if(this.setOptions) | ||
56 | this.setOptions(options); | ||
57 | else | ||
58 | this.options = options || {}; | ||
59 | |||
60 | this.options.paramName = this.options.paramName || this.element.name; | ||
61 | this.options.tokens = this.options.tokens || []; | ||
62 | this.options.frequency = this.options.frequency || 0.4; | ||
63 | this.options.minChars = this.options.minChars || 1; | ||
64 | this.options.onShow = this.options.onShow || | ||
65 | function(element, update){ | ||
66 | if(!update.style.position || update.style.position=='absolute') { | ||
67 | update.style.position = 'absolute'; | ||
68 | Position.clone(element, update, { | ||
69 | setHeight: false, | ||
70 | offsetTop: element.offsetHeight | ||
71 | }); | ||
72 | } | ||
73 | Effect.Appear(update,{duration:0.15}); | ||
74 | }; | ||
75 | this.options.onHide = this.options.onHide || | ||
76 | function(element, update){ new Effect.Fade(update,{duration:0.15}) }; | ||
77 | |||
78 | if(typeof(this.options.tokens) == 'string') | ||
79 | this.options.tokens = new Array(this.options.tokens); | ||
80 | |||
81 | this.observer = null; | ||
82 | |||
83 | this.element.setAttribute('autocomplete','off'); | ||
84 | |||
85 | Element.hide(this.update); | ||
86 | |||
87 | Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this)); | ||
88 | Event.observe(this.element, 'keypress', this.onKeyPress.bindAsEventListener(this)); | ||
89 | |||
90 | // Turn autocomplete back on when the user leaves the page, so that the | ||
91 | // field's value will be remembered on Mozilla-based browsers. | ||
92 | Event.observe(window, 'beforeunload', function(){ | ||
93 | element.setAttribute('autocomplete', 'on'); | ||
94 | }); | ||
95 | }, | ||
96 | |||
97 | show: function() { | ||
98 | if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update); | ||
99 | if(!this.iefix && | ||
100 | (Prototype.Browser.IE) && | ||
101 | (Element.getStyle(this.update, 'position')=='absolute')) { | ||
102 | new Insertion.After(this.update, | ||
103 | '<iframe id="' + this.update.id + '_iefix" '+ | ||
104 | 'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' + | ||
105 | 'src="javascript:false;" frameborder="0" scrolling="no"></iframe>'); | ||
106 | this.iefix = $(this.update.id+'_iefix'); | ||
107 | } | ||
108 | if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50); | ||
109 | }, | ||
110 | |||
111 | fixIEOverlapping: function() { | ||
112 | Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)}); | ||
113 | this.iefix.style.zIndex = 1; | ||
114 | this.update.style.zIndex = 2; | ||
115 | Element.show(this.iefix); | ||
116 | }, | ||
117 | |||
118 | hide: function() { | ||
119 | this.stopIndicator(); | ||
120 | if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update); | ||
121 | if(this.iefix) Element.hide(this.iefix); | ||
122 | }, | ||
123 | |||
124 | startIndicator: function() { | ||
125 | if(this.options.indicator) Element.show(this.options.indicator); | ||
126 | }, | ||
127 | |||
128 | stopIndicator: function() { | ||
129 | if(this.options.indicator) Element.hide(this.options.indicator); | ||
130 | }, | ||
131 | |||
132 | onKeyPress: function(event) { | ||
133 | if(this.active) | ||
134 | switch(event.keyCode) { | ||
135 | case Event.KEY_TAB: | ||
136 | case Event.KEY_RETURN: | ||
137 | this.selectEntry(); | ||
138 | Event.stop(event); | ||
139 | case Event.KEY_ESC: | ||
140 | this.hide(); | ||
141 | this.active = false; | ||
142 | Event.stop(event); | ||
143 | return; | ||
144 | case Event.KEY_LEFT: | ||
145 | case Event.KEY_RIGHT: | ||
146 | return; | ||
147 | case Event.KEY_UP: | ||
148 | this.markPrevious(); | ||
149 | this.render(); | ||
150 | if(Prototype.Browser.WebKit) Event.stop(event); | ||
151 | return; | ||
152 | case Event.KEY_DOWN: | ||
153 | this.markNext(); | ||
154 | this.render(); | ||
155 | if(Prototype.Browser.WebKit) Event.stop(event); | ||
156 | return; | ||
157 | } | ||
158 | else | ||
159 | if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN || | ||
160 | (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return; | ||
161 | |||
162 | this.changed = true; | ||
163 | this.hasFocus = true; | ||
164 | |||
165 | if(this.observer) clearTimeout(this.observer); | ||
166 | this.observer = | ||
167 | setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000); | ||
168 | }, | ||
169 | |||
170 | activate: function() { | ||
171 | this.changed = false; | ||
172 | this.hasFocus = true; | ||
173 | this.getUpdatedChoices(); | ||
174 | }, | ||
175 | |||
176 | onHover: function(event) { | ||
177 | var element = Event.findElement(event, 'LI'); | ||
178 | if(this.index != element.autocompleteIndex) | ||
179 | { | ||
180 | this.index = element.autocompleteIndex; | ||
181 | this.render(); | ||
182 | } | ||
183 | Event.stop(event); | ||
184 | }, | ||
185 | |||
186 | onClick: function(event) { | ||
187 | var element = Event.findElement(event, 'LI'); | ||
188 | this.index = element.autocompleteIndex; | ||
189 | this.selectEntry(); | ||
190 | this.hide(); | ||
191 | }, | ||
192 | |||
193 | onBlur: function(event) { | ||
194 | // needed to make click events working | ||
195 | setTimeout(this.hide.bind(this), 250); | ||
196 | this.hasFocus = false; | ||
197 | this.active = false; | ||
198 | }, | ||
199 | |||
200 | render: function() { | ||
201 | if(this.entryCount > 0) { | ||
202 | for (var i = 0; i < this.entryCount; i++) | ||
203 | this.index==i ? | ||
204 | Element.addClassName(this.getEntry(i),"selected") : | ||
205 | Element.removeClassName(this.getEntry(i),"selected"); | ||
206 | if(this.hasFocus) { | ||
207 | this.show(); | ||
208 | this.active = true; | ||
209 | } | ||
210 | } else { | ||
211 | this.active = false; | ||
212 | this.hide(); | ||
213 | } | ||
214 | }, | ||
215 | |||
216 | markPrevious: function() { | ||
217 | if(this.index > 0) this.index-- | ||
218 | else this.index = this.entryCount-1; | ||
219 | this.getEntry(this.index).scrollIntoView(true); | ||
220 | }, | ||
221 | |||
222 | markNext: function() { | ||
223 | if(this.index < this.entryCount-1) this.index++ | ||
224 | else this.index = 0; | ||
225 | this.getEntry(this.index).scrollIntoView(false); | ||
226 | }, | ||
227 | |||
228 | getEntry: function(index) { | ||
229 | return this.update.firstChild.childNodes[index]; | ||
230 | }, | ||
231 | |||
232 | getCurrentEntry: function() { | ||
233 | return this.getEntry(this.index); | ||
234 | }, | ||
235 | |||
236 | selectEntry: function() { | ||
237 | this.active = false; | ||
238 | this.updateElement(this.getCurrentEntry()); | ||
239 | }, | ||
240 | |||
241 | updateElement: function(selectedElement) { | ||
242 | if (this.options.updateElement) { | ||
243 | this.options.updateElement(selectedElement); | ||
244 | return; | ||
245 | } | ||
246 | var value = ''; | ||
247 | if (this.options.select) { | ||
248 | var nodes = document.getElementsByClassName(this.options.select, selectedElement) || []; | ||
249 | if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select); | ||
250 | } else | ||
251 | value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal'); | ||
252 | |||
253 | var lastTokenPos = this.findLastToken(); | ||
254 | if (lastTokenPos != -1) { | ||
255 | var newValue = this.element.value.substr(0, lastTokenPos + 1); | ||
256 | var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/); | ||
257 | if (whitespace) | ||
258 | newValue += whitespace[0]; | ||
259 | this.element.value = newValue + value; | ||
260 | } else { | ||
261 | this.element.value = value; | ||
262 | } | ||
263 | this.element.focus(); | ||
264 | |||
265 | if (this.options.afterUpdateElement) | ||
266 | this.options.afterUpdateElement(this.element, selectedElement); | ||
267 | }, | ||
268 | |||
269 | updateChoices: function(choices) { | ||
270 | if(!this.changed && this.hasFocus) { | ||
271 | this.update.innerHTML = choices; | ||
272 | Element.cleanWhitespace(this.update); | ||
273 | Element.cleanWhitespace(this.update.down()); | ||
274 | |||
275 | if(this.update.firstChild && this.update.down().childNodes) { | ||
276 | this.entryCount = | ||
277 | this.update.down().childNodes.length; | ||
278 | for (var i = 0; i < this.entryCount; i++) { | ||
279 | var entry = this.getEntry(i); | ||
280 | entry.autocompleteIndex = i; | ||
281 | this.addObservers(entry); | ||
282 | } | ||
283 | } else { | ||
284 | this.entryCount = 0; | ||
285 | } | ||
286 | |||
287 | this.stopIndicator(); | ||
288 | this.index = 0; | ||
289 | |||
290 | if(this.entryCount==1 && this.options.autoSelect) { | ||
291 | this.selectEntry(); | ||
292 | this.hide(); | ||
293 | } else { | ||
294 | this.render(); | ||
295 | } | ||
296 | } | ||
297 | }, | ||
298 | |||
299 | addObservers: function(element) { | ||
300 | Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this)); | ||
301 | Event.observe(element, "click", this.onClick.bindAsEventListener(this)); | ||
302 | }, | ||
303 | |||
304 | onObserverEvent: function() { | ||
305 | this.changed = false; | ||
306 | if(this.getToken().length>=this.options.minChars) { | ||
307 | this.getUpdatedChoices(); | ||
308 | } else { | ||
309 | this.active = false; | ||
310 | this.hide(); | ||
311 | } | ||
312 | }, | ||
313 | |||
314 | getToken: function() { | ||
315 | var tokenPos = this.findLastToken(); | ||
316 | if (tokenPos != -1) | ||
317 | var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,''); | ||
318 | else | ||
319 | var ret = this.element.value; | ||
320 | |||
321 | return /\n/.test(ret) ? '' : ret; | ||
322 | }, | ||
323 | |||
324 | findLastToken: function() { | ||
325 | var lastTokenPos = -1; | ||
326 | |||
327 | for (var i=0; i<this.options.tokens.length; i++) { | ||
328 | var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]); | ||
329 | if (thisTokenPos > lastTokenPos) | ||
330 | lastTokenPos = thisTokenPos; | ||
331 | } | ||
332 | return lastTokenPos; | ||
333 | } | ||
334 | } | ||
335 | |||
336 | Ajax.Autocompleter = Class.create(); | ||
337 | Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), { | ||
338 | initialize: function(element, update, url, options) { | ||
339 | this.baseInitialize(element, update, options); | ||
340 | this.options.asynchronous = true; | ||
341 | this.options.onComplete = this.onComplete.bind(this); | ||
342 | this.options.defaultParams = this.options.parameters || null; | ||
343 | this.url = url; | ||
344 | }, | ||
345 | |||
346 | getUpdatedChoices: function() { | ||
347 | this.startIndicator(); | ||
348 | |||
349 | var entry = encodeURIComponent(this.options.paramName) + '=' + | ||
350 | encodeURIComponent(this.getToken()); | ||
351 | |||
352 | this.options.parameters = this.options.callback ? | ||
353 | this.options.callback(this.element, entry) : entry; | ||
354 | |||
355 | if(this.options.defaultParams) | ||
356 | this.options.parameters += '&' + this.options.defaultParams; | ||
357 | |||
358 | new Ajax.Request(this.url, this.options); | ||
359 | }, | ||
360 | |||
361 | onComplete: function(request) { | ||
362 | this.updateChoices(request.responseText); | ||
363 | } | ||
364 | |||
365 | }); | ||
366 | |||
367 | // The local array autocompleter. Used when you'd prefer to | ||
368 | // inject an array of autocompletion options into the page, rather | ||
369 | // than sending out Ajax queries, which can be quite slow sometimes. | ||
370 | // | ||
371 | // The constructor takes four parameters. The first two are, as usual, | ||
372 | // the id of the monitored textbox, and id of the autocompletion menu. | ||
373 | // The third is the array you want to autocomplete from, and the fourth | ||
374 | // is the options block. | ||
375 | // | ||
376 | // Extra local autocompletion options: | ||
377 | // - choices - How many autocompletion choices to offer | ||
378 | // | ||
379 | // - partialSearch - If false, the autocompleter will match entered | ||
380 | // text only at the beginning of strings in the | ||
381 | // autocomplete array. Defaults to true, which will | ||
382 | // match text at the beginning of any *word* in the | ||
383 | // strings in the autocomplete array. If you want to | ||
384 | // search anywhere in the string, additionally set | ||
385 | // the option fullSearch to true (default: off). | ||
386 | // | ||
387 | // - fullSsearch - Search anywhere in autocomplete array strings. | ||
388 | // | ||
389 | // - partialChars - How many characters to enter before triggering | ||
390 | // a partial match (unlike minChars, which defines | ||
391 | // how many characters are required to do any match | ||
392 | // at all). Defaults to 2. | ||
393 | // | ||
394 | // - ignoreCase - Whether to ignore case when autocompleting. | ||
395 | // Defaults to true. | ||
396 | // | ||
397 | // It's possible to pass in a custom function as the 'selector' | ||
398 | // option, if you prefer to write your own autocompletion logic. | ||
399 | // In that case, the other options above will not apply unless | ||
400 | // you support them. | ||
401 | |||
402 | Autocompleter.Local = Class.create(); | ||
403 | Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), { | ||
404 | initialize: function(element, update, array, options) { | ||
405 | this.baseInitialize(element, update, options); | ||
406 | this.options.array = array; | ||
407 | }, | ||
408 | |||
409 | getUpdatedChoices: function() { | ||
410 | this.updateChoices(this.options.selector(this)); | ||
411 | }, | ||
412 | |||
413 | setOptions: function(options) { | ||
414 | this.options = Object.extend({ | ||
415 | choices: 10, | ||
416 | partialSearch: true, | ||
417 | partialChars: 2, | ||
418 | ignoreCase: true, | ||
419 | fullSearch: false, | ||
420 | selector: function(instance) { | ||
421 | var ret = []; // Beginning matches | ||
422 | var partial = []; // Inside matches | ||
423 | var entry = instance.getToken(); | ||
424 | var count = 0; | ||
425 | |||
426 | for (var i = 0; i < instance.options.array.length && | ||
427 | ret.length < instance.options.choices ; i++) { | ||
428 | |||
429 | var elem = instance.options.array[i]; | ||
430 | var foundPos = instance.options.ignoreCase ? | ||
431 | elem.toLowerCase().indexOf(entry.toLowerCase()) : | ||
432 | elem.indexOf(entry); | ||
433 | |||
434 | while (foundPos != -1) { | ||
435 | if (foundPos == 0 && elem.length != entry.length) { | ||
436 | ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" + | ||
437 | elem.substr(entry.length) + "</li>"); | ||
438 | break; | ||
439 | } else if (entry.length >= instance.options.partialChars && | ||
440 | instance.options.partialSearch && foundPos != -1) { | ||
441 | if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) { | ||
442 | partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" + | ||
443 | elem.substr(foundPos, entry.length) + "</strong>" + elem.substr( | ||
444 | foundPos + entry.length) + "</li>"); | ||
445 | break; | ||
446 | } | ||
447 | } | ||
448 | |||
449 | foundPos = instance.options.ignoreCase ? | ||
450 | elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : | ||
451 | elem.indexOf(entry, foundPos + 1); | ||
452 | |||
453 | } | ||
454 | } | ||
455 | if (partial.length) | ||
456 | ret = ret.concat(partial.slice(0, instance.options.choices - ret.length)) | ||
457 | return "<ul>" + ret.join('') + "</ul>"; | ||
458 | } | ||
459 | }, options || {}); | ||
460 | } | ||
461 | }); | ||
462 | |||
463 | // AJAX in-place editor | ||
464 | // | ||
465 | // see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor | ||
466 | |||
467 | // Use this if you notice weird scrolling problems on some browsers, | ||
468 | // the DOM might be a bit confused when this gets called so do this | ||
469 | // waits 1 ms (with setTimeout) until it does the activation | ||
470 | Field.scrollFreeActivate = function(field) { | ||
471 | setTimeout(function() { | ||
472 | Field.activate(field); | ||
473 | }, 1); | ||
474 | } | ||
475 | |||
476 | Ajax.InPlaceEditor = Class.create(); | ||
477 | Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99"; | ||
478 | Ajax.InPlaceEditor.prototype = { | ||
479 | initialize: function(element, url, options) { | ||
480 | this.url = url; | ||
481 | this.element = $(element); | ||
482 | |||
483 | this.options = Object.extend({ | ||
484 | paramName: "value", | ||
485 | okButton: true, | ||
486 | okLink: false, | ||
487 | okText: "ok", | ||
488 | cancelButton: false, | ||
489 | cancelLink: true, | ||
490 | cancelText: "cancel", | ||
491 | textBeforeControls: '', | ||
492 | textBetweenControls: '', | ||
493 | textAfterControls: '', | ||
494 | savingText: "Saving...", | ||
495 | clickToEditText: "Click to edit", | ||
496 | okText: "ok", | ||
497 | rows: 1, | ||
498 | onComplete: function(transport, element) { | ||
499 | new Effect.Highlight(element, {startcolor: this.options.highlightcolor}); | ||
500 | }, | ||
501 | onFailure: function(transport) { | ||
502 | alert("Error communicating with the server: " + transport.responseText.stripTags()); | ||
503 | }, | ||
504 | callback: function(form) { | ||
505 | return Form.serialize(form); | ||
506 | }, | ||
507 | handleLineBreaks: true, | ||
508 | loadingText: 'Loading...', | ||
509 | savingClassName: 'inplaceeditor-saving', | ||
510 | loadingClassName: 'inplaceeditor-loading', | ||
511 | formClassName: 'inplaceeditor-form', | ||
512 | highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor, | ||
513 | highlightendcolor: "#FFFFFF", | ||
514 | externalControl: null, | ||
515 | submitOnBlur: false, | ||
516 | ajaxOptions: {}, | ||
517 | evalScripts: false | ||
518 | }, options || {}); | ||
519 | |||
520 | if(!this.options.formId && this.element.id) { | ||
521 | this.options.formId = this.element.id + "-inplaceeditor"; | ||
522 | if ($(this.options.formId)) { | ||
523 | // there's already a form with that name, don't specify an id | ||
524 | this.options.formId = null; | ||
525 | } | ||
526 | } | ||
527 | |||
528 | if (this.options.externalControl) { | ||
529 | this.options.externalControl = $(this.options.externalControl); | ||
530 | } | ||
531 | |||
532 | this.originalBackground = Element.getStyle(this.element, 'background-color'); | ||
533 | if (!this.originalBackground) { | ||
534 | this.originalBackground = "transparent"; | ||
535 | } | ||
536 | |||
537 | this.element.title = this.options.clickToEditText; | ||
538 | |||
539 | this.onclickListener = this.enterEditMode.bindAsEventListener(this); | ||
540 | this.mouseoverListener = this.enterHover.bindAsEventListener(this); | ||
541 | this.mouseoutListener = this.leaveHover.bindAsEventListener(this); | ||
542 | Event.observe(this.element, 'click', this.onclickListener); | ||
543 | Event.observe(this.element, 'mouseover', this.mouseoverListener); | ||
544 | Event.observe(this.element, 'mouseout', this.mouseoutListener); | ||
545 | if (this.options.externalControl) { | ||
546 | Event.observe(this.options.externalControl, 'click', this.onclickListener); | ||
547 | Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener); | ||
548 | Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener); | ||
549 | } | ||
550 | }, | ||
551 | enterEditMode: function(evt) { | ||
552 | if (this.saving) return; | ||
553 | if (this.editing) return; | ||
554 | this.editing = true; | ||
555 | this.onEnterEditMode(); | ||
556 | if (this.options.externalControl) { | ||
557 | Element.hide(this.options.externalControl); | ||
558 | } | ||
559 | Element.hide(this.element); | ||
560 | this.createForm(); | ||
561 | this.element.parentNode.insertBefore(this.form, this.element); | ||
562 | if (!this.options.loadTextURL) Field.scrollFreeActivate(this.editField); | ||
563 | // stop the event to avoid a page refresh in Safari | ||
564 | if (evt) { | ||
565 | Event.stop(evt); | ||
566 | } | ||
567 | return false; | ||
568 | }, | ||
569 | createForm: function() { | ||
570 | this.form = document.createElement("form"); | ||
571 | this.form.id = this.options.formId; | ||
572 | Element.addClassName(this.form, this.options.formClassName) | ||
573 | this.form.onsubmit = this.onSubmit.bind(this); | ||
574 | |||
575 | this.createEditField(); | ||
576 | |||
577 | if (this.options.textarea) { | ||
578 | var br = document.createElement("br"); | ||
579 | this.form.appendChild(br); | ||
580 | } | ||
581 | |||
582 | if (this.options.textBeforeControls) | ||
583 | this.form.appendChild(document.createTextNode(this.options.textBeforeControls)); | ||
584 | |||
585 | if (this.options.okButton) { | ||
586 | var okButton = document.createElement("input"); | ||
587 | okButton.type = "submit"; | ||
588 | okButton.value = this.options.okText; | ||
589 | okButton.className = 'editor_ok_button'; | ||
590 | this.form.appendChild(okButton); | ||
591 | } | ||
592 | |||
593 | if (this.options.okLink) { | ||
594 | var okLink = document.createElement("a"); | ||
595 | okLink.href = "#"; | ||
596 | okLink.appendChild(document.createTextNode(this.options.okText)); | ||
597 | okLink.onclick = this.onSubmit.bind(this); | ||
598 | okLink.className = 'editor_ok_link'; | ||
599 | this.form.appendChild(okLink); | ||
600 | } | ||
601 | |||
602 | if (this.options.textBetweenControls && | ||
603 | (this.options.okLink || this.options.okButton) && | ||
604 | (this.options.cancelLink || this.options.cancelButton)) | ||
605 | this.form.appendChild(document.createTextNode(this.options.textBetweenControls)); | ||
606 | |||
607 | if (this.options.cancelButton) { | ||
608 | var cancelButton = document.createElement("input"); | ||
609 | cancelButton.type = "submit"; | ||
610 | cancelButton.value = this.options.cancelText; | ||
611 | cancelButton.onclick = this.onclickCancel.bind(this); | ||
612 | cancelButton.className = 'editor_cancel_button'; | ||
613 | this.form.appendChild(cancelButton); | ||
614 | } | ||
615 | |||
616 | if (this.options.cancelLink) { | ||
617 | var cancelLink = document.createElement("a"); | ||
618 | cancelLink.href = "#"; | ||
619 | cancelLink.appendChild(document.createTextNode(this.options.cancelText)); | ||
620 | cancelLink.onclick = this.onclickCancel.bind(this); | ||
621 | cancelLink.className = 'editor_cancel editor_cancel_link'; | ||
622 | this.form.appendChild(cancelLink); | ||
623 | } | ||
624 | |||
625 | if (this.options.textAfterControls) | ||
626 | this.form.appendChild(document.createTextNode(this.options.textAfterControls)); | ||
627 | }, | ||
628 | hasHTMLLineBreaks: function(string) { | ||
629 | if (!this.options.handleLineBreaks) return false; | ||
630 | return string.match(/<br/i) || string.match(/<p>/i); | ||
631 | }, | ||
632 | convertHTMLLineBreaks: function(string) { | ||
633 | return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, ""); | ||
634 | }, | ||
635 | createEditField: function() { | ||
636 | var text; | ||
637 | if(this.options.loadTextURL) { | ||
638 | text = this.options.loadingText; | ||
639 | } else { | ||
640 | text = this.getText(); | ||
641 | } | ||
642 | |||
643 | var obj = this; | ||
644 | |||
645 | if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) { | ||
646 | this.options.textarea = false; | ||
647 | var textField = document.createElement("input"); | ||
648 | textField.obj = this; | ||
649 | textField.type = "text"; | ||
650 | textField.name = this.options.paramName; | ||
651 | textField.value = text; | ||
652 | textField.style.backgroundColor = this.options.highlightcolor; | ||
653 | textField.className = 'editor_field'; | ||
654 | var size = this.options.size || this.options.cols || 0; | ||
655 | if (size != 0) textField.size = size; | ||
656 | if (this.options.submitOnBlur) | ||
657 | textField.onblur = this.onSubmit.bind(this); | ||
658 | this.editField = textField; | ||
659 | } else { | ||
660 | this.options.textarea = true; | ||
661 | var textArea = document.createElement("textarea"); | ||
662 | textArea.obj = this; | ||
663 | textArea.name = this.options.paramName; | ||
664 | textArea.value = this.convertHTMLLineBreaks(text); | ||
665 | textArea.rows = this.options.rows; | ||
666 | textArea.cols = this.options.cols || 40; | ||
667 | textArea.className = 'editor_field'; | ||
668 | if (this.options.submitOnBlur) | ||
669 | textArea.onblur = this.onSubmit.bind(this); | ||
670 | this.editField = textArea; | ||
671 | } | ||
672 | |||
673 | if(this.options.loadTextURL) { | ||
674 | this.loadExternalText(); | ||
675 | } | ||
676 | this.form.appendChild(this.editField); | ||
677 | }, | ||
678 | getText: function() { | ||
679 | return this.element.innerHTML; | ||
680 | }, | ||
681 | loadExternalText: function() { | ||
682 | Element.addClassName(this.form, this.options.loadingClassName); | ||
683 | this.editField.disabled = true; | ||
684 | new Ajax.Request( | ||
685 | this.options.loadTextURL, | ||
686 | Object.extend({ | ||
687 | asynchronous: true, | ||
688 | onComplete: this.onLoadedExternalText.bind(this) | ||
689 | }, this.options.ajaxOptions) | ||
690 | ); | ||
691 | }, | ||
692 | onLoadedExternalText: function(transport) { | ||
693 | Element.removeClassName(this.form, this.options.loadingClassName); | ||
694 | this.editField.disabled = false; | ||
695 | this.editField.value = transport.responseText.stripTags(); | ||
696 | Field.scrollFreeActivate(this.editField); | ||
697 | }, | ||
698 | onclickCancel: function() { | ||
699 | this.onComplete(); | ||
700 | this.leaveEditMode(); | ||
701 | return false; | ||
702 | }, | ||
703 | onFailure: function(transport) { | ||
704 | this.options.onFailure(transport); | ||
705 | if (this.oldInnerHTML) { | ||
706 | this.element.innerHTML = this.oldInnerHTML; | ||
707 | this.oldInnerHTML = null; | ||
708 | } | ||
709 | return false; | ||
710 | }, | ||
711 | onSubmit: function() { | ||
712 | // onLoading resets these so we need to save them away for the Ajax call | ||
713 | var form = this.form; | ||
714 | var value = this.editField.value; | ||
715 | |||
716 | // do this first, sometimes the ajax call returns before we get a chance to switch on Saving... | ||
717 | // which means this will actually switch on Saving... *after* we've left edit mode causing Saving... | ||
718 | // to be displayed indefinitely | ||
719 | this.onLoading(); | ||
720 | |||
721 | if (this.options.evalScripts) { | ||
722 | new Ajax.Request( | ||
723 | this.url, Object.extend({ | ||
724 | parameters: this.options.callback(form, value), | ||
725 | onComplete: this.onComplete.bind(this), | ||
726 | onFailure: this.onFailure.bind(this), | ||
727 | asynchronous:true, | ||
728 | evalScripts:true | ||
729 | }, this.options.ajaxOptions)); | ||
730 | } else { | ||
731 | new Ajax.Updater( | ||
732 | { success: this.element, | ||
733 | // don't update on failure (this could be an option) | ||
734 | failure: null }, | ||
735 | this.url, Object.extend({ | ||
736 | parameters: this.options.callback(form, value), | ||
737 | onComplete: this.onComplete.bind(this), | ||
738 | onFailure: this.onFailure.bind(this) | ||
739 | }, this.options.ajaxOptions)); | ||
740 | } | ||
741 | // stop the event to avoid a page refresh in Safari | ||
742 | if (arguments.length > 1) { | ||
743 | Event.stop(arguments[0]); | ||
744 | } | ||
745 | return false; | ||
746 | }, | ||
747 | onLoading: function() { | ||
748 | this.saving = true; | ||
749 | this.removeForm(); | ||
750 | this.leaveHover(); | ||
751 | this.showSaving(); | ||
752 | }, | ||
753 | showSaving: function() { | ||
754 | this.oldInnerHTML = this.element.innerHTML; | ||
755 | this.element.innerHTML = this.options.savingText; | ||
756 | Element.addClassName(this.element, this.options.savingClassName); | ||
757 | this.element.style.backgroundColor = this.originalBackground; | ||
758 | Element.show(this.element); | ||
759 | }, | ||
760 | removeForm: function() { | ||
761 | if(this.form) { | ||
762 | if (this.form.parentNode) Element.remove(this.form); | ||
763 | this.form = null; | ||
764 | } | ||
765 | }, | ||
766 | enterHover: function() { | ||
767 | if (this.saving) return; | ||
768 | this.element.style.backgroundColor = this.options.highlightcolor; | ||
769 | if (this.effect) { | ||
770 | this.effect.cancel(); | ||
771 | } | ||
772 | Element.addClassName(this.element, this.options.hoverClassName) | ||
773 | }, | ||
774 | leaveHover: function() { | ||
775 | if (this.options.backgroundColor) { | ||
776 | this.element.style.backgroundColor = this.oldBackground; | ||
777 | } | ||
778 | Element.removeClassName(this.element, this.options.hoverClassName) | ||
779 | if (this.saving) return; | ||
780 | this.effect = new Effect.Highlight(this.element, { | ||
781 | startcolor: this.options.highlightcolor, | ||
782 | endcolor: this.options.highlightendcolor, | ||
783 | restorecolor: this.originalBackground | ||
784 | }); | ||
785 | }, | ||
786 | leaveEditMode: function() { | ||
787 | Element.removeClassName(this.element, this.options.savingClassName); | ||
788 | this.removeForm(); | ||
789 | this.leaveHover(); | ||
790 | this.element.style.backgroundColor = this.originalBackground; | ||
791 | Element.show(this.element); | ||
792 | if (this.options.externalControl) { | ||
793 | Element.show(this.options.externalControl); | ||
794 | } | ||
795 | this.editing = false; | ||
796 | this.saving = false; | ||
797 | this.oldInnerHTML = null; | ||
798 | this.onLeaveEditMode(); | ||
799 | }, | ||
800 | onComplete: function(transport) { | ||
801 | this.leaveEditMode(); | ||
802 | this.options.onComplete.bind(this)(transport, this.element); | ||
803 | }, | ||
804 | onEnterEditMode: function() {}, | ||
805 | onLeaveEditMode: function() {}, | ||
806 | dispose: function() { | ||
807 | if (this.oldInnerHTML) { | ||
808 | this.element.innerHTML = this.oldInnerHTML; | ||
809 | } | ||
810 | this.leaveEditMode(); | ||
811 | Event.stopObserving(this.element, 'click', this.onclickListener); | ||
812 | Event.stopObserving(this.element, 'mouseover', this.mouseoverListener); | ||
813 | Event.stopObserving(this.element, 'mouseout', this.mouseoutListener); | ||
814 | if (this.options.externalControl) { | ||
815 | Event.stopObserving(this.options.externalControl, 'click', this.onclickListener); | ||
816 | Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener); | ||
817 | Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener); | ||
818 | } | ||
819 | } | ||
820 | }; | ||
821 | |||
822 | Ajax.InPlaceCollectionEditor = Class.create(); | ||
823 | Object.extend(Ajax.InPlaceCollectionEditor.prototype, Ajax.InPlaceEditor.prototype); | ||
824 | Object.extend(Ajax.InPlaceCollectionEditor.prototype, { | ||
825 | createEditField: function() { | ||
826 | if (!this.cached_selectTag) { | ||
827 | var selectTag = document.createElement("select"); | ||
828 | var collection = this.options.collection || []; | ||
829 | var optionTag; | ||
830 | collection.each(function(e,i) { | ||
831 | optionTag = document.createElement("option"); | ||
832 | optionTag.value = (e instanceof Array) ? e[0] : e; | ||
833 | if((typeof this.options.value == 'undefined') && | ||
834 | ((e instanceof Array) ? this.element.innerHTML == e[1] : e == optionTag.value)) optionTag.selected = true; | ||
835 | if(this.options.value==optionTag.value) optionTag.selected = true; | ||
836 | optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e)); | ||
837 | selectTag.appendChild(optionTag); | ||
838 | }.bind(this)); | ||
839 | this.cached_selectTag = selectTag; | ||
840 | } | ||
841 | |||
842 | this.editField = this.cached_selectTag; | ||
843 | if(this.options.loadTextURL) this.loadExternalText(); | ||
844 | this.form.appendChild(this.editField); | ||
845 | this.options.callback = function(form, value) { | ||
846 | return "value=" + encodeURIComponent(value); | ||
847 | } | ||
848 | } | ||
849 | }); | ||
850 | |||
851 | // Delayed observer, like Form.Element.Observer, | ||
852 | // but waits for delay after last key input | ||
853 | // Ideal for live-search fields | ||
854 | |||
855 | Form.Element.DelayedObserver = Class.create(); | ||
856 | Form.Element.DelayedObserver.prototype = { | ||
857 | initialize: function(element, delay, callback) { | ||
858 | this.delay = delay || 0.5; | ||
859 | this.element = $(element); | ||
860 | this.callback = callback; | ||
861 | this.timer = null; | ||
862 | this.lastValue = $F(this.element); | ||
863 | Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this)); | ||
864 | }, | ||
865 | delayedListener: function(event) { | ||
866 | if(this.lastValue == $F(this.element)) return; | ||
867 | if(this.timer) clearTimeout(this.timer); | ||
868 | this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000); | ||
869 | this.lastValue = $F(this.element); | ||
870 | }, | ||
871 | onTimerEvent: function() { | ||
872 | this.timer = null; | ||
873 | this.callback(this.element, $F(this.element)); | ||
874 | } | ||
875 | }; | ||
diff --git a/docroot/lib/scriptaculous/dragdrop.js b/docroot/lib/scriptaculous/dragdrop.js new file mode 100755 index 0000000..108dd66 --- /dev/null +++ b/docroot/lib/scriptaculous/dragdrop.js | |||
@@ -0,0 +1,970 @@ | |||
1 | // script.aculo.us dragdrop.js v1.7.1_beta3, Fri May 25 17:19:41 +0200 2007 | ||
2 | |||
3 | // Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) | ||
4 | // (c) 2005-2007 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz) | ||
5 | // | ||
6 | // script.aculo.us is freely distributable under the terms of an MIT-style license. | ||
7 | // For details, see the script.aculo.us web site: http://script.aculo.us/ | ||
8 | |||
9 | if(typeof Effect == 'undefined') | ||
10 | throw("dragdrop.js requires including script.aculo.us' effects.js library"); | ||
11 | |||
12 | var Droppables = { | ||
13 | drops: [], | ||
14 | |||
15 | remove: function(element) { | ||
16 | this.drops = this.drops.reject(function(d) { return d.element==$(element) }); | ||
17 | }, | ||
18 | |||
19 | add: function(element) { | ||
20 | element = $(element); | ||
21 | var options = Object.extend({ | ||
22 | greedy: true, | ||
23 | hoverclass: null, | ||
24 | tree: false | ||
25 | }, arguments[1] || {}); | ||
26 | |||
27 | // cache containers | ||
28 | if(options.containment) { | ||
29 | options._containers = []; | ||
30 | var containment = options.containment; | ||
31 | if((typeof containment == 'object') && | ||
32 | (containment.constructor == Array)) { | ||
33 | containment.each( function(c) { options._containers.push($(c)) }); | ||
34 | } else { | ||
35 | options._containers.push($(containment)); | ||
36 | } | ||
37 | } | ||
38 | |||
39 | if(options.accept) options.accept = [options.accept].flatten(); | ||
40 | |||
41 | Element.makePositioned(element); // fix IE | ||
42 | options.element = element; | ||
43 | |||
44 | this.drops.push(options); | ||
45 | }, | ||
46 | |||
47 | findDeepestChild: function(drops) { | ||
48 | deepest = drops[0]; | ||
49 | |||
50 | for (i = 1; i < drops.length; ++i) | ||
51 | if (Element.isParent(drops[i].element, deepest.element)) | ||
52 | deepest = drops[i]; | ||
53 | |||
54 | return deepest; | ||
55 | }, | ||
56 | |||
57 | isContained: function(element, drop) { | ||
58 | var containmentNode; | ||
59 | if(drop.tree) { | ||
60 | containmentNode = element.treeNode; | ||
61 | } else { | ||
62 | containmentNode = element.parentNode; | ||
63 | } | ||
64 | return drop._containers.detect(function(c) { return containmentNode == c }); | ||
65 | }, | ||
66 | |||
67 | isAffected: function(point, element, drop) { | ||
68 | return ( | ||
69 | (drop.element!=element) && | ||
70 | ((!drop._containers) || | ||
71 | this.isContained(element, drop)) && | ||
72 | ((!drop.accept) || | ||
73 | (Element.classNames(element).detect( | ||
74 | function(v) { return drop.accept.include(v) } ) )) && | ||
75 | Position.within(drop.element, point[0], point[1]) ); | ||
76 | }, | ||
77 | |||
78 | deactivate: function(drop) { | ||
79 | if(drop.hoverclass) | ||
80 | Element.removeClassName(drop.element, drop.hoverclass); | ||
81 | this.last_active = null; | ||
82 | }, | ||
83 | |||
84 | activate: function(drop) { | ||
85 | if(drop.hoverclass) | ||
86 | Element.addClassName(drop.element, drop.hoverclass); | ||
87 | this.last_active = drop; | ||
88 | }, | ||
89 | |||
90 | show: function(point, element) { | ||
91 | if(!this.drops.length) return; | ||
92 | var affected = []; | ||
93 | |||
94 | if(this.last_active) this.deactivate(this.last_active); | ||
95 | this.drops.each( function(drop) { | ||
96 | if(Droppables.isAffected(point, element, drop)) | ||
97 | affected.push(drop); | ||
98 | }); | ||
99 | |||
100 | if(affected.length>0) { | ||
101 | drop = Droppables.findDeepestChild(affected); | ||
102 | Position.within(drop.element, point[0], point[1]); | ||
103 | if(drop.onHover) | ||
104 | drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element)); | ||
105 | |||
106 | Droppables.activate(drop); | ||
107 | } | ||
108 | }, | ||
109 | |||
110 | fire: function(event, element) { | ||
111 | if(!this.last_active) return; | ||
112 | Position.prepare(); | ||
113 | |||
114 | if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active)) | ||
115 | if (this.last_active.onDrop) { | ||
116 | this.last_active.onDrop(element, this.last_active.element, event); | ||
117 | return true; | ||
118 | } | ||
119 | }, | ||
120 | |||
121 | reset: function() { | ||
122 | if(this.last_active) | ||
123 | this.deactivate(this.last_active); | ||
124 | } | ||
125 | } | ||
126 | |||
127 | var Draggables = { | ||
128 | drags: [], | ||
129 | observers: [], | ||
130 | |||
131 | register: function(draggable) { | ||
132 | if(this.drags.length == 0) { | ||
133 | this.eventMouseUp = this.endDrag.bindAsEventListener(this); | ||
134 | this.eventMouseMove = this.updateDrag.bindAsEventListener(this); | ||
135 | this.eventKeypress = this.keyPress.bindAsEventListener(this); | ||
136 | |||
137 | Event.observe(document, "mouseup", this.eventMouseUp); | ||
138 | Event.observe(document, "mousemove", this.eventMouseMove); | ||
139 | Event.observe(document, "keypress", this.eventKeypress); | ||
140 | } | ||
141 | this.drags.push(draggable); | ||
142 | }, | ||
143 | |||
144 | unregister: function(draggable) { | ||
145 | this.drags = this.drags.reject(function(d) { return d==draggable }); | ||
146 | if(this.drags.length == 0) { | ||
147 | Event.stopObserving(document, "mouseup", this.eventMouseUp); | ||
148 | Event.stopObserving(document, "mousemove", this.eventMouseMove); | ||
149 | Event.stopObserving(document, "keypress", this.eventKeypress); | ||
150 | } | ||
151 | }, | ||
152 | |||
153 | activate: function(draggable) { | ||
154 | if(draggable.options.delay) { | ||
155 | this._timeout = setTimeout(function() { | ||
156 | Draggables._timeout = null; | ||
157 | window.focus(); | ||
158 | Draggables.activeDraggable = draggable; | ||
159 | }.bind(this), draggable.options.delay); | ||
160 | } else { | ||
161 | window.focus(); // allows keypress events if window isn't currently focused, fails for Safari | ||
162 | this.activeDraggable = draggable; | ||
163 | } | ||
164 | }, | ||
165 | |||
166 | deactivate: function() { | ||
167 | this.activeDraggable = null; | ||
168 | }, | ||
169 | |||
170 | updateDrag: function(event) { | ||
171 | if(!this.activeDraggable) return; | ||
172 | var pointer = [Event.pointerX(event), Event.pointerY(event)]; | ||
173 | // Mozilla-based browsers fire successive mousemove events with | ||
174 | // the same coordinates, prevent needless redrawing (moz bug?) | ||
175 | if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return; | ||
176 | this._lastPointer = pointer; | ||
177 | |||
178 | this.activeDraggable.updateDrag(event, pointer); | ||
179 | }, | ||
180 | |||
181 | endDrag: function(event) { | ||
182 | if(this._timeout) { | ||
183 | clearTimeout(this._timeout); | ||
184 | this._timeout = null; | ||
185 | } | ||
186 | if(!this.activeDraggable) return; | ||
187 | this._lastPointer = null; | ||
188 | this.activeDraggable.endDrag(event); | ||
189 | this.activeDraggable = null; | ||
190 | }, | ||
191 | |||
192 | keyPress: function(event) { | ||
193 | if(this.activeDraggable) | ||
194 | this.activeDraggable.keyPress(event); | ||
195 | }, | ||
196 | |||
197 | addObserver: function(observer) { | ||
198 | this.observers.push(observer); | ||
199 | this._cacheObserverCallbacks(); | ||
200 | }, | ||
201 | |||
202 | removeObserver: function(element) { // element instead of observer fixes mem leaks | ||
203 | this.observers = this.observers.reject( function(o) { return o.element==element }); | ||
204 | this._cacheObserverCallbacks(); | ||
205 | }, | ||
206 | |||
207 | notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag' | ||
208 | if(this[eventName+'Count'] > 0) | ||
209 | this.observers.each( function(o) { | ||
210 | if(o[eventName]) o[eventName](eventName, draggable, event); | ||
211 | }); | ||
212 | if(draggable.options[eventName]) draggable.options[eventName](draggable, event); | ||
213 | }, | ||
214 | |||
215 | _cacheObserverCallbacks: function() { | ||
216 | ['onStart','onEnd','onDrag'].each( function(eventName) { | ||
217 | Draggables[eventName+'Count'] = Draggables.observers.select( | ||
218 | function(o) { return o[eventName]; } | ||
219 | ).length; | ||
220 | }); | ||
221 | } | ||
222 | } | ||
223 | |||
224 | /*--------------------------------------------------------------------------*/ | ||
225 | |||
226 | var Draggable = Class.create(); | ||
227 | Draggable._dragging = {}; | ||
228 | |||
229 | Draggable.prototype = { | ||
230 | initialize: function(element) { | ||
231 | var defaults = { | ||
232 | handle: false, | ||
233 | reverteffect: function(element, top_offset, left_offset) { | ||
234 | var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02; | ||
235 | new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur, | ||
236 | queue: {scope:'_draggable', position:'end'} | ||
237 | }); | ||
238 | }, | ||
239 | endeffect: function(element) { | ||
240 | var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0; | ||
241 | new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity, | ||
242 | queue: {scope:'_draggable', position:'end'}, | ||
243 | afterFinish: function(){ | ||
244 | Draggable._dragging[element] = false | ||
245 | } | ||
246 | }); | ||
247 | }, | ||
248 | zindex: 1000, | ||
249 | revert: false, | ||
250 | quiet: false, | ||
251 | scroll: false, | ||
252 | scrollSensitivity: 20, | ||
253 | scrollSpeed: 15, | ||
254 | snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] } | ||
255 | delay: 0 | ||
256 | }; | ||
257 | |||
258 | if(!arguments[1] || typeof arguments[1].endeffect == 'undefined') | ||
259 | Object.extend(defaults, { | ||
260 | starteffect: function(element) { | ||
261 | element._opacity = Element.getOpacity(element); | ||
262 | Draggable._dragging[element] = true; | ||
263 | new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7}); | ||
264 | } | ||
265 | }); | ||
266 | |||
267 | var options = Object.extend(defaults, arguments[1] || {}); | ||
268 | |||
269 | this.element = $(element); | ||
270 | |||
271 | if(options.handle && (typeof options.handle == 'string')) | ||
272 | this.handle = this.element.down('.'+options.handle, 0); | ||
273 | |||
274 | if(!this.handle) this.handle = $(options.handle); | ||
275 | if(!this.handle) this.handle = this.element; | ||
276 | |||
277 | if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) { | ||
278 | options.scroll = $(options.scroll); | ||
279 | this._isScrollChild = Element.childOf(this.element, options.scroll); | ||
280 | } | ||
281 | |||
282 | Element.makePositioned(this.element); // fix IE | ||
283 | |||
284 | this.delta = this.currentDelta(); | ||
285 | this.options = options; | ||
286 | this.dragging = false; | ||
287 | |||
288 | this.eventMouseDown = this.initDrag.bindAsEventListener(this); | ||
289 | Event.observe(this.handle, "mousedown", this.eventMouseDown); | ||
290 | |||
291 | Draggables.register(this); | ||
292 | }, | ||
293 | |||
294 | destroy: function() { | ||
295 | Event.stopObserving(this.handle, "mousedown", this.eventMouseDown); | ||
296 | Draggables.unregister(this); | ||
297 | }, | ||
298 | |||
299 | currentDelta: function() { | ||
300 | return([ | ||
301 | parseInt(Element.getStyle(this.element,'left') || '0'), | ||
302 | parseInt(Element.getStyle(this.element,'top') || '0')]); | ||
303 | }, | ||
304 | |||
305 | initDrag: function(event) { | ||
306 | if(typeof Draggable._dragging[this.element] != 'undefined' && | ||
307 | Draggable._dragging[this.element]) return; | ||
308 | if(Event.isLeftClick(event)) { | ||
309 | // abort on form elements, fixes a Firefox issue | ||
310 | var src = Event.element(event); | ||
311 | if((tag_name = src.tagName.toUpperCase()) && ( | ||
312 | tag_name=='INPUT' || | ||
313 | tag_name=='SELECT' || | ||
314 | tag_name=='OPTION' || | ||
315 | tag_name=='BUTTON' || | ||
316 | tag_name=='TEXTAREA')) return; | ||
317 | |||
318 | var pointer = [Event.pointerX(event), Event.pointerY(event)]; | ||
319 | var pos = Position.cumulativeOffset(this.element); | ||
320 | this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) }); | ||
321 | |||
322 | Draggables.activate(this); | ||
323 | Event.stop(event); | ||
324 | } | ||
325 | }, | ||
326 | |||
327 | startDrag: function(event) { | ||
328 | this.dragging = true; | ||
329 | |||
330 | if(this.options.zindex) { | ||
331 | this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0); | ||
332 | this.element.style.zIndex = this.options.zindex; | ||
333 | } | ||
334 | |||
335 | if(this.options.ghosting) { | ||
336 | this._clone = this.element.cloneNode(true); | ||
337 | Position.absolutize(this.element); | ||
338 | this.element.parentNode.insertBefore(this._clone, this.element); | ||
339 | } | ||
340 | |||
341 | if(this.options.scroll) { | ||
342 | if (this.options.scroll == window) { | ||
343 | var where = this._getWindowScroll(this.options.scroll); | ||
344 | this.originalScrollLeft = where.left; | ||
345 | this.originalScrollTop = where.top; | ||
346 | } else { | ||
347 | this.originalScrollLeft = this.options.scroll.scrollLeft; | ||
348 | this.originalScrollTop = this.options.scroll.scrollTop; | ||
349 | } | ||
350 | } | ||
351 | |||
352 | Draggables.notify('onStart', this, event); | ||
353 | |||
354 | if(this.options.starteffect) this.options.starteffect(this.element); | ||
355 | }, | ||
356 | |||
357 | updateDrag: function(event, pointer) { | ||
358 | if(!this.dragging) this.startDrag(event); | ||
359 | |||
360 | if(!this.options.quiet){ | ||
361 | Position.prepare(); | ||
362 | Droppables.show(pointer, this.element); | ||
363 | } | ||
364 | |||
365 | Draggables.notify('onDrag', this, event); | ||
366 | |||
367 | this.draw(pointer); | ||
368 | if(this.options.change) this.options.change(this); | ||
369 | |||
370 | if(this.options.scroll) { | ||
371 | this.stopScrolling(); | ||
372 | |||
373 | var p; | ||
374 | if (this.options.scroll == window) { | ||
375 | with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; } | ||
376 | } else { | ||
377 | p = Position.page(this.options.scroll); | ||
378 | p[0] += this.options.scroll.scrollLeft + Position.deltaX; | ||
379 | p[1] += this.options.scroll.scrollTop + Position.deltaY; | ||
380 | p.push(p[0]+this.options.scroll.offsetWidth); | ||
381 | p.push(p[1]+this.options.scroll.offsetHeight); | ||
382 | } | ||
383 | var speed = [0,0]; | ||
384 | if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity); | ||
385 | if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity); | ||
386 | if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity); | ||
387 | if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity); | ||
388 | this.startScrolling(speed); | ||
389 | } | ||
390 | |||
391 | // fix AppleWebKit rendering | ||
392 | if(Prototype.Browser.WebKit) window.scrollBy(0,0); | ||
393 | |||
394 | Event.stop(event); | ||
395 | }, | ||
396 | |||
397 | finishDrag: function(event, success) { | ||
398 | this.dragging = false; | ||
399 | |||
400 | if(this.options.quiet){ | ||
401 | Position.prepare(); | ||
402 | var pointer = [Event.pointerX(event), Event.pointerY(event)]; | ||
403 | Droppables.show(pointer, this.element); | ||
404 | } | ||
405 | |||
406 | if(this.options.ghosting) { | ||
407 | Position.relativize(this.element); | ||
408 | Element.remove(this._clone); | ||
409 | this._clone = null; | ||
410 | } | ||
411 | |||
412 | var dropped = false; | ||
413 | if(success) { | ||
414 | dropped = Droppables.fire(event, this.element); | ||
415 | if (!dropped) dropped = false; | ||
416 | } | ||
417 | if(dropped && this.options.onDropped) this.options.onDropped(this.element); | ||
418 | Draggables.notify('onEnd', this, event); | ||
419 | |||
420 | var revert = this.options.revert; | ||
421 | if(revert && typeof revert == 'function') revert = revert(this.element); | ||
422 | |||
423 | var d = this.currentDelta(); | ||
424 | if(revert && this.options.reverteffect) { | ||
425 | if (dropped == 0 || revert != 'failure') | ||
426 | this.options.reverteffect(this.element, | ||
427 | d[1]-this.delta[1], d[0]-this.delta[0]); | ||
428 | } else { | ||
429 | this.delta = d; | ||
430 | } | ||
431 | |||
432 | if(this.options.zindex) | ||
433 | this.element.style.zIndex = this.originalZ; | ||
434 | |||
435 | if(this.options.endeffect) | ||
436 | this.options.endeffect(this.element); | ||
437 | |||
438 | Draggables.deactivate(this); | ||
439 | Droppables.reset(); | ||
440 | }, | ||
441 | |||
442 | keyPress: function(event) { | ||
443 | if(event.keyCode!=Event.KEY_ESC) return; | ||
444 | this.finishDrag(event, false); | ||
445 | Event.stop(event); | ||
446 | }, | ||
447 | |||
448 | endDrag: function(event) { | ||
449 | if(!this.dragging) return; | ||
450 | this.stopScrolling(); | ||
451 | this.finishDrag(event, true); | ||
452 | Event.stop(event); | ||
453 | }, | ||
454 | |||
455 | draw: function(point) { | ||
456 | var pos = Position.cumulativeOffset(this.element); | ||
457 | if(this.options.ghosting) { | ||
458 | var r = Position.realOffset(this.element); | ||
459 | pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY; | ||
460 | } | ||
461 | |||
462 | var d = this.currentDelta(); | ||
463 | pos[0] -= d[0]; pos[1] -= d[1]; | ||
464 | |||
465 | if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) { | ||
466 | pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft; | ||
467 | pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop; | ||
468 | } | ||
469 | |||
470 | var p = [0,1].map(function(i){ | ||
471 | return (point[i]-pos[i]-this.offset[i]) | ||
472 | }.bind(this)); | ||
473 | |||
474 | if(this.options.snap) { | ||
475 | if(typeof this.options.snap == 'function') { | ||
476 | p = this.options.snap(p[0],p[1],this); | ||
477 | } else { | ||
478 | if(this.options.snap instanceof Array) { | ||
479 | p = p.map( function(v, i) { | ||
480 | return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this)) | ||
481 | } else { | ||
482 | p = p.map( function(v) { | ||
483 | return Math.round(v/this.options.snap)*this.options.snap }.bind(this)) | ||
484 | } | ||
485 | }} | ||
486 | |||
487 | var style = this.element.style; | ||
488 | if((!this.options.constraint) || (this.options.constraint=='horizontal')) | ||
489 | style.left = p[0] + "px"; | ||
490 | if((!this.options.constraint) || (this.options.constraint=='vertical')) | ||
491 | style.top = p[1] + "px"; | ||
492 | |||
493 | if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering | ||
494 | }, | ||
495 | |||
496 | stopScrolling: function() { | ||
497 | if(this.scrollInterval) { | ||
498 | clearInterval(this.scrollInterval); | ||
499 | this.scrollInterval = null; | ||
500 | Draggables._lastScrollPointer = null; | ||
501 | } | ||
502 | }, | ||
503 | |||
504 | startScrolling: function(speed) { | ||
505 | if(!(speed[0] || speed[1])) return; | ||
506 | this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed]; | ||
507 | this.lastScrolled = new Date(); | ||
508 | this.scrollInterval = setInterval(this.scroll.bind(this), 10); | ||
509 | }, | ||
510 | |||
511 | scroll: function() { | ||
512 | var current = new Date(); | ||
513 | var delta = current - this.lastScrolled; | ||
514 | this.lastScrolled = current; | ||
515 | if(this.options.scroll == window) { | ||
516 | with (this._getWindowScroll(this.options.scroll)) { | ||
517 | if (this.scrollSpeed[0] || this.scrollSpeed[1]) { | ||
518 | var d = delta / 1000; | ||
519 | this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] ); | ||
520 | } | ||
521 | } | ||
522 | } else { | ||
523 | this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000; | ||
524 | this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000; | ||
525 | } | ||
526 | |||
527 | Position.prepare(); | ||
528 | Droppables.show(Draggables._lastPointer, this.element); | ||
529 | Draggables.notify('onDrag', this); | ||
530 | if (this._isScrollChild) { | ||
531 | Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer); | ||
532 | Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000; | ||
533 | Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000; | ||
534 | if (Draggables._lastScrollPointer[0] < 0) | ||
535 | Draggables._lastScrollPointer[0] = 0; | ||
536 | if (Draggables._lastScrollPointer[1] < 0) | ||
537 | Draggables._lastScrollPointer[1] = 0; | ||
538 | this.draw(Draggables._lastScrollPointer); | ||
539 | } | ||
540 | |||
541 | if(this.options.change) this.options.change(this); | ||
542 | }, | ||
543 | |||
544 | _getWindowScroll: function(w) { | ||
545 | var T, L, W, H; | ||
546 | with (w.document) { | ||
547 | if (w.document.documentElement && documentElement.scrollTop) { | ||
548 | T = documentElement.scrollTop; | ||
549 | L = documentElement.scrollLeft; | ||
550 | } else if (w.document.body) { | ||
551 | T = body.scrollTop; | ||
552 | L = body.scrollLeft; | ||
553 | } | ||
554 | if (w.innerWidth) { | ||
555 | W = w.innerWidth; | ||
556 | H = w.innerHeight; | ||
557 | } else if (w.document.documentElement && documentElement.clientWidth) { | ||
558 | W = documentElement.clientWidth; | ||
559 | H = documentElement.clientHeight; | ||
560 | } else { | ||
561 | W = body.offsetWidth; | ||
562 | H = body.offsetHeight | ||
563 | } | ||
564 | } | ||
565 | return { top: T, left: L, width: W, height: H }; | ||
566 | } | ||
567 | } | ||
568 | |||
569 | /*--------------------------------------------------------------------------*/ | ||
570 | |||
571 | var SortableObserver = Class.create(); | ||
572 | SortableObserver.prototype = { | ||
573 | initialize: function(element, observer) { | ||
574 | this.element = $(element); | ||
575 | this.observer = observer; | ||
576 | this.lastValue = Sortable.serialize(this.element); | ||
577 | }, | ||
578 | |||
579 | onStart: function() { | ||
580 | this.lastValue = Sortable.serialize(this.element); | ||
581 | }, | ||
582 | |||
583 | onEnd: function() { | ||
584 | Sortable.unmark(); | ||
585 | if(this.lastValue != Sortable.serialize(this.element)) | ||
586 | this.observer(this.element) | ||
587 | } | ||
588 | } | ||
589 | |||
590 | var Sortable = { | ||
591 | SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/, | ||
592 | |||
593 | sortables: {}, | ||
594 | |||
595 | _findRootElement: function(element) { | ||
596 | while (element.tagName.toUpperCase() != "BODY") { | ||
597 | if(element.id && Sortable.sortables[element.id]) return element; | ||
598 | element = element.parentNode; | ||
599 | } | ||
600 | }, | ||
601 | |||
602 | options: function(element) { | ||
603 | element = Sortable._findRootElement($(element)); | ||
604 | if(!element) return; | ||
605 | return Sortable.sortables[element.id]; | ||
606 | }, | ||
607 | |||
608 | destroy: function(element){ | ||
609 | var s = Sortable.options(element); | ||
610 | |||
611 | if(s) { | ||
612 | Draggables.removeObserver(s.element); | ||
613 | s.droppables.each(function(d){ Droppables.remove(d) }); | ||
614 | s.draggables.invoke('destroy'); | ||
615 | |||
616 | delete Sortable.sortables[s.element.id]; | ||
617 | } | ||
618 | }, | ||
619 | |||
620 | create: function(element) { | ||
621 | element = $(element); | ||
622 | var options = Object.extend({ | ||
623 | element: element, | ||
624 | tag: 'li', // assumes li children, override with tag: 'tagname' | ||
625 | dropOnEmpty: false, | ||
626 | tree: false, | ||
627 | treeTag: 'ul', | ||
628 | overlap: 'vertical', // one of 'vertical', 'horizontal' | ||
629 | constraint: 'vertical', // one of 'vertical', 'horizontal', false | ||
630 | containment: element, // also takes array of elements (or id's); or false | ||
631 | handle: false, // or a CSS class | ||
632 | only: false, | ||
633 | delay: 0, | ||
634 | hoverclass: null, | ||
635 | ghosting: false, | ||
636 | quiet: false, | ||
637 | scroll: false, | ||
638 | scrollSensitivity: 20, | ||
639 | scrollSpeed: 15, | ||
640 | format: this.SERIALIZE_RULE, | ||
641 | |||
642 | // these take arrays of elements or ids and can be | ||
643 | // used for better initialization performance | ||
644 | elements: false, | ||
645 | handles: false, | ||
646 | |||
647 | onChange: Prototype.emptyFunction, | ||
648 | onUpdate: Prototype.emptyFunction | ||
649 | }, arguments[1] || {}); | ||
650 | |||
651 | // clear any old sortable with same element | ||
652 | this.destroy(element); | ||
653 | |||
654 | // build options for the draggables | ||
655 | var options_for_draggable = { | ||
656 | revert: true, | ||
657 | quiet: options.quiet, | ||
658 | scroll: options.scroll, | ||
659 | scrollSpeed: options.scrollSpeed, | ||
660 | scrollSensitivity: options.scrollSensitivity, | ||
661 | delay: options.delay, | ||
662 | ghosting: options.ghosting, | ||
663 | constraint: options.constraint, | ||
664 | handle: options.handle }; | ||
665 | |||
666 | if(options.starteffect) | ||
667 | options_for_draggable.starteffect = options.starteffect; | ||
668 | |||
669 | if(options.reverteffect) | ||
670 | options_for_draggable.reverteffect = options.reverteffect; | ||
671 | else | ||
672 | if(options.ghosting) options_for_draggable.reverteffect = function(element) { | ||
673 | element.style.top = 0; | ||
674 | element.style.left = 0; | ||
675 | }; | ||
676 | |||
677 | if(options.endeffect) | ||
678 | options_for_draggable.endeffect = options.endeffect; | ||
679 | |||
680 | if(options.zindex) | ||
681 | options_for_draggable.zindex = options.zindex; | ||
682 | |||
683 | // build options for the droppables | ||
684 | var options_for_droppable = { | ||
685 | overlap: options.overlap, | ||
686 | containment: options.containment, | ||
687 | tree: options.tree, | ||
688 | hoverclass: options.hoverclass, | ||
689 | onHover: Sortable.onHover | ||
690 | } | ||
691 | |||
692 | var options_for_tree = { | ||
693 | onHover: Sortable.onEmptyHover, | ||
694 | overlap: options.overlap, | ||
695 | containment: options.containment, | ||
696 | hoverclass: options.hoverclass | ||
697 | } | ||
698 | |||
699 | // fix for gecko engine | ||
700 | Element.cleanWhitespace(element); | ||
701 | |||
702 | options.draggables = []; | ||
703 | options.droppables = []; | ||
704 | |||
705 | // drop on empty handling | ||
706 | if(options.dropOnEmpty || options.tree) { | ||
707 | Droppables.add(element, options_for_tree); | ||
708 | options.droppables.push(element); | ||
709 | } | ||
710 | |||
711 | (options.elements || this.findElements(element, options) || []).each( function(e,i) { | ||
712 | var handle = options.handles ? $(options.handles[i]) : | ||
713 | (options.handle ? $(e).getElementsByClassName(options.handle)[0] : e); | ||
714 | options.draggables.push( | ||
715 | new Draggable(e, Object.extend(options_for_draggable, { handle: handle }))); | ||
716 | Droppables.add(e, options_for_droppable); | ||
717 | if(options.tree) e.treeNode = element; | ||
718 | options.droppables.push(e); | ||
719 | }); | ||
720 | |||
721 | if(options.tree) { | ||
722 | (Sortable.findTreeElements(element, options) || []).each( function(e) { | ||
723 | Droppables.add(e, options_for_tree); | ||
724 | e.treeNode = element; | ||
725 | options.droppables.push(e); | ||
726 | }); | ||
727 | } | ||
728 | |||
729 | // keep reference | ||
730 | this.sortables[element.id] = options; | ||
731 | |||
732 | // for onupdate | ||
733 | Draggables.addObserver(new SortableObserver(element, options.onUpdate)); | ||
734 | |||
735 | }, | ||
736 | |||
737 | // return all suitable-for-sortable elements in a guaranteed order | ||
738 | findElements: function(element, options) { | ||
739 | return Element.findChildren( | ||
740 | element, options.only, options.tree ? true : false, options.tag); | ||
741 | }, | ||
742 | |||
743 | findTreeElements: function(element, options) { | ||
744 | return Element.findChildren( | ||
745 | element, options.only, options.tree ? true : false, options.treeTag); | ||
746 | }, | ||
747 | |||
748 | onHover: function(element, dropon, overlap) { | ||
749 | if(Element.isParent(dropon, element)) return; | ||
750 | |||
751 | if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) { | ||
752 | return; | ||
753 | } else if(overlap>0.5) { | ||
754 | Sortable.mark(dropon, 'before'); | ||
755 | if(dropon.previousSibling != element) { | ||
756 | var oldParentNode = element.parentNode; | ||
757 | element.style.visibility = "hidden"; // fix gecko rendering | ||
758 | dropon.parentNode.insertBefore(element, dropon); | ||
759 | if(dropon.parentNode!=oldParentNode) | ||
760 | Sortable.options(oldParentNode).onChange(element); | ||
761 | Sortable.options(dropon.parentNode).onChange(element); | ||
762 | } | ||
763 | } else { | ||
764 | Sortable.mark(dropon, 'after'); | ||
765 | var nextElement = dropon.nextSibling || null; | ||
766 | if(nextElement != element) { | ||
767 | var oldParentNode = element.parentNode; | ||
768 | element.style.visibility = "hidden"; // fix gecko rendering | ||
769 | dropon.parentNode.insertBefore(element, nextElement); | ||
770 | if(dropon.parentNode!=oldParentNode) | ||
771 | Sortable.options(oldParentNode).onChange(element); | ||
772 | Sortable.options(dropon.parentNode).onChange(element); | ||
773 | } | ||
774 | } | ||
775 | }, | ||
776 | |||
777 | onEmptyHover: function(element, dropon, overlap) { | ||
778 | var oldParentNode = element.parentNode; | ||
779 | var droponOptions = Sortable.options(dropon); | ||
780 | |||
781 | if(!Element.isParent(dropon, element)) { | ||
782 | var index; | ||
783 | |||
784 | var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only}); | ||
785 | var child = null; | ||
786 | |||
787 | if(children) { | ||
788 | var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap); | ||
789 | |||
790 | for (index = 0; index < children.length; index += 1) { | ||
791 | if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) { | ||
792 | offset -= Element.offsetSize (children[index], droponOptions.overlap); | ||
793 | } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) { | ||
794 | child = index + 1 < children.length ? children[index + 1] : null; | ||
795 | break; | ||
796 | } else { | ||
797 | child = children[index]; | ||
798 | break; | ||
799 | } | ||
800 | } | ||
801 | } | ||
802 | |||
803 | dropon.insertBefore(element, child); | ||
804 | |||
805 | Sortable.options(oldParentNode).onChange(element); | ||
806 | droponOptions.onChange(element); | ||
807 | } | ||
808 | }, | ||
809 | |||
810 | unmark: function() { | ||
811 | if(Sortable._marker) Sortable._marker.hide(); | ||
812 | }, | ||
813 | |||
814 | mark: function(dropon, position) { | ||
815 | // mark on ghosting only | ||
816 | var sortable = Sortable.options(dropon.parentNode); | ||
817 | if(sortable && !sortable.ghosting) return; | ||
818 | |||
819 | if(!Sortable._marker) { | ||
820 | Sortable._marker = | ||
821 | ($('dropmarker') || Element.extend(document.createElement('DIV'))). | ||
822 | hide().addClassName('dropmarker').setStyle({position:'absolute'}); | ||
823 | document.getElementsByTagName("body").item(0).appendChild(Sortable._marker); | ||
824 | } | ||
825 | var offsets = Position.cumulativeOffset(dropon); | ||
826 | Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'}); | ||
827 | |||
828 | if(position=='after') | ||
829 | if(sortable.overlap == 'horizontal') | ||
830 | Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'}); | ||
831 | else | ||
832 | Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'}); | ||
833 | |||
834 | Sortable._marker.show(); | ||
835 | }, | ||
836 | |||
837 | _tree: function(element, options, parent) { | ||
838 | var children = Sortable.findElements(element, options) || []; | ||
839 | |||
840 | for (var i = 0; i < children.length; ++i) { | ||
841 | var match = children[i].id.match(options.format); | ||
842 | |||
843 | if (!match) continue; | ||
844 | |||
845 | var child = { | ||
846 | id: encodeURIComponent(match ? match[1] : null), | ||
847 | element: element, | ||
848 | parent: parent, | ||
849 | children: [], | ||
850 | position: parent.children.length, | ||
851 | container: $(children[i]).down(options.treeTag) | ||
852 | } | ||
853 | |||
854 | /* Get the element containing the children and recurse over it */ | ||
855 | if (child.container) | ||
856 | this._tree(child.container, options, child) | ||
857 | |||
858 | parent.children.push (child); | ||
859 | } | ||
860 | |||
861 | return parent; | ||
862 | }, | ||
863 | |||
864 | tree: function(element) { | ||
865 | element = $(element); | ||
866 | var sortableOptions = this.options(element); | ||
867 | var options = Object.extend({ | ||
868 | tag: sortableOptions.tag, | ||
869 | treeTag: sortableOptions.treeTag, | ||
870 | only: sortableOptions.only, | ||
871 | name: element.id, | ||
872 | format: sortableOptions.format | ||
873 | }, arguments[1] || {}); | ||
874 | |||
875 | var root = { | ||
876 | id: null, | ||
877 | parent: null, | ||
878 | children: [], | ||
879 | container: element, | ||
880 | position: 0 | ||
881 | } | ||
882 | |||
883 | return Sortable._tree(element, options, root); | ||
884 | }, | ||
885 | |||
886 | /* Construct a [i] index for a particular node */ | ||
887 | _constructIndex: function(node) { | ||
888 | var index = ''; | ||
889 | do { | ||
890 | if (node.id) index = '[' + node.position + ']' + index; | ||
891 | } while ((node = node.parent) != null); | ||
892 | return index; | ||
893 | }, | ||
894 | |||
895 | sequence: function(element) { | ||
896 | element = $(element); | ||
897 | var options = Object.extend(this.options(element), arguments[1] || {}); | ||
898 | |||
899 | return $(this.findElements(element, options) || []).map( function(item) { | ||
900 | return item.id.match(options.format) ? item.id.match(options.format)[1] : ''; | ||
901 | }); | ||
902 | }, | ||
903 | |||
904 | setSequence: function(element, new_sequence) { | ||
905 | element = $(element); | ||
906 | var options = Object.extend(this.options(element), arguments[2] || {}); | ||
907 | |||
908 | var nodeMap = {}; | ||
909 | this.findElements(element, options).each( function(n) { | ||
910 | if (n.id.match(options.format)) | ||
911 | nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode]; | ||
912 | n.parentNode.removeChild(n); | ||
913 | }); | ||
914 | |||
915 | new_sequence.each(function(ident) { | ||
916 | var n = nodeMap[ident]; | ||
917 | if (n) { | ||
918 | n[1].appendChild(n[0]); | ||
919 | delete nodeMap[ident]; | ||
920 | } | ||
921 | }); | ||
922 | }, | ||
923 | |||
924 | serialize: function(element) { | ||
925 | element = $(element); | ||
926 | var options = Object.extend(Sortable.options(element), arguments[1] || {}); | ||
927 | var name = encodeURIComponent( | ||
928 | (arguments[1] && arguments[1].name) ? arguments[1].name : element.id); | ||
929 | |||
930 | if (options.tree) { | ||
931 | return Sortable.tree(element, arguments[1]).children.map( function (item) { | ||
932 | return [name + Sortable._constructIndex(item) + "[id]=" + | ||
933 | encodeURIComponent(item.id)].concat(item.children.map(arguments.callee)); | ||
934 | }).flatten().join('&'); | ||
935 | } else { | ||
936 | return Sortable.sequence(element, arguments[1]).map( function(item) { | ||
937 | return name + "[]=" + encodeURIComponent(item); | ||
938 | }).join('&'); | ||
939 | } | ||
940 | } | ||
941 | } | ||
942 | |||
943 | // Returns true if child is contained within element | ||
944 | Element.isParent = function(child, element) { | ||
945 | if (!child.parentNode || child == element) return false; | ||
946 | if (child.parentNode == element) return true; | ||
947 | return Element.isParent(child.parentNode, element); | ||
948 | } | ||
949 | |||
950 | Element.findChildren = function(element, only, recursive, tagName) { | ||
951 | if(!element.hasChildNodes()) return null; | ||
952 | tagName = tagName.toUpperCase(); | ||
953 | if(only) only = [only].flatten(); | ||
954 | var elements = []; | ||
955 | $A(element.childNodes).each( function(e) { | ||
956 | if(e.tagName && e.tagName.toUpperCase()==tagName && | ||
957 | (!only || (Element.classNames(e).detect(function(v) { return only.include(v) })))) | ||
958 | elements.push(e); | ||
959 | if(recursive) { | ||
960 | var grandchildren = Element.findChildren(e, only, recursive, tagName); | ||
961 | if(grandchildren) elements.push(grandchildren); | ||
962 | } | ||
963 | }); | ||
964 | |||
965 | return (elements.length>0 ? elements.flatten() : []); | ||
966 | } | ||
967 | |||
968 | Element.offsetSize = function (element, type) { | ||
969 | return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')]; | ||
970 | } | ||
diff --git a/docroot/lib/scriptaculous/effects.js b/docroot/lib/scriptaculous/effects.js new file mode 100755 index 0000000..70d0752 --- /dev/null +++ b/docroot/lib/scriptaculous/effects.js | |||
@@ -0,0 +1,1094 @@ | |||
1 | // script.aculo.us effects.js v1.7.1_beta3, Fri May 25 17:19:41 +0200 2007 | ||
2 | |||
3 | // Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) | ||
4 | // Contributors: | ||
5 | // Justin Palmer (http://encytemedia.com/) | ||
6 | // Mark Pilgrim (http://diveintomark.org/) | ||
7 | // Martin Bialasinki | ||
8 | // | ||
9 | // script.aculo.us is freely distributable under the terms of an MIT-style license. | ||
10 | // For details, see the script.aculo.us web site: http://script.aculo.us/ | ||
11 | |||
12 | // converts rgb() and #xxx to #xxxxxx format, | ||
13 | // returns self (or first argument) if not convertable | ||
14 | String.prototype.parseColor = function() { | ||
15 | var color = '#'; | ||
16 | if(this.slice(0,4) == 'rgb(') { | ||
17 | var cols = this.slice(4,this.length-1).split(','); | ||
18 | var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3); | ||
19 | } else { | ||
20 | if(this.slice(0,1) == '#') { | ||
21 | if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase(); | ||
22 | if(this.length==7) color = this.toLowerCase(); | ||
23 | } | ||
24 | } | ||
25 | return(color.length==7 ? color : (arguments[0] || this)); | ||
26 | } | ||
27 | |||
28 | /*--------------------------------------------------------------------------*/ | ||
29 | |||
30 | Element.collectTextNodes = function(element) { | ||
31 | return $A($(element).childNodes).collect( function(node) { | ||
32 | return (node.nodeType==3 ? node.nodeValue : | ||
33 | (node.hasChildNodes() ? Element.collectTextNodes(node) : '')); | ||
34 | }).flatten().join(''); | ||
35 | } | ||
36 | |||
37 | Element.collectTextNodesIgnoreClass = function(element, className) { | ||
38 | return $A($(element).childNodes).collect( function(node) { | ||
39 | return (node.nodeType==3 ? node.nodeValue : | ||
40 | ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? | ||
41 | Element.collectTextNodesIgnoreClass(node, className) : '')); | ||
42 | }).flatten().join(''); | ||
43 | } | ||
44 | |||
45 | Element.setContentZoom = function(element, percent) { | ||
46 | element = $(element); | ||
47 | element.setStyle({fontSize: (percent/100) + 'em'}); | ||
48 | if(Prototype.Browser.WebKit) window.scrollBy(0,0); | ||
49 | return element; | ||
50 | } | ||
51 | |||
52 | Element.getInlineOpacity = function(element){ | ||
53 | return $(element).style.opacity || ''; | ||
54 | } | ||
55 | |||
56 | Element.forceRerendering = function(element) { | ||
57 | try { | ||
58 | element = $(element); | ||
59 | var n = document.createTextNode(' '); | ||
60 | element.appendChild(n); | ||
61 | element.removeChild(n); | ||
62 | } catch(e) { } | ||
63 | }; | ||
64 | |||
65 | /*--------------------------------------------------------------------------*/ | ||
66 | |||
67 | Array.prototype.call = function() { | ||
68 | var args = arguments; | ||
69 | this.each(function(f){ f.apply(this, args) }); | ||
70 | } | ||
71 | |||
72 | /*--------------------------------------------------------------------------*/ | ||
73 | |||
74 | var Effect = { | ||
75 | _elementDoesNotExistError: { | ||
76 | name: 'ElementDoesNotExistError', | ||
77 | message: 'The specified DOM element does not exist, but is required for this effect to operate' | ||
78 | }, | ||
79 | tagifyText: function(element) { | ||
80 | if(typeof Builder == 'undefined') | ||
81 | throw("Effect.tagifyText requires including script.aculo.us' builder.js library"); | ||
82 | |||
83 | var tagifyStyle = 'position:relative'; | ||
84 | if(Prototype.Browser.IE) tagifyStyle += ';zoom:1'; | ||
85 | |||
86 | element = $(element); | ||
87 | $A(element.childNodes).each( function(child) { | ||
88 | if(child.nodeType==3) { | ||
89 | child.nodeValue.toArray().each( function(character) { | ||
90 | element.insertBefore( | ||
91 | Builder.node('span',{style: tagifyStyle}, | ||
92 | character == ' ' ? String.fromCharCode(160) : character), | ||
93 | child); | ||
94 | }); | ||
95 | Element.remove(child); | ||
96 | } | ||
97 | }); | ||
98 | }, | ||
99 | multiple: function(element, effect) { | ||
100 | var elements; | ||
101 | if(((typeof element == 'object') || | ||
102 | (typeof element == 'function')) && | ||
103 | (element.length)) | ||
104 | elements = element; | ||
105 | else | ||
106 | elements = $(element).childNodes; | ||
107 | |||
108 | var options = Object.extend({ | ||
109 | speed: 0.1, | ||
110 | delay: 0.0 | ||
111 | }, arguments[2] || {}); | ||
112 | var masterDelay = options.delay; | ||
113 | |||
114 | $A(elements).each( function(element, index) { | ||
115 | new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay })); | ||
116 | }); | ||
117 | }, | ||
118 | PAIRS: { | ||
119 | 'slide': ['SlideDown','SlideUp'], | ||
120 | 'blind': ['BlindDown','BlindUp'], | ||
121 | 'appear': ['Appear','Fade'] | ||
122 | }, | ||
123 | toggle: function(element, effect) { | ||
124 | element = $(element); | ||
125 | effect = (effect || 'appear').toLowerCase(); | ||
126 | var options = Object.extend({ | ||
127 | queue: { position:'end', scope:(element.id || 'global'), limit: 1 } | ||
128 | }, arguments[2] || {}); | ||
129 | Effect[element.visible() ? | ||
130 | Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options); | ||
131 | } | ||
132 | }; | ||
133 | |||
134 | var Effect2 = Effect; // deprecated | ||
135 | |||
136 | /* ------------- transitions ------------- */ | ||
137 | |||
138 | Effect.Transitions = { | ||
139 | linear: Prototype.K, | ||
140 | sinoidal: function(pos) { | ||
141 | return (-Math.cos(pos*Math.PI)/2) + 0.5; | ||
142 | }, | ||
143 | reverse: function(pos) { | ||
144 | return 1-pos; | ||
145 | }, | ||
146 | flicker: function(pos) { | ||
147 | var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4; | ||
148 | return (pos > 1 ? 1 : pos); | ||
149 | }, | ||
150 | wobble: function(pos) { | ||
151 | return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5; | ||
152 | }, | ||
153 | pulse: function(pos, pulses) { | ||
154 | pulses = pulses || 5; | ||
155 | return ( | ||
156 | Math.round((pos % (1/pulses)) * pulses) == 0 ? | ||
157 | ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) : | ||
158 | 1 - ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) | ||
159 | ); | ||
160 | }, | ||
161 | none: function(pos) { | ||
162 | return 0; | ||
163 | }, | ||
164 | full: function(pos) { | ||
165 | return 1; | ||
166 | } | ||
167 | }; | ||
168 | |||
169 | /* ------------- core effects ------------- */ | ||
170 | |||
171 | Effect.ScopedQueue = Class.create(); | ||
172 | Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), { | ||
173 | initialize: function() { | ||
174 | this.effects = []; | ||
175 | this.interval = null; | ||
176 | }, | ||
177 | _each: function(iterator) { | ||
178 | this.effects._each(iterator); | ||
179 | }, | ||
180 | add: function(effect) { | ||
181 | var timestamp = new Date().getTime(); | ||
182 | |||
183 | var position = (typeof effect.options.queue == 'string') ? | ||
184 | effect.options.queue : effect.options.queue.position; | ||
185 | |||
186 | switch(position) { | ||
187 | case 'front': | ||
188 | // move unstarted effects after this effect | ||
189 | this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) { | ||
190 | e.startOn += effect.finishOn; | ||
191 | e.finishOn += effect.finishOn; | ||
192 | }); | ||
193 | break; | ||
194 | case 'with-last': | ||
195 | timestamp = this.effects.pluck('startOn').max() || timestamp; | ||
196 | break; | ||
197 | case 'end': | ||
198 | // start effect after last queued effect has finished | ||
199 | timestamp = this.effects.pluck('finishOn').max() || timestamp; | ||
200 | break; | ||
201 | } | ||
202 | |||
203 | effect.startOn += timestamp; | ||
204 | effect.finishOn += timestamp; | ||
205 | |||
206 | if(!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit)) | ||
207 | this.effects.push(effect); | ||
208 | |||
209 | if(!this.interval) | ||
210 | this.interval = setInterval(this.loop.bind(this), 15); | ||
211 | }, | ||
212 | remove: function(effect) { | ||
213 | this.effects = this.effects.reject(function(e) { return e==effect }); | ||
214 | if(this.effects.length == 0) { | ||
215 | clearInterval(this.interval); | ||
216 | this.interval = null; | ||
217 | } | ||
218 | }, | ||
219 | loop: function() { | ||
220 | var timePos = new Date().getTime(); | ||
221 | for(var i=0, len=this.effects.length;i<len;i++) | ||
222 | this.effects[i] && this.effects[i].loop(timePos); | ||
223 | } | ||
224 | }); | ||
225 | |||
226 | Effect.Queues = { | ||
227 | instances: $H(), | ||
228 | get: function(queueName) { | ||
229 | if(typeof queueName != 'string') return queueName; | ||
230 | |||
231 | if(!this.instances[queueName]) | ||
232 | this.instances[queueName] = new Effect.ScopedQueue(); | ||
233 | |||
234 | return this.instances[queueName]; | ||
235 | } | ||
236 | } | ||
237 | Effect.Queue = Effect.Queues.get('global'); | ||
238 | |||
239 | Effect.DefaultOptions = { | ||
240 | transition: Effect.Transitions.sinoidal, | ||
241 | duration: 1.0, // seconds | ||
242 | fps: 100, // 100= assume 66fps max. | ||
243 | sync: false, // true for combining | ||
244 | from: 0.0, | ||
245 | to: 1.0, | ||
246 | delay: 0.0, | ||
247 | queue: 'parallel' | ||
248 | } | ||
249 | |||
250 | Effect.Base = function() {}; | ||
251 | Effect.Base.prototype = { | ||
252 | position: null, | ||
253 | start: function(options) { | ||
254 | function codeForEvent(options,eventName){ | ||
255 | return ( | ||
256 | (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') + | ||
257 | (options[eventName] ? 'this.options.'+eventName+'(this);' : '') | ||
258 | ); | ||
259 | } | ||
260 | if(options.transition === false) options.transition = Effect.Transitions.linear; | ||
261 | this.options = Object.extend(Object.extend({},Effect.DefaultOptions), options || {}); | ||
262 | this.currentFrame = 0; | ||
263 | this.state = 'idle'; | ||
264 | this.startOn = this.options.delay*1000; | ||
265 | this.finishOn = this.startOn+(this.options.duration*1000); | ||
266 | this.fromToDelta = this.options.to-this.options.from; | ||
267 | this.totalTime = this.finishOn-this.startOn; | ||
268 | this.totalFrames = this.options.fps*this.options.duration; | ||
269 | |||
270 | eval('this.render = function(pos){ '+ | ||
271 | 'if(this.state=="idle"){this.state="running";'+ | ||
272 | codeForEvent(options,'beforeSetup')+ | ||
273 | (this.setup ? 'this.setup();':'')+ | ||
274 | codeForEvent(options,'afterSetup')+ | ||
275 | '};if(this.state=="running"){'+ | ||
276 | 'pos=this.options.transition(pos)*'+this.fromToDelta+'+'+this.options.from+';'+ | ||
277 | 'this.position=pos;'+ | ||
278 | codeForEvent(options,'beforeUpdate')+ | ||
279 | (this.update ? 'this.update(pos);':'')+ | ||
280 | codeForEvent(options,'afterUpdate')+ | ||
281 | '}}'); | ||
282 | |||
283 | this.event('beforeStart'); | ||
284 | if(!this.options.sync) | ||
285 | Effect.Queues.get(typeof this.options.queue == 'string' ? | ||
286 | 'global' : this.options.queue.scope).add(this); | ||
287 | }, | ||
288 | loop: function(timePos) { | ||
289 | if(timePos >= this.startOn) { | ||
290 | if(timePos >= this.finishOn) { | ||
291 | this.render(1.0); | ||
292 | this.cancel(); | ||
293 | this.event('beforeFinish'); | ||
294 | if(this.finish) this.finish(); | ||
295 | this.event('afterFinish'); | ||
296 | return; | ||
297 | } | ||
298 | var pos = (timePos - this.startOn) / this.totalTime, | ||
299 | frame = Math.round(pos * this.totalFrames); | ||
300 | if(frame > this.currentFrame) { | ||
301 | this.render(pos); | ||
302 | this.currentFrame = frame; | ||
303 | } | ||
304 | } | ||
305 | }, | ||
306 | cancel: function() { | ||
307 | if(!this.options.sync) | ||
308 | Effect.Queues.get(typeof this.options.queue == 'string' ? | ||
309 | 'global' : this.options.queue.scope).remove(this); | ||
310 | this.state = 'finished'; | ||
311 | }, | ||
312 | event: function(eventName) { | ||
313 | if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this); | ||
314 | if(this.options[eventName]) this.options[eventName](this); | ||
315 | }, | ||
316 | inspect: function() { | ||
317 | var data = $H(); | ||
318 | for(property in this) | ||
319 | if(typeof this[property] != 'function') data[property] = this[property]; | ||
320 | return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>'; | ||
321 | } | ||
322 | } | ||
323 | |||
324 | Effect.Parallel = Class.create(); | ||
325 | Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), { | ||
326 | initialize: function(effects) { | ||
327 | this.effects = effects || []; | ||
328 | this.start(arguments[1]); | ||
329 | }, | ||
330 | update: function(position) { | ||
331 | this.effects.invoke('render', position); | ||
332 | }, | ||
333 | finish: function(position) { | ||
334 | this.effects.each( function(effect) { | ||
335 | effect.render(1.0); | ||
336 | effect.cancel(); | ||
337 | effect.event('beforeFinish'); | ||
338 | if(effect.finish) effect.finish(position); | ||
339 | effect.event('afterFinish'); | ||
340 | }); | ||
341 | } | ||
342 | }); | ||
343 | |||
344 | Effect.Event = Class.create(); | ||
345 | Object.extend(Object.extend(Effect.Event.prototype, Effect.Base.prototype), { | ||
346 | initialize: function() { | ||
347 | var options = Object.extend({ | ||
348 | duration: 0 | ||
349 | }, arguments[0] || {}); | ||
350 | this.start(options); | ||
351 | }, | ||
352 | update: Prototype.emptyFunction | ||
353 | }); | ||
354 | |||
355 | Effect.Opacity = Class.create(); | ||
356 | Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), { | ||
357 | initialize: function(element) { | ||
358 | this.element = $(element); | ||
359 | if(!this.element) throw(Effect._elementDoesNotExistError); | ||
360 | // make this work on IE on elements without 'layout' | ||
361 | if(Prototype.Browser.IE && (!this.element.currentStyle.hasLayout)) | ||
362 | this.element.setStyle({zoom: 1}); | ||
363 | var options = Object.extend({ | ||
364 | from: this.element.getOpacity() || 0.0, | ||
365 | to: 1.0 | ||
366 | }, arguments[1] || {}); | ||
367 | this.start(options); | ||
368 | }, | ||
369 | update: function(position) { | ||
370 | this.element.setOpacity(position); | ||
371 | } | ||
372 | }); | ||
373 | |||
374 | Effect.Move = Class.create(); | ||
375 | Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), { | ||
376 | initialize: function(element) { | ||
377 | this.element = $(element); | ||
378 | if(!this.element) throw(Effect._elementDoesNotExistError); | ||
379 | var options = Object.extend({ | ||
380 | x: 0, | ||
381 | y: 0, | ||
382 | mode: 'relative' | ||
383 | }, arguments[1] || {}); | ||
384 | this.start(options); | ||
385 | }, | ||
386 | setup: function() { | ||
387 | // Bug in Opera: Opera returns the "real" position of a static element or | ||
388 | // relative element that does not have top/left explicitly set. | ||
389 | // ==> Always set top and left for position relative elements in your stylesheets | ||
390 | // (to 0 if you do not need them) | ||
391 | this.element.makePositioned(); | ||
392 | this.originalLeft = parseFloat(this.element.getStyle('left') || '0'); | ||
393 | this.originalTop = parseFloat(this.element.getStyle('top') || '0'); | ||
394 | if(this.options.mode == 'absolute') { | ||
395 | // absolute movement, so we need to calc deltaX and deltaY | ||
396 | this.options.x = this.options.x - this.originalLeft; | ||
397 | this.options.y = this.options.y - this.originalTop; | ||
398 | } | ||
399 | }, | ||
400 | update: function(position) { | ||
401 | this.element.setStyle({ | ||
402 | left: Math.round(this.options.x * position + this.originalLeft) + 'px', | ||
403 | top: Math.round(this.options.y * position + this.originalTop) + 'px' | ||
404 | }); | ||
405 | } | ||
406 | }); | ||
407 | |||
408 | // for backwards compatibility | ||
409 | Effect.MoveBy = function(element, toTop, toLeft) { | ||
410 | return new Effect.Move(element, | ||
411 | Object.extend({ x: toLeft, y: toTop }, arguments[3] || {})); | ||
412 | }; | ||
413 | |||
414 | Effect.Scale = Class.create(); | ||
415 | Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), { | ||
416 | initialize: function(element, percent) { | ||
417 | this.element = $(element); | ||
418 | if(!this.element) throw(Effect._elementDoesNotExistError); | ||
419 | var options = Object.extend({ | ||
420 | scaleX: true, | ||
421 | scaleY: true, | ||
422 | scaleContent: true, | ||
423 | scaleFromCenter: false, | ||
424 | scaleMode: 'box', // 'box' or 'contents' or {} with provided values | ||
425 | scaleFrom: 100.0, | ||
426 | scaleTo: percent | ||
427 | }, arguments[2] || {}); | ||
428 | this.start(options); | ||
429 | }, | ||
430 | setup: function() { | ||
431 | this.restoreAfterFinish = this.options.restoreAfterFinish || false; | ||
432 | this.elementPositioning = this.element.getStyle('position'); | ||
433 | |||
434 | this.originalStyle = {}; | ||
435 | ['top','left','width','height','fontSize'].each( function(k) { | ||
436 | this.originalStyle[k] = this.element.style[k]; | ||
437 | }.bind(this)); | ||
438 | |||
439 | this.originalTop = this.element.offsetTop; | ||
440 | this.originalLeft = this.element.offsetLeft; | ||
441 | |||
442 | var fontSize = this.element.getStyle('font-size') || '100%'; | ||
443 | ['em','px','%','pt'].each( function(fontSizeType) { | ||
444 | if(fontSize.indexOf(fontSizeType)>0) { | ||
445 | this.fontSize = parseFloat(fontSize); | ||
446 | this.fontSizeType = fontSizeType; | ||
447 | } | ||
448 | }.bind(this)); | ||
449 | |||
450 | this.factor = (this.options.scaleTo - this.options.scaleFrom)/100; | ||
451 | |||
452 | this.dims = null; | ||
453 | if(this.options.scaleMode=='box') | ||
454 | this.dims = [this.element.offsetHeight, this.element.offsetWidth]; | ||
455 | if(/^content/.test(this.options.scaleMode)) | ||
456 | this.dims = [this.element.scrollHeight, this.element.scrollWidth]; | ||
457 | if(!this.dims) | ||
458 | this.dims = [this.options.scaleMode.originalHeight, | ||
459 | this.options.scaleMode.originalWidth]; | ||
460 | }, | ||
461 | update: function(position) { | ||
462 | var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position); | ||
463 | if(this.options.scaleContent && this.fontSize) | ||
464 | this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType }); | ||
465 | this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale); | ||
466 | }, | ||
467 | finish: function(position) { | ||
468 | if(this.restoreAfterFinish) this.element.setStyle(this.originalStyle); | ||
469 | }, | ||
470 | setDimensions: function(height, width) { | ||
471 | var d = {}; | ||
472 | if(this.options.scaleX) d.width = Math.round(width) + 'px'; | ||
473 | if(this.options.scaleY) d.height = Math.round(height) + 'px'; | ||
474 | if(this.options.scaleFromCenter) { | ||
475 | var topd = (height - this.dims[0])/2; | ||
476 | var leftd = (width - this.dims[1])/2; | ||
477 | if(this.elementPositioning == 'absolute') { | ||
478 | if(this.options.scaleY) d.top = this.originalTop-topd + 'px'; | ||
479 | if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px'; | ||
480 | } else { | ||
481 | if(this.options.scaleY) d.top = -topd + 'px'; | ||
482 | if(this.options.scaleX) d.left = -leftd + 'px'; | ||
483 | } | ||
484 | } | ||
485 | this.element.setStyle(d); | ||
486 | } | ||
487 | }); | ||
488 | |||
489 | Effect.Highlight = Class.create(); | ||
490 | Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), { | ||
491 | initialize: function(element) { | ||
492 | this.element = $(element); | ||
493 | if(!this.element) throw(Effect._elementDoesNotExistError); | ||
494 | var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {}); | ||
495 | this.start(options); | ||
496 | }, | ||
497 | setup: function() { | ||
498 | // Prevent executing on elements not in the layout flow | ||
499 | if(this.element.getStyle('display')=='none') { this.cancel(); return; } | ||
500 | // Disable background image during the effect | ||
501 | this.oldStyle = {}; | ||
502 | if (!this.options.keepBackgroundImage) { | ||
503 | this.oldStyle.backgroundImage = this.element.getStyle('background-image'); | ||
504 | this.element.setStyle({backgroundImage: 'none'}); | ||
505 | } | ||
506 | if(!this.options.endcolor) | ||
507 | this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff'); | ||
508 | if(!this.options.restorecolor) | ||
509 | this.options.restorecolor = this.element.getStyle('background-color'); | ||
510 | // init color calculations | ||
511 | this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this)); | ||
512 | this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this)); | ||
513 | }, | ||
514 | update: function(position) { | ||
515 | this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){ | ||
516 | return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) }); | ||
517 | }, | ||
518 | finish: function() { | ||
519 | this.element.setStyle(Object.extend(this.oldStyle, { | ||
520 | backgroundColor: this.options.restorecolor | ||
521 | })); | ||
522 | } | ||
523 | }); | ||
524 | |||
525 | Effect.ScrollTo = Class.create(); | ||
526 | Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), { | ||
527 | initialize: function(element) { | ||
528 | this.element = $(element); | ||
529 | this.start(arguments[1] || {}); | ||
530 | }, | ||
531 | setup: function() { | ||
532 | Position.prepare(); | ||
533 | var offsets = Position.cumulativeOffset(this.element); | ||
534 | if(this.options.offset) offsets[1] += this.options.offset; | ||
535 | var max = window.innerHeight ? | ||
536 | window.height - window.innerHeight : | ||
537 | document.body.scrollHeight - | ||
538 | (document.documentElement.clientHeight ? | ||
539 | document.documentElement.clientHeight : document.body.clientHeight); | ||
540 | this.scrollStart = Position.deltaY; | ||
541 | this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart; | ||
542 | }, | ||
543 | update: function(position) { | ||
544 | Position.prepare(); | ||
545 | window.scrollTo(Position.deltaX, | ||
546 | this.scrollStart + (position*this.delta)); | ||
547 | } | ||
548 | }); | ||
549 | |||
550 | /* ------------- combination effects ------------- */ | ||
551 | |||
552 | Effect.Fade = function(element) { | ||
553 | element = $(element); | ||
554 | var oldOpacity = element.getInlineOpacity(); | ||
555 | var options = Object.extend({ | ||
556 | from: element.getOpacity() || 1.0, | ||
557 | to: 0.0, | ||
558 | afterFinishInternal: function(effect) { | ||
559 | if(effect.options.to!=0) return; | ||
560 | effect.element.hide().setStyle({opacity: oldOpacity}); | ||
561 | }}, arguments[1] || {}); | ||
562 | return new Effect.Opacity(element,options); | ||
563 | } | ||
564 | |||
565 | Effect.Appear = function(element) { | ||
566 | element = $(element); | ||
567 | var options = Object.extend({ | ||
568 | from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0), | ||
569 | to: 1.0, | ||
570 | // force Safari to render floated elements properly | ||
571 | afterFinishInternal: function(effect) { | ||
572 | effect.element.forceRerendering(); | ||
573 | }, | ||
574 | beforeSetup: function(effect) { | ||
575 | effect.element.setOpacity(effect.options.from).show(); | ||
576 | }}, arguments[1] || {}); | ||
577 | return new Effect.Opacity(element,options); | ||
578 | } | ||
579 | |||
580 | Effect.Puff = function(element) { | ||
581 | element = $(element); | ||
582 | var oldStyle = { | ||
583 | opacity: element.getInlineOpacity(), | ||
584 | position: element.getStyle('position'), | ||
585 | top: element.style.top, | ||
586 | left: element.style.left, | ||
587 | width: element.style.width, | ||
588 | height: element.style.height | ||
589 | }; | ||
590 | return new Effect.Parallel( | ||
591 | [ new Effect.Scale(element, 200, | ||
592 | { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), | ||
593 | new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], | ||
594 | Object.extend({ duration: 1.0, | ||
595 | beforeSetupInternal: function(effect) { | ||
596 | Position.absolutize(effect.effects[0].element) | ||
597 | }, | ||
598 | afterFinishInternal: function(effect) { | ||
599 | effect.effects[0].element.hide().setStyle(oldStyle); } | ||
600 | }, arguments[1] || {}) | ||
601 | ); | ||
602 | } | ||
603 | |||
604 | Effect.BlindUp = function(element) { | ||
605 | element = $(element); | ||
606 | element.makeClipping(); | ||
607 | return new Effect.Scale(element, 0, | ||
608 | Object.extend({ scaleContent: false, | ||
609 | scaleX: false, | ||
610 | restoreAfterFinish: true, | ||
611 | afterFinishInternal: function(effect) { | ||
612 | effect.element.hide().undoClipping(); | ||
613 | } | ||
614 | }, arguments[1] || {}) | ||
615 | ); | ||
616 | } | ||
617 | |||
618 | Effect.BlindDown = function(element) { | ||
619 | element = $(element); | ||
620 | var elementDimensions = element.getDimensions(); | ||
621 | return new Effect.Scale(element, 100, Object.extend({ | ||
622 | scaleContent: false, | ||
623 | scaleX: false, | ||
624 | scaleFrom: 0, | ||
625 | scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, | ||
626 | restoreAfterFinish: true, | ||
627 | afterSetup: function(effect) { | ||
628 | effect.element.makeClipping().setStyle({height: '0px'}).show(); | ||
629 | }, | ||
630 | afterFinishInternal: function(effect) { | ||
631 | effect.element.undoClipping(); | ||
632 | } | ||
633 | }, arguments[1] || {})); | ||
634 | } | ||
635 | |||
636 | Effect.SwitchOff = function(element) { | ||
637 | element = $(element); | ||
638 | var oldOpacity = element.getInlineOpacity(); | ||
639 | return new Effect.Appear(element, Object.extend({ | ||
640 | duration: 0.4, | ||
641 | from: 0, | ||
642 | transition: Effect.Transitions.flicker, | ||
643 | afterFinishInternal: function(effect) { | ||
644 | new Effect.Scale(effect.element, 1, { | ||
645 | duration: 0.3, scaleFromCenter: true, | ||
646 | scaleX: false, scaleContent: false, restoreAfterFinish: true, | ||
647 | beforeSetup: function(effect) { | ||
648 | effect.element.makePositioned().makeClipping(); | ||
649 | }, | ||
650 | afterFinishInternal: function(effect) { | ||
651 | effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity}); | ||
652 | } | ||
653 | }) | ||
654 | } | ||
655 | }, arguments[1] || {})); | ||
656 | } | ||
657 | |||
658 | Effect.DropOut = function(element) { | ||
659 | element = $(element); | ||
660 | var oldStyle = { | ||
661 | top: element.getStyle('top'), | ||
662 | left: element.getStyle('left'), | ||
663 | opacity: element.getInlineOpacity() }; | ||
664 | return new Effect.Parallel( | ||
665 | [ new Effect.Move(element, {x: 0, y: 100, sync: true }), | ||
666 | new Effect.Opacity(element, { sync: true, to: 0.0 }) ], | ||
667 | Object.extend( | ||
668 | { duration: 0.5, | ||
669 | beforeSetup: function(effect) { | ||
670 | effect.effects[0].element.makePositioned(); | ||
671 | }, | ||
672 | afterFinishInternal: function(effect) { | ||
673 | effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle); | ||
674 | } | ||
675 | }, arguments[1] || {})); | ||
676 | } | ||
677 | |||
678 | Effect.Shake = function(element) { | ||
679 | element = $(element); | ||
680 | var oldStyle = { | ||
681 | top: element.getStyle('top'), | ||
682 | left: element.getStyle('left') }; | ||
683 | return new Effect.Move(element, | ||
684 | { x: 20, y: 0, duration: 0.05, afterFinishInternal: function(effect) { | ||
685 | new Effect.Move(effect.element, | ||
686 | { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { | ||
687 | new Effect.Move(effect.element, | ||
688 | { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { | ||
689 | new Effect.Move(effect.element, | ||
690 | { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { | ||
691 | new Effect.Move(effect.element, | ||
692 | { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { | ||
693 | new Effect.Move(effect.element, | ||
694 | { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) { | ||
695 | effect.element.undoPositioned().setStyle(oldStyle); | ||
696 | }}) }}) }}) }}) }}) }}); | ||
697 | } | ||
698 | |||
699 | Effect.SlideDown = function(element) { | ||
700 | element = $(element).cleanWhitespace(); | ||
701 | // SlideDown need to have the content of the element wrapped in a container element with fixed height! | ||
702 | var oldInnerBottom = element.down().getStyle('bottom'); | ||
703 | var elementDimensions = element.getDimensions(); | ||
704 | return new Effect.Scale(element, 100, Object.extend({ | ||
705 | scaleContent: false, | ||
706 | scaleX: false, | ||
707 | scaleFrom: window.opera ? 0 : 1, | ||
708 | scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, | ||
709 | restoreAfterFinish: true, | ||
710 | afterSetup: function(effect) { | ||
711 | effect.element.makePositioned(); | ||
712 | effect.element.down().makePositioned(); | ||
713 | if(window.opera) effect.element.setStyle({top: ''}); | ||
714 | effect.element.makeClipping().setStyle({height: '0px'}).show(); | ||
715 | }, | ||
716 | afterUpdateInternal: function(effect) { | ||
717 | effect.element.down().setStyle({bottom: | ||
718 | (effect.dims[0] - effect.element.clientHeight) + 'px' }); | ||
719 | }, | ||
720 | afterFinishInternal: function(effect) { | ||
721 | effect.element.undoClipping().undoPositioned(); | ||
722 | effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); } | ||
723 | }, arguments[1] || {}) | ||
724 | ); | ||
725 | } | ||
726 | |||
727 | Effect.SlideUp = function(element) { | ||
728 | element = $(element).cleanWhitespace(); | ||
729 | var oldInnerBottom = element.down().getStyle('bottom'); | ||
730 | return new Effect.Scale(element, window.opera ? 0 : 1, | ||
731 | Object.extend({ scaleContent: false, | ||
732 | scaleX: false, | ||
733 | scaleMode: 'box', | ||
734 | scaleFrom: 100, | ||
735 | restoreAfterFinish: true, | ||
736 | beforeStartInternal: function(effect) { | ||
737 | effect.element.makePositioned(); | ||
738 | effect.element.down().makePositioned(); | ||
739 | if(window.opera) effect.element.setStyle({top: ''}); | ||
740 | effect.element.makeClipping().show(); | ||
741 | }, | ||
742 | afterUpdateInternal: function(effect) { | ||
743 | effect.element.down().setStyle({bottom: | ||
744 | (effect.dims[0] - effect.element.clientHeight) + 'px' }); | ||
745 | }, | ||
746 | afterFinishInternal: function(effect) { | ||
747 | effect.element.hide().undoClipping().undoPositioned().setStyle({bottom: oldInnerBottom}); | ||
748 | effect.element.down().undoPositioned(); | ||
749 | } | ||
750 | }, arguments[1] || {}) | ||
751 | ); | ||
752 | } | ||
753 | |||
754 | // Bug in opera makes the TD containing this element expand for a instance after finish | ||
755 | Effect.Squish = function(element) { | ||
756 | return new Effect.Scale(element, window.opera ? 1 : 0, { | ||
757 | restoreAfterFinish: true, | ||
758 | beforeSetup: function(effect) { | ||
759 | effect.element.makeClipping(); | ||
760 | }, | ||
761 | afterFinishInternal: function(effect) { | ||
762 | effect.element.hide().undoClipping(); | ||
763 | } | ||
764 | }); | ||
765 | } | ||
766 | |||
767 | Effect.Grow = function(element) { | ||
768 | element = $(element); | ||
769 | var options = Object.extend({ | ||
770 | direction: 'center', | ||
771 | moveTransition: Effect.Transitions.sinoidal, | ||
772 | scaleTransition: Effect.Transitions.sinoidal, | ||
773 | opacityTransition: Effect.Transitions.full | ||
774 | }, arguments[1] || {}); | ||
775 | var oldStyle = { | ||
776 | top: element.style.top, | ||
777 | left: element.style.left, | ||
778 | height: element.style.height, | ||
779 | width: element.style.width, | ||
780 | opacity: element.getInlineOpacity() }; | ||
781 | |||
782 | var dims = element.getDimensions(); | ||
783 | var initialMoveX, initialMoveY; | ||
784 | var moveX, moveY; | ||
785 | |||
786 | switch (options.direction) { | ||
787 | case 'top-left': | ||
788 | initialMoveX = initialMoveY = moveX = moveY = 0; | ||
789 | break; | ||
790 | case 'top-right': | ||
791 | initialMoveX = dims.width; | ||
792 | initialMoveY = moveY = 0; | ||
793 | moveX = -dims.width; | ||
794 | break; | ||
795 | case 'bottom-left': | ||
796 | initialMoveX = moveX = 0; | ||
797 | initialMoveY = dims.height; | ||
798 | moveY = -dims.height; | ||
799 | break; | ||
800 | case 'bottom-right': | ||
801 | initialMoveX = dims.width; | ||
802 | initialMoveY = dims.height; | ||
803 | moveX = -dims.width; | ||
804 | moveY = -dims.height; | ||
805 | break; | ||
806 | case 'center': | ||
807 | initialMoveX = dims.width / 2; | ||
808 | initialMoveY = dims.height / 2; | ||
809 | moveX = -dims.width / 2; | ||
810 | moveY = -dims.height / 2; | ||
811 | break; | ||
812 | } | ||
813 | |||
814 | return new Effect.Move(element, { | ||
815 | x: initialMoveX, | ||
816 | y: initialMoveY, | ||
817 | duration: 0.01, | ||
818 | beforeSetup: function(effect) { | ||
819 | effect.element.hide().makeClipping().makePositioned(); | ||
820 | }, | ||
821 | afterFinishInternal: function(effect) { | ||
822 | new Effect.Parallel( | ||
823 | [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }), | ||
824 | new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }), | ||
825 | new Effect.Scale(effect.element, 100, { | ||
826 | scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, | ||
827 | sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true}) | ||
828 | ], Object.extend({ | ||
829 | beforeSetup: function(effect) { | ||
830 | effect.effects[0].element.setStyle({height: '0px'}).show(); | ||
831 | }, | ||
832 | afterFinishInternal: function(effect) { | ||
833 | effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); | ||
834 | } | ||
835 | }, options) | ||
836 | ) | ||
837 | } | ||
838 | }); | ||
839 | } | ||
840 | |||
841 | Effect.Shrink = function(element) { | ||
842 | element = $(element); | ||
843 | var options = Object.extend({ | ||
844 | direction: 'center', | ||
845 | moveTransition: Effect.Transitions.sinoidal, | ||
846 | scaleTransition: Effect.Transitions.sinoidal, | ||
847 | opacityTransition: Effect.Transitions.none | ||
848 | }, arguments[1] || {}); | ||
849 | var oldStyle = { | ||
850 | top: element.style.top, | ||
851 | left: element.style.left, | ||
852 | height: element.style.height, | ||
853 | width: element.style.width, | ||
854 | opacity: element.getInlineOpacity() }; | ||
855 | |||
856 | var dims = element.getDimensions(); | ||
857 | var moveX, moveY; | ||
858 | |||
859 | switch (options.direction) { | ||
860 | case 'top-left': | ||
861 | moveX = moveY = 0; | ||
862 | break; | ||
863 | case 'top-right': | ||
864 | moveX = dims.width; | ||
865 | moveY = 0; | ||
866 | break; | ||
867 | case 'bottom-left': | ||
868 | moveX = 0; | ||
869 | moveY = dims.height; | ||
870 | break; | ||
871 | case 'bottom-right': | ||
872 | moveX = dims.width; | ||
873 | moveY = dims.height; | ||
874 | break; | ||
875 | case 'center': | ||
876 | moveX = dims.width / 2; | ||
877 | moveY = dims.height / 2; | ||
878 | break; | ||
879 | } | ||
880 | |||
881 | return new Effect.Parallel( | ||
882 | [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }), | ||
883 | new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}), | ||
884 | new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }) | ||
885 | ], Object.extend({ | ||
886 | beforeStartInternal: function(effect) { | ||
887 | effect.effects[0].element.makePositioned().makeClipping(); | ||
888 | }, | ||
889 | afterFinishInternal: function(effect) { | ||
890 | effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); } | ||
891 | }, options) | ||
892 | ); | ||
893 | } | ||
894 | |||
895 | Effect.Pulsate = function(element) { | ||
896 | element = $(element); | ||
897 | var options = arguments[1] || {}; | ||
898 | var oldOpacity = element.getInlineOpacity(); | ||
899 | var transition = options.transition || Effect.Transitions.sinoidal; | ||
900 | var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) }; | ||
901 | reverser.bind(transition); | ||
902 | return new Effect.Opacity(element, | ||
903 | Object.extend(Object.extend({ duration: 2.0, from: 0, | ||
904 | afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); } | ||
905 | }, options), {transition: reverser})); | ||
906 | } | ||
907 | |||
908 | Effect.Fold = function(element) { | ||
909 | element = $(element); | ||
910 | var oldStyle = { | ||
911 | top: element.style.top, | ||
912 | left: element.style.left, | ||
913 | width: element.style.width, | ||
914 | height: element.style.height }; | ||
915 | element.makeClipping(); | ||
916 | return new Effect.Scale(element, 5, Object.extend({ | ||
917 | scaleContent: false, | ||
918 | scaleX: false, | ||
919 | afterFinishInternal: function(effect) { | ||
920 | new Effect.Scale(element, 1, { | ||
921 | scaleContent: false, | ||
922 | scaleY: false, | ||
923 | afterFinishInternal: function(effect) { | ||
924 | effect.element.hide().undoClipping().setStyle(oldStyle); | ||
925 | } }); | ||
926 | }}, arguments[1] || {})); | ||
927 | }; | ||
928 | |||
929 | Effect.Morph = Class.create(); | ||
930 | Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), { | ||
931 | initialize: function(element) { | ||
932 | this.element = $(element); | ||
933 | if(!this.element) throw(Effect._elementDoesNotExistError); | ||
934 | var options = Object.extend({ | ||
935 | style: {} | ||
936 | }, arguments[1] || {}); | ||
937 | if (typeof options.style == 'string') { | ||
938 | if(options.style.indexOf(':') == -1) { | ||
939 | var cssText = '', selector = '.' + options.style; | ||
940 | $A(document.styleSheets).reverse().each(function(styleSheet) { | ||
941 | if (styleSheet.cssRules) cssRules = styleSheet.cssRules; | ||
942 | else if (styleSheet.rules) cssRules = styleSheet.rules; | ||
943 | $A(cssRules).reverse().each(function(rule) { | ||
944 | if (selector == rule.selectorText) { | ||
945 | cssText = rule.style.cssText; | ||
946 | throw $break; | ||
947 | } | ||
948 | }); | ||
949 | if (cssText) throw $break; | ||
950 | }); | ||
951 | this.style = cssText.parseStyle(); | ||
952 | options.afterFinishInternal = function(effect){ | ||
953 | effect.element.addClassName(effect.options.style); | ||
954 | effect.transforms.each(function(transform) { | ||
955 | if(transform.style != 'opacity') | ||
956 | effect.element.style[transform.style] = ''; | ||
957 | }); | ||
958 | } | ||
959 | } else this.style = options.style.parseStyle(); | ||
960 | } else this.style = $H(options.style) | ||
961 | this.start(options); | ||
962 | }, | ||
963 | setup: function(){ | ||
964 | function parseColor(color){ | ||
965 | if(!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff'; | ||
966 | color = color.parseColor(); | ||
967 | return $R(0,2).map(function(i){ | ||
968 | return parseInt( color.slice(i*2+1,i*2+3), 16 ) | ||
969 | }); | ||
970 | } | ||
971 | this.transforms = this.style.map(function(pair){ | ||
972 | var property = pair[0], value = pair[1], unit = null; | ||
973 | |||
974 | if(value.parseColor('#zzzzzz') != '#zzzzzz') { | ||
975 | value = value.parseColor(); | ||
976 | unit = 'color'; | ||
977 | } else if(property == 'opacity') { | ||
978 | value = parseFloat(value); | ||
979 | if(Prototype.Browser.IE && (!this.element.currentStyle.hasLayout)) | ||
980 | this.element.setStyle({zoom: 1}); | ||
981 | } else if(Element.CSS_LENGTH.test(value)) { | ||
982 | var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/); | ||
983 | value = parseFloat(components[1]); | ||
984 | unit = (components.length == 3) ? components[2] : null; | ||
985 | } | ||
986 | |||
987 | var originalValue = this.element.getStyle(property); | ||
988 | return { | ||
989 | style: property.camelize(), | ||
990 | originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0), | ||
991 | targetValue: unit=='color' ? parseColor(value) : value, | ||
992 | unit: unit | ||
993 | }; | ||
994 | }.bind(this)).reject(function(transform){ | ||
995 | return ( | ||
996 | (transform.originalValue == transform.targetValue) || | ||
997 | ( | ||
998 | transform.unit != 'color' && | ||
999 | (isNaN(transform.originalValue) || isNaN(transform.targetValue)) | ||
1000 | ) | ||
1001 | ) | ||
1002 | }); | ||
1003 | }, | ||
1004 | update: function(position) { | ||
1005 | var style = {}, transform, i = this.transforms.length; | ||
1006 | while(i--) | ||
1007 | style[(transform = this.transforms[i]).style] = | ||
1008 | transform.unit=='color' ? '#'+ | ||
1009 | (Math.round(transform.originalValue[0]+ | ||
1010 | (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() + | ||
1011 | (Math.round(transform.originalValue[1]+ | ||
1012 | (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() + | ||
1013 | (Math.round(transform.originalValue[2]+ | ||
1014 | (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() : | ||
1015 | transform.originalValue + Math.round( | ||
1016 | ((transform.targetValue - transform.originalValue) * position) * 1000)/1000 + transform.unit; | ||
1017 | this.element.setStyle(style, true); | ||
1018 | } | ||
1019 | }); | ||
1020 | |||
1021 | Effect.Transform = Class.create(); | ||
1022 | Object.extend(Effect.Transform.prototype, { | ||
1023 | initialize: function(tracks){ | ||
1024 | this.tracks = []; | ||
1025 | this.options = arguments[1] || {}; | ||
1026 | this.addTracks(tracks); | ||
1027 | }, | ||
1028 | addTracks: function(tracks){ | ||
1029 | tracks.each(function(track){ | ||
1030 | var data = $H(track).values().first(); | ||
1031 | this.tracks.push($H({ | ||
1032 | ids: $H(track).keys().first(), | ||
1033 | effect: Effect.Morph, | ||
1034 | options: { style: data } | ||
1035 | })); | ||
1036 | }.bind(this)); | ||
1037 | return this; | ||
1038 | }, | ||
1039 | play: function(){ | ||
1040 | return new Effect.Parallel( | ||
1041 | this.tracks.map(function(track){ | ||
1042 | var elements = [$(track.ids) || $$(track.ids)].flatten(); | ||
1043 | return elements.map(function(e){ return new track.effect(e, Object.extend({ sync:true }, track.options)) }); | ||
1044 | }).flatten(), | ||
1045 | this.options | ||
1046 | ); | ||
1047 | } | ||
1048 | }); | ||
1049 | |||
1050 | Element.CSS_PROPERTIES = $w( | ||
1051 | 'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' + | ||
1052 | 'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' + | ||
1053 | 'borderRightColor borderRightStyle borderRightWidth borderSpacing ' + | ||
1054 | 'borderTopColor borderTopStyle borderTopWidth bottom clip color ' + | ||
1055 | 'fontSize fontWeight height left letterSpacing lineHeight ' + | ||
1056 | 'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+ | ||
1057 | 'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' + | ||
1058 | 'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' + | ||
1059 | 'right textIndent top width wordSpacing zIndex'); | ||
1060 | |||
1061 | Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/; | ||
1062 | |||
1063 | String.prototype.parseStyle = function(){ | ||
1064 | var element = document.createElement('div'); | ||
1065 | element.innerHTML = '<div style="' + this + '"></div>'; | ||
1066 | var style = element.childNodes[0].style, styleRules = $H(); | ||
1067 | |||
1068 | Element.CSS_PROPERTIES.each(function(property){ | ||
1069 | if(style[property]) styleRules[property] = style[property]; | ||
1070 | }); | ||
1071 | if(Prototype.Browser.IE && this.indexOf('opacity') > -1) { | ||
1072 | styleRules.opacity = this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]; | ||
1073 | } | ||
1074 | return styleRules; | ||
1075 | }; | ||
1076 | |||
1077 | Element.morph = function(element, style) { | ||
1078 | new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || {})); | ||
1079 | return element; | ||
1080 | }; | ||
1081 | |||
1082 | ['getInlineOpacity','forceRerendering','setContentZoom', | ||
1083 | 'collectTextNodes','collectTextNodesIgnoreClass','morph'].each( | ||
1084 | function(f) { Element.Methods[f] = Element[f]; } | ||
1085 | ); | ||
1086 | |||
1087 | Element.Methods.visualEffect = function(element, effect, options) { | ||
1088 | s = effect.dasherize().camelize(); | ||
1089 | effect_class = s.charAt(0).toUpperCase() + s.substring(1); | ||
1090 | new Effect[effect_class](element, options); | ||
1091 | return $(element); | ||
1092 | }; | ||
1093 | |||
1094 | Element.addMethods(); \ No newline at end of file | ||
diff --git a/docroot/lib/scriptaculous/scriptaculous.js b/docroot/lib/scriptaculous/scriptaculous.js new file mode 100755 index 0000000..7c472a6 --- /dev/null +++ b/docroot/lib/scriptaculous/scriptaculous.js | |||
@@ -0,0 +1,58 @@ | |||
1 | // script.aculo.us scriptaculous.js v1.7.1_beta3, Fri May 25 17:19:41 +0200 2007 | ||
2 | |||
3 | // Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) | ||
4 | // | ||
5 | // Permission is hereby granted, free of charge, to any person obtaining | ||
6 | // a copy of this software and associated documentation files (the | ||
7 | // "Software"), to deal in the Software without restriction, including | ||
8 | // without limitation the rights to use, copy, modify, merge, publish, | ||
9 | // distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | // permit persons to whom the Software is furnished to do so, subject to | ||
11 | // the following conditions: | ||
12 | // | ||
13 | // The above copyright notice and this permission notice shall be | ||
14 | // included in all copies or substantial portions of the Software. | ||
15 | // | ||
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
17 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
18 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
19 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | ||
20 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
21 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
22 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
23 | // | ||
24 | // For details, see the script.aculo.us web site: http://script.aculo.us/ | ||
25 | |||
26 | var Scriptaculous = { | ||
27 | Version: '1.7.1_beta3', | ||
28 | require: function(libraryName) { | ||
29 | // inserting via DOM fails in Safari 2.0, so brute force approach | ||
30 | document.write('<script type="text/javascript" src="'+libraryName+'"></script>'); | ||
31 | }, | ||
32 | REQUIRED_PROTOTYPE: '1.5.1', | ||
33 | load: function() { | ||
34 | function convertVersionString(versionString){ | ||
35 | var r = versionString.split('.'); | ||
36 | return parseInt(r[0])*100000 + parseInt(r[1])*1000 + parseInt(r[2]); | ||
37 | } | ||
38 | |||
39 | if((typeof Prototype=='undefined') || | ||
40 | (typeof Element == 'undefined') || | ||
41 | (typeof Element.Methods=='undefined') || | ||
42 | (convertVersionString(Prototype.Version) < | ||
43 | convertVersionString(Scriptaculous.REQUIRED_PROTOTYPE))) | ||
44 | throw("script.aculo.us requires the Prototype JavaScript framework >= " + | ||
45 | Scriptaculous.REQUIRED_PROTOTYPE); | ||
46 | |||
47 | $A(document.getElementsByTagName("script")).findAll( function(s) { | ||
48 | return (s.src && s.src.match(/scriptaculous\.js(\?.*)?$/)) | ||
49 | }).each( function(s) { | ||
50 | var path = s.src.replace(/scriptaculous\.js(\?.*)?$/,''); | ||
51 | var includes = s.src.match(/\?.*load=([a-z,]*)/); | ||
52 | (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider,sound').split(',').each( | ||
53 | function(include) { Scriptaculous.require(path+include+'.js') }); | ||
54 | }); | ||
55 | } | ||
56 | } | ||
57 | |||
58 | Scriptaculous.load(); \ No newline at end of file | ||
diff --git a/docroot/lib/scriptaculous/slider.js b/docroot/lib/scriptaculous/slider.js new file mode 100755 index 0000000..c1a84eb --- /dev/null +++ b/docroot/lib/scriptaculous/slider.js | |||
@@ -0,0 +1,277 @@ | |||
1 | // script.aculo.us slider.js v1.7.1_beta3, Fri May 25 17:19:41 +0200 2007 | ||
2 | |||
3 | // Copyright (c) 2005-2007 Marty Haught, Thomas Fuchs | ||
4 | // | ||
5 | // script.aculo.us is freely distributable under the terms of an MIT-style license. | ||
6 | // For details, see the script.aculo.us web site: http://script.aculo.us/ | ||
7 | |||
8 | if(!Control) var Control = {}; | ||
9 | Control.Slider = Class.create(); | ||
10 | |||
11 | // options: | ||
12 | // axis: 'vertical', or 'horizontal' (default) | ||
13 | // | ||
14 | // callbacks: | ||
15 | // onChange(value) | ||
16 | // onSlide(value) | ||
17 | Control.Slider.prototype = { | ||
18 | initialize: function(handle, track, options) { | ||
19 | var slider = this; | ||
20 | |||
21 | if(handle instanceof Array) { | ||
22 | this.handles = handle.collect( function(e) { return $(e) }); | ||
23 | } else { | ||
24 | this.handles = [$(handle)]; | ||
25 | } | ||
26 | |||
27 | this.track = $(track); | ||
28 | this.options = options || {}; | ||
29 | |||
30 | this.axis = this.options.axis || 'horizontal'; | ||
31 | this.increment = this.options.increment || 1; | ||
32 | this.step = parseInt(this.options.step || '1'); | ||
33 | this.range = this.options.range || $R(0,1); | ||
34 | |||
35 | this.value = 0; // assure backwards compat | ||
36 | this.values = this.handles.map( function() { return 0 }); | ||
37 | this.spans = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : false; | ||
38 | this.options.startSpan = $(this.options.startSpan || null); | ||
39 | this.options.endSpan = $(this.options.endSpan || null); | ||
40 | |||
41 | this.restricted = this.options.restricted || false; | ||
42 | |||
43 | this.maximum = this.options.maximum || this.range.end; | ||
44 | this.minimum = this.options.minimum || this.range.start; | ||
45 | |||
46 | // Will be used to align the handle onto the track, if necessary | ||
47 | this.alignX = parseInt(this.options.alignX || '0'); | ||
48 | this.alignY = parseInt(this.options.alignY || '0'); | ||
49 | |||
50 | this.trackLength = this.maximumOffset() - this.minimumOffset(); | ||
51 | |||
52 | this.handleLength = this.isVertical() ? | ||
53 | (this.handles[0].offsetHeight != 0 ? | ||
54 | this.handles[0].offsetHeight : this.handles[0].style.height.replace(/px$/,"")) : | ||
55 | (this.handles[0].offsetWidth != 0 ? this.handles[0].offsetWidth : | ||
56 | this.handles[0].style.width.replace(/px$/,"")); | ||
57 | |||
58 | this.active = false; | ||
59 | this.dragging = false; | ||
60 | this.disabled = false; | ||
61 | |||
62 | if(this.options.disabled) this.setDisabled(); | ||
63 | |||
64 | // Allowed values array | ||
65 | this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false; | ||
66 | if(this.allowedValues) { | ||
67 | this.minimum = this.allowedValues.min(); | ||
68 | this.maximum = this.allowedValues.max(); | ||
69 | } | ||
70 | |||
71 | this.eventMouseDown = this.startDrag.bindAsEventListener(this); | ||
72 | this.eventMouseUp = this.endDrag.bindAsEventListener(this); | ||
73 | this.eventMouseMove = this.update.bindAsEventListener(this); | ||
74 | |||
75 | // Initialize handles in reverse (make sure first handle is active) | ||
76 | this.handles.each( function(h,i) { | ||
77 | i = slider.handles.length-1-i; | ||
78 | slider.setValue(parseFloat( | ||
79 | (slider.options.sliderValue instanceof Array ? | ||
80 | slider.options.sliderValue[i] : slider.options.sliderValue) || | ||
81 | slider.range.start), i); | ||
82 | Element.makePositioned(h); // fix IE | ||
83 | Event.observe(h, "mousedown", slider.eventMouseDown); | ||
84 | }); | ||
85 | |||
86 | Event.observe(this.track, "mousedown", this.eventMouseDown); | ||
87 | Event.observe(document, "mouseup", this.eventMouseUp); | ||
88 | Event.observe(document, "mousemove", this.eventMouseMove); | ||
89 | |||
90 | this.initialized = true; | ||
91 | }, | ||
92 | dispose: function() { | ||
93 | var slider = this; | ||
94 | Event.stopObserving(this.track, "mousedown", this.eventMouseDown); | ||
95 | Event.stopObserving(document, "mouseup", this.eventMouseUp); | ||
96 | Event.stopObserving(document, "mousemove", this.eventMouseMove); | ||
97 | this.handles.each( function(h) { | ||
98 | Event.stopObserving(h, "mousedown", slider.eventMouseDown); | ||
99 | }); | ||
100 | }, | ||
101 | setDisabled: function(){ | ||
102 | this.disabled = true; | ||
103 | }, | ||
104 | setEnabled: function(){ | ||
105 | this.disabled = false; | ||
106 | }, | ||
107 | getNearestValue: function(value){ | ||
108 | if(this.allowedValues){ | ||
109 | if(value >= this.allowedValues.max()) return(this.allowedValues.max()); | ||
110 | if(value <= this.allowedValues.min()) return(this.allowedValues.min()); | ||
111 | |||
112 | var offset = Math.abs(this.allowedValues[0] - value); | ||
113 | var newValue = this.allowedValues[0]; | ||
114 | this.allowedValues.each( function(v) { | ||
115 | var currentOffset = Math.abs(v - value); | ||
116 | if(currentOffset <= offset){ | ||
117 | newValue = v; | ||
118 | offset = currentOffset; | ||
119 | } | ||
120 | }); | ||
121 | return newValue; | ||
122 | } | ||
123 | if(value > this.range.end) return this.range.end; | ||
124 | if(value < this.range.start) return this.range.start; | ||
125 | return value; | ||
126 | }, | ||
127 | setValue: function(sliderValue, handleIdx){ | ||
128 | if(!this.active) { | ||
129 | this.activeHandleIdx = handleIdx || 0; | ||
130 | this.activeHandle = this.handles[this.activeHandleIdx]; | ||
131 | this.updateStyles(); | ||
132 | } | ||
133 | handleIdx = handleIdx || this.activeHandleIdx || 0; | ||
134 | if(this.initialized && this.restricted) { | ||
135 | if((handleIdx>0) && (sliderValue<this.values[handleIdx-1])) | ||
136 | sliderValue = this.values[handleIdx-1]; | ||
137 | if((handleIdx < (this.handles.length-1)) && (sliderValue>this.values[handleIdx+1])) | ||
138 | sliderValue = this.values[handleIdx+1]; | ||
139 | } | ||
140 | sliderValue = this.getNearestValue(sliderValue); | ||
141 | this.values[handleIdx] = sliderValue; | ||
142 | this.value = this.values[0]; // assure backwards compat | ||
143 | |||
144 | this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] = | ||
145 | this.translateToPx(sliderValue); | ||
146 | |||
147 | this.drawSpans(); | ||
148 | if(!this.dragging || !this.event) this.updateFinished(); | ||
149 | }, | ||
150 | setValueBy: function(delta, handleIdx) { | ||
151 | this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta, | ||
152 | handleIdx || this.activeHandleIdx || 0); | ||
153 | }, | ||
154 | translateToPx: function(value) { | ||
155 | return Math.round( | ||
156 | ((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) * | ||
157 | (value - this.range.start)) + "px"; | ||
158 | }, | ||
159 | translateToValue: function(offset) { | ||
160 | return ((offset/(this.trackLength-this.handleLength) * | ||
161 | (this.range.end-this.range.start)) + this.range.start); | ||
162 | }, | ||
163 | getRange: function(range) { | ||
164 | var v = this.values.sortBy(Prototype.K); | ||
165 | range = range || 0; | ||
166 | return $R(v[range],v[range+1]); | ||
167 | }, | ||
168 | minimumOffset: function(){ | ||
169 | return(this.isVertical() ? this.alignY : this.alignX); | ||
170 | }, | ||
171 | maximumOffset: function(){ | ||
172 | return(this.isVertical() ? | ||
173 | (this.track.offsetHeight != 0 ? this.track.offsetHeight : | ||
174 | this.track.style.height.replace(/px$/,"")) - this.alignY : | ||
175 | (this.track.offsetWidth != 0 ? this.track.offsetWidth : | ||
176 | this.track.style.width.replace(/px$/,"")) - this.alignY); | ||
177 | }, | ||
178 | isVertical: function(){ | ||
179 | return (this.axis == 'vertical'); | ||
180 | }, | ||
181 | drawSpans: function() { | ||
182 | var slider = this; | ||
183 | if(this.spans) | ||
184 | $R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) }); | ||
185 | if(this.options.startSpan) | ||
186 | this.setSpan(this.options.startSpan, | ||
187 | $R(0, this.values.length>1 ? this.getRange(0).min() : this.value )); | ||
188 | if(this.options.endSpan) | ||
189 | this.setSpan(this.options.endSpan, | ||
190 | $R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum)); | ||
191 | }, | ||
192 | setSpan: function(span, range) { | ||
193 | if(this.isVertical()) { | ||
194 | span.style.top = this.translateToPx(range.start); | ||
195 | span.style.height = this.translateToPx(range.end - range.start + this.range.start); | ||
196 | } else { | ||
197 | span.style.left = this.translateToPx(range.start); | ||
198 | span.style.width = this.translateToPx(range.end - range.start + this.range.start); | ||
199 | } | ||
200 | }, | ||
201 | updateStyles: function() { | ||
202 | this.handles.each( function(h){ Element.removeClassName(h, 'selected') }); | ||
203 | Element.addClassName(this.activeHandle, 'selected'); | ||
204 | }, | ||
205 | startDrag: function(event) { | ||
206 | if(Event.isLeftClick(event)) { | ||
207 | if(!this.disabled){ | ||
208 | this.active = true; | ||
209 | |||
210 | var handle = Event.element(event); | ||
211 | var pointer = [Event.pointerX(event), Event.pointerY(event)]; | ||
212 | var track = handle; | ||
213 | if(track==this.track) { | ||
214 | var offsets = Position.cumulativeOffset(this.track); | ||
215 | this.event = event; | ||
216 | this.setValue(this.translateToValue( | ||
217 | (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2) | ||
218 | )); | ||
219 | var offsets = Position.cumulativeOffset(this.activeHandle); | ||
220 | this.offsetX = (pointer[0] - offsets[0]); | ||
221 | this.offsetY = (pointer[1] - offsets[1]); | ||
222 | } else { | ||
223 | // find the handle (prevents issues with Safari) | ||
224 | while((this.handles.indexOf(handle) == -1) && handle.parentNode) | ||
225 | handle = handle.parentNode; | ||
226 | |||
227 | if(this.handles.indexOf(handle)!=-1) { | ||
228 | this.activeHandle = handle; | ||
229 | this.activeHandleIdx = this.handles.indexOf(this.activeHandle); | ||
230 | this.updateStyles(); | ||
231 | |||
232 | var offsets = Position.cumulativeOffset(this.activeHandle); | ||
233 | this.offsetX = (pointer[0] - offsets[0]); | ||
234 | this.offsetY = (pointer[1] - offsets[1]); | ||
235 | } | ||
236 | } | ||
237 | } | ||
238 | Event.stop(event); | ||
239 | } | ||
240 | }, | ||
241 | update: function(event) { | ||
242 | if(this.active) { | ||
243 | if(!this.dragging) this.dragging = true; | ||
244 | this.draw(event); | ||
245 | if(Prototype.Browser.WebKit) window.scrollBy(0,0); | ||
246 | Event.stop(event); | ||
247 | } | ||
248 | }, | ||
249 | draw: function(event) { | ||
250 | var pointer = [Event.pointerX(event), Event.pointerY(event)]; | ||
251 | var offsets = Position.cumulativeOffset(this.track); | ||
252 | pointer[0] -= this.offsetX + offsets[0]; | ||
253 | pointer[1] -= this.offsetY + offsets[1]; | ||
254 | this.event = event; | ||
255 | this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] )); | ||
256 | if(this.initialized && this.options.onSlide) | ||
257 | this.options.onSlide(this.values.length>1 ? this.values : this.value, this); | ||
258 | }, | ||
259 | endDrag: function(event) { | ||
260 | if(this.active && this.dragging) { | ||
261 | this.finishDrag(event, true); | ||
262 | Event.stop(event); | ||
263 | } | ||
264 | this.active = false; | ||
265 | this.dragging = false; | ||
266 | }, | ||
267 | finishDrag: function(event, success) { | ||
268 | this.active = false; | ||
269 | this.dragging = false; | ||
270 | this.updateFinished(); | ||
271 | }, | ||
272 | updateFinished: function() { | ||
273 | if(this.initialized && this.options.onChange) | ||
274 | this.options.onChange(this.values.length>1 ? this.values : this.value, this); | ||
275 | this.event = null; | ||
276 | } | ||
277 | } \ No newline at end of file | ||
diff --git a/docroot/lib/scriptaculous/sound.js b/docroot/lib/scriptaculous/sound.js new file mode 100755 index 0000000..164c79a --- /dev/null +++ b/docroot/lib/scriptaculous/sound.js | |||
@@ -0,0 +1,60 @@ | |||
1 | // script.aculo.us sound.js v1.7.1_beta3, Fri May 25 17:19:41 +0200 2007 | ||
2 | |||
3 | // Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) | ||
4 | // | ||
5 | // Based on code created by Jules Gravinese (http://www.webveteran.com/) | ||
6 | // | ||
7 | // script.aculo.us is freely distributable under the terms of an MIT-style license. | ||
8 | // For details, see the script.aculo.us web site: http://script.aculo.us/ | ||
9 | |||
10 | Sound = { | ||
11 | tracks: {}, | ||
12 | _enabled: true, | ||
13 | template: | ||
14 | new Template('<embed style="height:0" id="sound_#{track}_#{id}" src="#{url}" loop="false" autostart="true" hidden="true"/>'), | ||
15 | enable: function(){ | ||
16 | Sound._enabled = true; | ||
17 | }, | ||
18 | disable: function(){ | ||
19 | Sound._enabled = false; | ||
20 | }, | ||
21 | play: function(url){ | ||
22 | if(!Sound._enabled) return; | ||
23 | var options = Object.extend({ | ||
24 | track: 'global', url: url, replace: false | ||
25 | }, arguments[1] || {}); | ||
26 | |||
27 | if(options.replace && this.tracks[options.track]) { | ||
28 | $R(0, this.tracks[options.track].id).each(function(id){ | ||
29 | var sound = $('sound_'+options.track+'_'+id); | ||
30 | sound.Stop && sound.Stop(); | ||
31 | sound.remove(); | ||
32 | }) | ||
33 | this.tracks[options.track] = null; | ||
34 | } | ||
35 | |||
36 | if(!this.tracks[options.track]) | ||
37 | this.tracks[options.track] = { id: 0 } | ||
38 | else | ||
39 | this.tracks[options.track].id++; | ||
40 | |||
41 | options.id = this.tracks[options.track].id; | ||
42 | if (Prototype.Browser.IE) { | ||
43 | var sound = document.createElement('bgsound'); | ||
44 | sound.setAttribute('id','sound_'+options.track+'_'+options.id); | ||
45 | sound.setAttribute('src',options.url); | ||
46 | sound.setAttribute('loop','1'); | ||
47 | sound.setAttribute('autostart','true'); | ||
48 | $$('body')[0].appendChild(sound); | ||
49 | } | ||
50 | else | ||
51 | new Insertion.Bottom($$('body')[0], Sound.template.evaluate(options)); | ||
52 | } | ||
53 | }; | ||
54 | |||
55 | if(Prototype.Browser.Gecko && navigator.userAgent.indexOf("Win") > 0){ | ||
56 | if(navigator.plugins && $A(navigator.plugins).detect(function(p){ return p.name.indexOf('QuickTime') != -1 })) | ||
57 | Sound.template = new Template('<object id="sound_#{track}_#{id}" width="0" height="0" type="audio/mpeg" data="#{url}"/>') | ||
58 | else | ||
59 | Sound.play = function(){} | ||
60 | } | ||
diff --git a/docroot/lib/scriptaculous/unittest.js b/docroot/lib/scriptaculous/unittest.js new file mode 100755 index 0000000..d2dff8b --- /dev/null +++ b/docroot/lib/scriptaculous/unittest.js | |||
@@ -0,0 +1,564 @@ | |||
1 | // script.aculo.us unittest.js v1.7.1_beta3, Fri May 25 17:19:41 +0200 2007 | ||
2 | |||
3 | // Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) | ||
4 | // (c) 2005-2007 Jon Tirsen (http://www.tirsen.com) | ||
5 | // (c) 2005-2007 Michael Schuerig (http://www.schuerig.de/michael/) | ||
6 | // | ||
7 | // script.aculo.us is freely distributable under the terms of an MIT-style license. | ||
8 | // For details, see the script.aculo.us web site: http://script.aculo.us/ | ||
9 | |||
10 | // experimental, Firefox-only | ||
11 | Event.simulateMouse = function(element, eventName) { | ||
12 | var options = Object.extend({ | ||
13 | pointerX: 0, | ||
14 | pointerY: 0, | ||
15 | buttons: 0, | ||
16 | ctrlKey: false, | ||
17 | altKey: false, | ||
18 | shiftKey: false, | ||
19 | metaKey: false | ||
20 | }, arguments[2] || {}); | ||
21 | var oEvent = document.createEvent("MouseEvents"); | ||
22 | oEvent.initMouseEvent(eventName, true, true, document.defaultView, | ||
23 | options.buttons, options.pointerX, options.pointerY, options.pointerX, options.pointerY, | ||
24 | options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, 0, $(element)); | ||
25 | |||
26 | if(this.mark) Element.remove(this.mark); | ||
27 | this.mark = document.createElement('div'); | ||
28 | this.mark.appendChild(document.createTextNode(" ")); | ||
29 | document.body.appendChild(this.mark); | ||
30 | this.mark.style.position = 'absolute'; | ||
31 | this.mark.style.top = options.pointerY + "px"; | ||
32 | this.mark.style.left = options.pointerX + "px"; | ||
33 | this.mark.style.width = "5px"; | ||
34 | this.mark.style.height = "5px;"; | ||
35 | this.mark.style.borderTop = "1px solid red;" | ||
36 | this.mark.style.borderLeft = "1px solid red;" | ||
37 | |||
38 | if(this.step) | ||
39 | alert('['+new Date().getTime().toString()+'] '+eventName+'/'+Test.Unit.inspect(options)); | ||
40 | |||
41 | $(element).dispatchEvent(oEvent); | ||
42 | }; | ||
43 | |||
44 | // Note: Due to a fix in Firefox 1.0.5/6 that probably fixed "too much", this doesn't work in 1.0.6 or DP2. | ||
45 | // You need to downgrade to 1.0.4 for now to get this working | ||
46 | // See https://bugzilla.mozilla.org/show_bug.cgi?id=289940 for the fix that fixed too much | ||
47 | Event.simulateKey = function(element, eventName) { | ||
48 | var options = Object.extend({ | ||
49 | ctrlKey: false, | ||
50 | altKey: false, | ||
51 | shiftKey: false, | ||
52 | metaKey: false, | ||
53 | keyCode: 0, | ||
54 | charCode: 0 | ||
55 | }, arguments[2] || {}); | ||
56 | |||
57 | var oEvent = document.createEvent("KeyEvents"); | ||
58 | oEvent.initKeyEvent(eventName, true, true, window, | ||
59 | options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, | ||
60 | options.keyCode, options.charCode ); | ||
61 | $(element).dispatchEvent(oEvent); | ||
62 | }; | ||
63 | |||
64 | Event.simulateKeys = function(element, command) { | ||
65 | for(var i=0; i<command.length; i++) { | ||
66 | Event.simulateKey(element,'keypress',{charCode:command.charCodeAt(i)}); | ||
67 | } | ||
68 | }; | ||
69 | |||
70 | var Test = {} | ||
71 | Test.Unit = {}; | ||
72 | |||
73 | // security exception workaround | ||
74 | Test.Unit.inspect = Object.inspect; | ||
75 | |||
76 | Test.Unit.Logger = Class.create(); | ||
77 | Test.Unit.Logger.prototype = { | ||
78 | initialize: function(log) { | ||
79 | this.log = $(log); | ||
80 | if (this.log) { | ||
81 | this._createLogTable(); | ||
82 | } | ||
83 | }, | ||
84 | start: function(testName) { | ||
85 | if (!this.log) return; | ||
86 | this.testName = testName; | ||
87 | this.lastLogLine = document.createElement('tr'); | ||
88 | this.statusCell = document.createElement('td'); | ||
89 | this.nameCell = document.createElement('td'); | ||
90 | this.nameCell.className = "nameCell"; | ||
91 | this.nameCell.appendChild(document.createTextNode(testName)); | ||
92 | this.messageCell = document.createElement('td'); | ||
93 | this.lastLogLine.appendChild(this.statusCell); | ||
94 | this.lastLogLine.appendChild(this.nameCell); | ||
95 | this.lastLogLine.appendChild(this.messageCell); | ||
96 | this.loglines.appendChild(this.lastLogLine); | ||
97 | }, | ||
98 | finish: function(status, summary) { | ||
99 | if (!this.log) return; | ||
100 | this.lastLogLine.className = status; | ||
101 | this.statusCell.innerHTML = status; | ||
102 | this.messageCell.innerHTML = this._toHTML(summary); | ||
103 | this.addLinksToResults(); | ||
104 | }, | ||
105 | message: function(message) { | ||
106 | if (!this.log) return; | ||
107 | this.messageCell.innerHTML = this._toHTML(message); | ||
108 | }, | ||
109 | summary: function(summary) { | ||
110 | if (!this.log) return; | ||
111 | this.logsummary.innerHTML = this._toHTML(summary); | ||
112 | }, | ||
113 | _createLogTable: function() { | ||
114 | this.log.innerHTML = | ||
115 | '<div id="logsummary"></div>' + | ||
116 | '<table id="logtable">' + | ||
117 | '<thead><tr><th>Status</th><th>Test</th><th>Message</th></tr></thead>' + | ||
118 | '<tbody id="loglines"></tbody>' + | ||
119 | '</table>'; | ||
120 | this.logsummary = $('logsummary') | ||
121 | this.loglines = $('loglines'); | ||
122 | }, | ||
123 | _toHTML: function(txt) { | ||
124 | return txt.escapeHTML().replace(/\n/g,"<br/>"); | ||
125 | }, | ||
126 | addLinksToResults: function(){ | ||
127 | $$("tr.failed .nameCell").each( function(td){ // todo: limit to children of this.log | ||
128 | td.title = "Run only this test" | ||
129 | Event.observe(td, 'click', function(){ window.location.search = "?tests=" + td.innerHTML;}); | ||
130 | }); | ||
131 | $$("tr.passed .nameCell").each( function(td){ // todo: limit to children of this.log | ||
132 | td.title = "Run all tests" | ||
133 | Event.observe(td, 'click', function(){ window.location.search = "";}); | ||
134 | }); | ||
135 | } | ||
136 | } | ||
137 | |||
138 | Test.Unit.Runner = Class.create(); | ||
139 | Test.Unit.Runner.prototype = { | ||
140 | initialize: function(testcases) { | ||
141 | this.options = Object.extend({ | ||
142 | testLog: 'testlog' | ||
143 | }, arguments[1] || {}); | ||
144 | this.options.resultsURL = this.parseResultsURLQueryParameter(); | ||
145 | this.options.tests = this.parseTestsQueryParameter(); | ||
146 | if (this.options.testLog) { | ||
147 | this.options.testLog = $(this.options.testLog) || null; | ||
148 | } | ||
149 | if(this.options.tests) { | ||
150 | this.tests = []; | ||
151 | for(var i = 0; i < this.options.tests.length; i++) { | ||
152 | if(/^test/.test(this.options.tests[i])) { | ||
153 | this.tests.push(new Test.Unit.Testcase(this.options.tests[i], testcases[this.options.tests[i]], testcases["setup"], testcases["teardown"])); | ||
154 | } | ||
155 | } | ||
156 | } else { | ||
157 | if (this.options.test) { | ||
158 | this.tests = [new Test.Unit.Testcase(this.options.test, testcases[this.options.test], testcases["setup"], testcases["teardown"])]; | ||
159 | } else { | ||
160 | this.tests = []; | ||
161 | for(var testcase in testcases) { | ||
162 | if(/^test/.test(testcase)) { | ||
163 | this.tests.push( | ||
164 | new Test.Unit.Testcase( | ||
165 | this.options.context ? ' -> ' + this.options.titles[testcase] : testcase, | ||
166 | testcases[testcase], testcases["setup"], testcases["teardown"] | ||
167 | )); | ||
168 | } | ||
169 | } | ||
170 | } | ||
171 | } | ||
172 | this.currentTest = 0; | ||
173 | this.logger = new Test.Unit.Logger(this.options.testLog); | ||
174 | setTimeout(this.runTests.bind(this), 1000); | ||
175 | }, | ||
176 | parseResultsURLQueryParameter: function() { | ||
177 | return window.location.search.parseQuery()["resultsURL"]; | ||
178 | }, | ||
179 | parseTestsQueryParameter: function(){ | ||
180 | if (window.location.search.parseQuery()["tests"]){ | ||
181 | return window.location.search.parseQuery()["tests"].split(','); | ||
182 | }; | ||
183 | }, | ||
184 | // Returns: | ||
185 | // "ERROR" if there was an error, | ||
186 | // "FAILURE" if there was a failure, or | ||
187 | // "SUCCESS" if there was neither | ||
188 | getResult: function() { | ||
189 | var hasFailure = false; | ||
190 | for(var i=0;i<this.tests.length;i++) { | ||
191 | if (this.tests[i].errors > 0) { | ||
192 | return "ERROR"; | ||
193 | } | ||
194 | if (this.tests[i].failures > 0) { | ||
195 | hasFailure = true; | ||
196 | } | ||
197 | } | ||
198 | if (hasFailure) { | ||
199 | return "FAILURE"; | ||
200 | } else { | ||
201 | return "SUCCESS"; | ||
202 | } | ||
203 | }, | ||
204 | postResults: function() { | ||
205 | if (this.options.resultsURL) { | ||
206 | new Ajax.Request(this.options.resultsURL, | ||
207 | { method: 'get', parameters: 'result=' + this.getResult(), asynchronous: false }); | ||
208 | } | ||
209 | }, | ||
210 | runTests: function() { | ||
211 | var test = this.tests[this.currentTest]; | ||
212 | if (!test) { | ||
213 | // finished! | ||
214 | this.postResults(); | ||
215 | this.logger.summary(this.summary()); | ||
216 | return; | ||
217 | } | ||
218 | if(!test.isWaiting) { | ||
219 | this.logger.start(test.name); | ||
220 | } | ||
221 | test.run(); | ||
222 | if(test.isWaiting) { | ||
223 | this.logger.message("Waiting for " + test.timeToWait + "ms"); | ||
224 | setTimeout(this.runTests.bind(this), test.timeToWait || 1000); | ||
225 | } else { | ||
226 | this.logger.finish(test.status(), test.summary()); | ||
227 | this.currentTest++; | ||
228 | // tail recursive, hopefully the browser will skip the stackframe | ||
229 | this.runTests(); | ||
230 | } | ||
231 | }, | ||
232 | summary: function() { | ||
233 | var assertions = 0; | ||
234 | var failures = 0; | ||
235 | var errors = 0; | ||
236 | var messages = []; | ||
237 | for(var i=0;i<this.tests.length;i++) { | ||
238 | assertions += this.tests[i].assertions; | ||
239 | failures += this.tests[i].failures; | ||
240 | errors += this.tests[i].errors; | ||
241 | } | ||
242 | return ( | ||
243 | (this.options.context ? this.options.context + ': ': '') + | ||
244 | this.tests.length + " tests, " + | ||
245 | assertions + " assertions, " + | ||
246 | failures + " failures, " + | ||
247 | errors + " errors"); | ||
248 | } | ||
249 | } | ||
250 | |||
251 | Test.Unit.Assertions = Class.create(); | ||
252 | Test.Unit.Assertions.prototype = { | ||
253 | initialize: function() { | ||
254 | this.assertions = 0; | ||
255 | this.failures = 0; | ||
256 | this.errors = 0; | ||
257 | this.messages = []; | ||
258 | }, | ||
259 | summary: function() { | ||
260 | return ( | ||
261 | this.assertions + " assertions, " + | ||
262 | this.failures + " failures, " + | ||
263 | this.errors + " errors" + "\n" + | ||
264 | this.messages.join("\n")); | ||
265 | }, | ||
266 | pass: function() { | ||
267 | this.assertions++; | ||
268 | }, | ||
269 | fail: function(message) { | ||
270 | this.failures++; | ||
271 | this.messages.push("Failure: " + message); | ||
272 | }, | ||
273 | info: function(message) { | ||
274 | this.messages.push("Info: " + message); | ||
275 | }, | ||
276 | error: function(error) { | ||
277 | this.errors++; | ||
278 | this.messages.push(error.name + ": "+ error.message + "(" + Test.Unit.inspect(error) +")"); | ||
279 | }, | ||
280 | status: function() { | ||
281 | if (this.failures > 0) return 'failed'; | ||
282 | if (this.errors > 0) return 'error'; | ||
283 | return 'passed'; | ||
284 | }, | ||
285 | assert: function(expression) { | ||
286 | var message = arguments[1] || 'assert: got "' + Test.Unit.inspect(expression) + '"'; | ||
287 | try { expression ? this.pass() : | ||
288 | this.fail(message); } | ||
289 | catch(e) { this.error(e); } | ||
290 | }, | ||
291 | assertEqual: function(expected, actual) { | ||
292 | var message = arguments[2] || "assertEqual"; | ||
293 | try { (expected == actual) ? this.pass() : | ||
294 | this.fail(message + ': expected "' + Test.Unit.inspect(expected) + | ||
295 | '", actual "' + Test.Unit.inspect(actual) + '"'); } | ||
296 | catch(e) { this.error(e); } | ||
297 | }, | ||
298 | assertInspect: function(expected, actual) { | ||
299 | var message = arguments[2] || "assertInspect"; | ||
300 | try { (expected == actual.inspect()) ? this.pass() : | ||
301 | this.fail(message + ': expected "' + Test.Unit.inspect(expected) + | ||
302 | '", actual "' + Test.Unit.inspect(actual) + '"'); } | ||
303 | catch(e) { this.error(e); } | ||
304 | }, | ||
305 | assertEnumEqual: function(expected, actual) { | ||
306 | var message = arguments[2] || "assertEnumEqual"; | ||
307 | try { $A(expected).length == $A(actual).length && | ||
308 | expected.zip(actual).all(function(pair) { return pair[0] == pair[1] }) ? | ||
309 | this.pass() : this.fail(message + ': expected ' + Test.Unit.inspect(expected) + | ||
310 | ', actual ' + Test.Unit.inspect(actual)); } | ||
311 | catch(e) { this.error(e); } | ||
312 | }, | ||
313 | assertNotEqual: function(expected, actual) { | ||
314 | var message = arguments[2] || "assertNotEqual"; | ||
315 | try { (expected != actual) ? this.pass() : | ||
316 | this.fail(message + ': got "' + Test.Unit.inspect(actual) + '"'); } | ||
317 | catch(e) { this.error(e); } | ||
318 | }, | ||
319 | assertIdentical: function(expected, actual) { | ||
320 | var message = arguments[2] || "assertIdentical"; | ||
321 | try { (expected === actual) ? this.pass() : | ||
322 | this.fail(message + ': expected "' + Test.Unit.inspect(expected) + | ||
323 | '", actual "' + Test.Unit.inspect(actual) + '"'); } | ||
324 | catch(e) { this.error(e); } | ||
325 | }, | ||
326 | assertNotIdentical: function(expected, actual) { | ||
327 | var message = arguments[2] || "assertNotIdentical"; | ||
328 | try { !(expected === actual) ? this.pass() : | ||
329 | this.fail(message + ': expected "' + Test.Unit.inspect(expected) + | ||
330 | '", actual "' + Test.Unit.inspect(actual) + '"'); } | ||
331 | catch(e) { this.error(e); } | ||
332 | }, | ||
333 | assertNull: function(obj) { | ||
334 | var message = arguments[1] || 'assertNull' | ||
335 | try { (obj==null) ? this.pass() : | ||
336 | this.fail(message + ': got "' + Test.Unit.inspect(obj) + '"'); } | ||
337 | catch(e) { this.error(e); } | ||
338 | }, | ||
339 | assertMatch: function(expected, actual) { | ||
340 | var message = arguments[2] || 'assertMatch'; | ||
341 | var regex = new RegExp(expected); | ||
342 | try { (regex.exec(actual)) ? this.pass() : | ||
343 | this.fail(message + ' : regex: "' + Test.Unit.inspect(expected) + ' did not match: ' + Test.Unit.inspect(actual) + '"'); } | ||
344 | catch(e) { this.error(e); } | ||
345 | }, | ||
346 | assertHidden: function(element) { | ||
347 | var message = arguments[1] || 'assertHidden'; | ||
348 | this.assertEqual("none", element.style.display, message); | ||
349 | }, | ||
350 | assertNotNull: function(object) { | ||
351 | var message = arguments[1] || 'assertNotNull'; | ||
352 | this.assert(object != null, message); | ||
353 | }, | ||
354 | assertType: function(expected, actual) { | ||
355 | var message = arguments[2] || 'assertType'; | ||
356 | try { | ||
357 | (actual.constructor == expected) ? this.pass() : | ||
358 | this.fail(message + ': expected "' + Test.Unit.inspect(expected) + | ||
359 | '", actual "' + (actual.constructor) + '"'); } | ||
360 | catch(e) { this.error(e); } | ||
361 | }, | ||
362 | assertNotOfType: function(expected, actual) { | ||
363 | var message = arguments[2] || 'assertNotOfType'; | ||
364 | try { | ||
365 | (actual.constructor != expected) ? this.pass() : | ||
366 | this.fail(message + ': expected "' + Test.Unit.inspect(expected) + | ||
367 | '", actual "' + (actual.constructor) + '"'); } | ||
368 | catch(e) { this.error(e); } | ||
369 | }, | ||
370 | assertInstanceOf: function(expected, actual) { | ||
371 | var message = arguments[2] || 'assertInstanceOf'; | ||
372 | try { | ||
373 | (actual instanceof expected) ? this.pass() : | ||
374 | this.fail(message + ": object was not an instance of the expected type"); } | ||
375 | catch(e) { this.error(e); } | ||
376 | }, | ||
377 | assertNotInstanceOf: function(expected, actual) { | ||
378 | var message = arguments[2] || 'assertNotInstanceOf'; | ||
379 | try { | ||
380 | !(actual instanceof expected) ? this.pass() : | ||
381 | this.fail(message + ": object was an instance of the not expected type"); } | ||
382 | catch(e) { this.error(e); } | ||
383 | }, | ||
384 | assertRespondsTo: function(method, obj) { | ||
385 | var message = arguments[2] || 'assertRespondsTo'; | ||
386 | try { | ||
387 | (obj[method] && typeof obj[method] == 'function') ? this.pass() : | ||
388 | this.fail(message + ": object doesn't respond to [" + method + "]"); } | ||
389 | catch(e) { this.error(e); } | ||
390 | }, | ||
391 | assertReturnsTrue: function(method, obj) { | ||
392 | var message = arguments[2] || 'assertReturnsTrue'; | ||
393 | try { | ||
394 | var m = obj[method]; | ||
395 | if(!m) m = obj['is'+method.charAt(0).toUpperCase()+method.slice(1)]; | ||
396 | m() ? this.pass() : | ||
397 | this.fail(message + ": method returned false"); } | ||
398 | catch(e) { this.error(e); } | ||
399 | }, | ||
400 | assertReturnsFalse: function(method, obj) { | ||
401 | var message = arguments[2] || 'assertReturnsFalse'; | ||
402 | try { | ||
403 | var m = obj[method]; | ||
404 | if(!m) m = obj['is'+method.charAt(0).toUpperCase()+method.slice(1)]; | ||
405 | !m() ? this.pass() : | ||
406 | this.fail(message + ": method returned true"); } | ||
407 | catch(e) { this.error(e); } | ||
408 | }, | ||
409 | assertRaise: function(exceptionName, method) { | ||
410 | var message = arguments[2] || 'assertRaise'; | ||
411 | try { | ||
412 | method(); | ||
413 | this.fail(message + ": exception expected but none was raised"); } | ||
414 | catch(e) { | ||
415 | ((exceptionName == null) || (e.name==exceptionName)) ? this.pass() : this.error(e); | ||
416 | } | ||
417 | }, | ||
418 | assertElementsMatch: function() { | ||
419 | var expressions = $A(arguments), elements = $A(expressions.shift()); | ||
420 | if (elements.length != expressions.length) { | ||
421 | this.fail('assertElementsMatch: size mismatch: ' + elements.length + ' elements, ' + expressions.length + ' expressions'); | ||
422 | return false; | ||
423 | } | ||
424 | elements.zip(expressions).all(function(pair, index) { | ||
425 | var element = $(pair.first()), expression = pair.last(); | ||
426 | if (element.match(expression)) return true; | ||
427 | this.fail('assertElementsMatch: (in index ' + index + ') expected ' + expression.inspect() + ' but got ' + element.inspect()); | ||
428 | }.bind(this)) && this.pass(); | ||
429 | }, | ||
430 | assertElementMatches: function(element, expression) { | ||
431 | this.assertElementsMatch([element], expression); | ||
432 | }, | ||
433 | benchmark: function(operation, iterations) { | ||
434 | var startAt = new Date(); | ||
435 | (iterations || 1).times(operation); | ||
436 | var timeTaken = ((new Date())-startAt); | ||
437 | this.info((arguments[2] || 'Operation') + ' finished ' + | ||
438 | iterations + ' iterations in ' + (timeTaken/1000)+'s' ); | ||
439 | return timeTaken; | ||
440 | }, | ||
441 | _isVisible: function(element) { | ||
442 | element = $(element); | ||
443 | if(!element.parentNode) return true; | ||
444 | this.assertNotNull(element); | ||
445 | if(element.style && Element.getStyle(element, 'display') == 'none') | ||
446 | return false; | ||
447 | |||
448 | return this._isVisible(element.parentNode); | ||
449 | }, | ||
450 | assertNotVisible: function(element) { | ||
451 | this.assert(!this._isVisible(element), Test.Unit.inspect(element) + " was not hidden and didn't have a hidden parent either. " + ("" || arguments[1])); | ||
452 | }, | ||
453 | assertVisible: function(element) { | ||
454 | this.assert(this._isVisible(element), Test.Unit.inspect(element) + " was not visible. " + ("" || arguments[1])); | ||
455 | }, | ||
456 | benchmark: function(operation, iterations) { | ||
457 | var startAt = new Date(); | ||
458 | (iterations || 1).times(operation); | ||
459 | var timeTaken = ((new Date())-startAt); | ||
460 | this.info((arguments[2] || 'Operation') + ' finished ' + | ||
461 | iterations + ' iterations in ' + (timeTaken/1000)+'s' ); | ||
462 | return timeTaken; | ||
463 | } | ||
464 | } | ||
465 | |||
466 | Test.Unit.Testcase = Class.create(); | ||
467 | Object.extend(Object.extend(Test.Unit.Testcase.prototype, Test.Unit.Assertions.prototype), { | ||
468 | initialize: function(name, test, setup, teardown) { | ||
469 | Test.Unit.Assertions.prototype.initialize.bind(this)(); | ||
470 | this.name = name; | ||
471 | |||
472 | if(typeof test == 'string') { | ||
473 | test = test.gsub(/(\.should[^\(]+\()/,'#{0}this,'); | ||
474 | test = test.gsub(/(\.should[^\(]+)\(this,\)/,'#{1}(this)'); | ||
475 | this.test = function() { | ||
476 | eval('with(this){'+test+'}'); | ||
477 | } | ||
478 | } else { | ||
479 | this.test = test || function() {}; | ||
480 | } | ||
481 | |||
482 | this.setup = setup || function() {}; | ||
483 | this.teardown = teardown || function() {}; | ||
484 | this.isWaiting = false; | ||
485 | this.timeToWait = 1000; | ||
486 | }, | ||
487 | wait: function(time, nextPart) { | ||
488 | this.isWaiting = true; | ||
489 | this.test = nextPart; | ||
490 | this.timeToWait = time; | ||
491 | }, | ||
492 | run: function() { | ||
493 | try { | ||
494 | try { | ||
495 | if (!this.isWaiting) this.setup.bind(this)(); | ||
496 | this.isWaiting = false; | ||
497 | this.test.bind(this)(); | ||
498 | } finally { | ||
499 | if(!this.isWaiting) { | ||
500 | this.teardown.bind(this)(); | ||
501 | } | ||
502 | } | ||
503 | } | ||
504 | catch(e) { this.error(e); } | ||
505 | } | ||
506 | }); | ||
507 | |||
508 | // *EXPERIMENTAL* BDD-style testing to please non-technical folk | ||
509 | // This draws many ideas from RSpec http://rspec.rubyforge.org/ | ||
510 | |||
511 | Test.setupBDDExtensionMethods = function(){ | ||
512 | var METHODMAP = { | ||
513 | shouldEqual: 'assertEqual', | ||
514 | shouldNotEqual: 'assertNotEqual', | ||
515 | shouldEqualEnum: 'assertEnumEqual', | ||
516 | shouldBeA: 'assertType', | ||
517 | shouldNotBeA: 'assertNotOfType', | ||
518 | shouldBeAn: 'assertType', | ||
519 | shouldNotBeAn: 'assertNotOfType', | ||
520 | shouldBeNull: 'assertNull', | ||
521 | shouldNotBeNull: 'assertNotNull', | ||
522 | |||
523 | shouldBe: 'assertReturnsTrue', | ||
524 | shouldNotBe: 'assertReturnsFalse', | ||
525 | shouldRespondTo: 'assertRespondsTo' | ||
526 | }; | ||
527 | Test.BDDMethods = {}; | ||
528 | for(m in METHODMAP) { | ||
529 | Test.BDDMethods[m] = eval( | ||
530 | 'function(){'+ | ||
531 | 'var args = $A(arguments);'+ | ||
532 | 'var scope = args.shift();'+ | ||
533 | 'scope.'+METHODMAP[m]+'.apply(scope,(args || []).concat([this])); }'); | ||
534 | } | ||
535 | [Array.prototype, String.prototype, Number.prototype].each( | ||
536 | function(p){ Object.extend(p, Test.BDDMethods) } | ||
537 | ); | ||
538 | } | ||
539 | |||
540 | Test.context = function(name, spec, log){ | ||
541 | Test.setupBDDExtensionMethods(); | ||
542 | |||
543 | var compiledSpec = {}; | ||
544 | var titles = {}; | ||
545 | for(specName in spec) { | ||
546 | switch(specName){ | ||
547 | case "setup": | ||
548 | case "teardown": | ||
549 | compiledSpec[specName] = spec[specName]; | ||
550 | break; | ||
551 | default: | ||
552 | var testName = 'test'+specName.gsub(/\s+/,'-').camelize(); | ||
553 | var body = spec[specName].toString().split('\n').slice(1); | ||
554 | if(/^\{/.test(body[0])) body = body.slice(1); | ||
555 | body.pop(); | ||
556 | body = body.map(function(statement){ | ||
557 | return statement.strip() | ||
558 | }); | ||
559 | compiledSpec[testName] = body.join('\n'); | ||
560 | titles[testName] = specName; | ||
561 | } | ||
562 | } | ||
563 | new Test.Unit.Runner(compiledSpec, { titles: titles, testLog: log || 'testlog', context: name }); | ||
564 | }; \ No newline at end of file | ||
diff --git a/docroot/lib/swfobject/swfobject.js b/docroot/lib/swfobject/swfobject.js new file mode 100755 index 0000000..caa256a --- /dev/null +++ b/docroot/lib/swfobject/swfobject.js | |||
@@ -0,0 +1,233 @@ | |||
1 | /** | ||
2 | * SWFObject v1.5: Flash Player detection and embed - http://blog.deconcept.com/swfobject/ | ||
3 | * | ||
4 | * SWFObject is (c) 2007 Geoff Stearns and is released under the MIT License: | ||
5 | * http://www.opensource.org/licenses/mit-license.php | ||
6 | * | ||
7 | */ | ||
8 | if(typeof deconcept == "undefined") var deconcept = new Object(); | ||
9 | if(typeof deconcept.util == "undefined") deconcept.util = new Object(); | ||
10 | if(typeof deconcept.SWFObjectUtil == "undefined") deconcept.SWFObjectUtil = new Object(); | ||
11 | deconcept.SWFObject = function(swf, id, w, h, ver, c, quality, xiRedirectUrl, redirectUrl, detectKey) { | ||
12 | if (!document.getElementById) { return; } | ||
13 | this.DETECT_KEY = detectKey ? detectKey : 'detectflash'; | ||
14 | this.skipDetect = deconcept.util.getRequestParameter(this.DETECT_KEY); | ||
15 | this.params = new Object(); | ||
16 | this.variables = new Object(); | ||
17 | this.attributes = new Array(); | ||
18 | if(swf) { this.setAttribute('swf', swf); } | ||
19 | if(id) { this.setAttribute('id', id); } | ||
20 | if(w) { this.setAttribute('width', w); } | ||
21 | if(h) { this.setAttribute('height', h); } | ||
22 | if(ver) { this.setAttribute('version', new deconcept.PlayerVersion(ver.toString().split("."))); } | ||
23 | this.installedVer = deconcept.SWFObjectUtil.getPlayerVersion(); | ||
24 | if (!window.opera && document.all && this.installedVer.major > 7) { | ||
25 | // only add the onunload cleanup if the Flash Player version supports External Interface and we are in IE | ||
26 | deconcept.SWFObject.doPrepUnload = true; | ||
27 | } | ||
28 | if(c) { this.addParam('bgcolor', c); } | ||
29 | var q = quality ? quality : 'high'; | ||
30 | this.addParam('quality', q); | ||
31 | this.setAttribute('useExpressInstall', false); | ||
32 | this.setAttribute('doExpressInstall', false); | ||
33 | var xir = (xiRedirectUrl) ? xiRedirectUrl : window.location; | ||
34 | this.setAttribute('xiRedirectUrl', xir); | ||
35 | this.setAttribute('redirectUrl', ''); | ||
36 | if(redirectUrl) { this.setAttribute('redirectUrl', redirectUrl); } | ||
37 | } | ||
38 | deconcept.SWFObject.prototype = { | ||
39 | useExpressInstall: function(path) { | ||
40 | this.xiSWFPath = !path ? "expressinstall.swf" : path; | ||
41 | this.setAttribute('useExpressInstall', true); | ||
42 | }, | ||
43 | setAttribute: function(name, value){ | ||
44 | this.attributes[name] = value; | ||
45 | }, | ||
46 | getAttribute: function(name){ | ||
47 | return this.attributes[name]; | ||
48 | }, | ||
49 | addParam: function(name, value){ | ||
50 | this.params[name] = value; | ||
51 | }, | ||
52 | getParams: function(){ | ||
53 | return this.params; | ||
54 | }, | ||
55 | addVariable: function(name, value){ | ||
56 | this.variables[name] = value; | ||
57 | }, | ||
58 | getVariable: function(name){ | ||
59 | return this.variables[name]; | ||
60 | }, | ||
61 | getVariables: function(){ | ||
62 | return this.variables; | ||
63 | }, | ||
64 | getVariablePairs: function(){ | ||
65 | var variablePairs = new Array(); | ||
66 | var key; | ||
67 | var variables = this.getVariables(); | ||
68 | for(key in variables){ | ||
69 | variablePairs[variablePairs.length] = key +"="+ variables[key]; | ||
70 | } | ||
71 | return variablePairs; | ||
72 | }, | ||
73 | getSWFHTML: function() { | ||
74 | var swfNode = ""; | ||
75 | if (navigator.plugins && navigator.mimeTypes && navigator.mimeTypes.length) { // netscape plugin architecture | ||
76 | if (this.getAttribute("doExpressInstall")) { | ||
77 | this.addVariable("MMplayerType", "PlugIn"); | ||
78 | this.setAttribute('swf', this.xiSWFPath); | ||
79 | } | ||
80 | swfNode = '<embed type="application/x-shockwave-flash" src="'+ this.getAttribute('swf') +'" width="'+ this.getAttribute('width') +'" height="'+ this.getAttribute('height') +'" style="'+ this.getAttribute('style') +'"'; | ||
81 | swfNode += ' id="'+ this.getAttribute('id') +'" name="'+ this.getAttribute('id') +'" '; | ||
82 | var params = this.getParams(); | ||
83 | for(var key in params){ swfNode += [key] +'="'+ params[key] +'" '; } | ||
84 | var pairs = this.getVariablePairs().join("&"); | ||
85 | if (pairs.length > 0){ swfNode += 'flashvars="'+ pairs +'"'; } | ||
86 | swfNode += '/>'; | ||
87 | } else { // PC IE | ||
88 | if (this.getAttribute("doExpressInstall")) { | ||
89 | this.addVariable("MMplayerType", "ActiveX"); | ||
90 | this.setAttribute('swf', this.xiSWFPath); | ||
91 | } | ||
92 | swfNode = '<object id="'+ this.getAttribute('id') +'" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="'+ this.getAttribute('width') +'" height="'+ this.getAttribute('height') +'" style="'+ this.getAttribute('style') +'">'; | ||
93 | swfNode += '<param name="movie" value="'+ this.getAttribute('swf') +'" />'; | ||
94 | var params = this.getParams(); | ||
95 | for(var key in params) { | ||
96 | swfNode += '<param name="'+ key +'" value="'+ params[key] +'" />'; | ||
97 | } | ||
98 | var pairs = this.getVariablePairs().join("&"); | ||
99 | if(pairs.length > 0) {swfNode += '<param name="flashvars" value="'+ pairs +'" />';} | ||
100 | swfNode += "</object>"; | ||
101 | } | ||
102 | return swfNode; | ||
103 | }, | ||
104 | write: function(elementId){ | ||
105 | if(this.getAttribute('useExpressInstall')) { | ||
106 | // check to see if we need to do an express install | ||
107 | var expressInstallReqVer = new deconcept.PlayerVersion([6,0,65]); | ||
108 | if (this.installedVer.versionIsValid(expressInstallReqVer) && !this.installedVer.versionIsValid(this.getAttribute('version'))) { | ||
109 | this.setAttribute('doExpressInstall', true); | ||
110 | this.addVariable("MMredirectURL", escape(this.getAttribute('xiRedirectUrl'))); | ||
111 | document.title = document.title.slice(0, 47) + " - Flash Player Installation"; | ||
112 | this.addVariable("MMdoctitle", document.title); | ||
113 | } | ||
114 | } | ||
115 | if(this.skipDetect || this.getAttribute('doExpressInstall') || this.installedVer.versionIsValid(this.getAttribute('version'))){ | ||
116 | var n = (typeof elementId == 'string') ? document.getElementById(elementId) : elementId; | ||
117 | n.innerHTML = this.getSWFHTML(); | ||
118 | return true; | ||
119 | }else{ | ||
120 | if(this.getAttribute('redirectUrl') != "") { | ||
121 | document.location.replace(this.getAttribute('redirectUrl')); | ||
122 | } | ||
123 | } | ||
124 | return false; | ||
125 | } | ||
126 | } | ||
127 | |||
128 | /* ---- detection functions ---- */ | ||
129 | deconcept.SWFObjectUtil.getPlayerVersion = function(){ | ||
130 | var PlayerVersion = new deconcept.PlayerVersion([0,0,0]); | ||
131 | if(navigator.plugins && navigator.mimeTypes.length){ | ||
132 | var x = navigator.plugins["Shockwave Flash"]; | ||
133 | if(x && x.description) { | ||
134 | PlayerVersion = new deconcept.PlayerVersion(x.description.replace(/([a-zA-Z]|\s)+/, "").replace(/(\s+r|\s+b[0-9]+)/, ".").split(".")); | ||
135 | } | ||
136 | }else if (navigator.userAgent && navigator.userAgent.indexOf("Windows CE") >= 0){ // if Windows CE | ||
137 | var axo = 1; | ||
138 | var counter = 3; | ||
139 | while(axo) { | ||
140 | try { | ||
141 | counter++; | ||
142 | axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash."+ counter); | ||
143 | // document.write("player v: "+ counter); | ||
144 | PlayerVersion = new deconcept.PlayerVersion([counter,0,0]); | ||
145 | } catch (e) { | ||
146 | axo = null; | ||
147 | } | ||
148 | } | ||
149 | } else { // Win IE (non mobile) | ||
150 | // do minor version lookup in IE, but avoid fp6 crashing issues | ||
151 | // see http://blog.deconcept.com/2006/01/11/getvariable-setvariable-crash-internet-explorer-flash-6/ | ||
152 | try{ | ||
153 | var axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7"); | ||
154 | }catch(e){ | ||
155 | try { | ||
156 | var axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6"); | ||
157 | PlayerVersion = new deconcept.PlayerVersion([6,0,21]); | ||
158 | axo.AllowScriptAccess = "always"; // error if player version < 6.0.47 (thanks to Michael Williams @ Adobe for this code) | ||
159 | } catch(e) { | ||
160 | if (PlayerVersion.major == 6) { | ||
161 | return PlayerVersion; | ||
162 | } | ||
163 | } | ||
164 | try { | ||
165 | axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash"); | ||
166 | } catch(e) {} | ||
167 | } | ||
168 | if (axo != null) { | ||
169 | PlayerVersion = new deconcept.PlayerVersion(axo.GetVariable("$version").split(" ")[1].split(",")); | ||
170 | } | ||
171 | } | ||
172 | return PlayerVersion; | ||
173 | } | ||
174 | deconcept.PlayerVersion = function(arrVersion){ | ||
175 | this.major = arrVersion[0] != null ? parseInt(arrVersion[0]) : 0; | ||
176 | this.minor = arrVersion[1] != null ? parseInt(arrVersion[1]) : 0; | ||
177 | this.rev = arrVersion[2] != null ? parseInt(arrVersion[2]) : 0; | ||
178 | } | ||
179 | deconcept.PlayerVersion.prototype.versionIsValid = function(fv){ | ||
180 | if(this.major < fv.major) return false; | ||
181 | if(this.major > fv.major) return true; | ||
182 | if(this.minor < fv.minor) return false; | ||
183 | if(this.minor > fv.minor) return true; | ||
184 | if(this.rev < fv.rev) return false; | ||
185 | return true; | ||
186 | } | ||
187 | /* ---- get value of query string param ---- */ | ||
188 | deconcept.util = { | ||
189 | getRequestParameter: function(param) { | ||
190 | var q = document.location.search || document.location.hash; | ||
191 | if (param == null) { return q; } | ||
192 | if(q) { | ||
193 | var pairs = q.substring(1).split("&"); | ||
194 | for (var i=0; i < pairs.length; i++) { | ||
195 | if (pairs[i].substring(0, pairs[i].indexOf("=")) == param) { | ||
196 | return pairs[i].substring((pairs[i].indexOf("=")+1)); | ||
197 | } | ||
198 | } | ||
199 | } | ||
200 | return ""; | ||
201 | } | ||
202 | } | ||
203 | /* fix for video streaming bug */ | ||
204 | deconcept.SWFObjectUtil.cleanupSWFs = function() { | ||
205 | var objects = document.getElementsByTagName("OBJECT"); | ||
206 | for (var i = objects.length - 1; i >= 0; i--) { | ||
207 | objects[i].style.display = 'none'; | ||
208 | for (var x in objects[i]) { | ||
209 | if (typeof objects[i][x] == 'function') { | ||
210 | objects[i][x] = function(){}; | ||
211 | } | ||
212 | } | ||
213 | } | ||
214 | } | ||
215 | // fixes bug in some fp9 versions see http://blog.deconcept.com/2006/07/28/swfobject-143-released/ | ||
216 | if (deconcept.SWFObject.doPrepUnload) { | ||
217 | if (!deconcept.unloadSet) { | ||
218 | deconcept.SWFObjectUtil.prepUnload = function() { | ||
219 | __flash_unloadHandler = function(){}; | ||
220 | __flash_savedUnloadHandler = function(){}; | ||
221 | window.attachEvent("onunload", deconcept.SWFObjectUtil.cleanupSWFs); | ||
222 | } | ||
223 | window.attachEvent("onbeforeunload", deconcept.SWFObjectUtil.prepUnload); | ||
224 | deconcept.unloadSet = true; | ||
225 | } | ||
226 | } | ||
227 | /* add document.getElementById if needed (mobile IE < 5) */ | ||
228 | if (!document.getElementById && document.all) { document.getElementById = function(id) { return document.all[id]; }} | ||
229 | |||
230 | /* add some aliases for ease of use/backwards compatibility */ | ||
231 | var getQueryParamValue = deconcept.util.getRequestParameter; | ||
232 | var FlashObject = deconcept.SWFObject; // for legacy support | ||
233 | var SWFObject = deconcept.SWFObject; | ||
diff --git a/docroot/logged_out.html b/docroot/logged_out.html new file mode 100755 index 0000000..8ae997a --- /dev/null +++ b/docroot/logged_out.html | |||
@@ -0,0 +1,27 @@ | |||
1 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | ||
2 | <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> | ||
3 | <!-- | ||
4 | * Material Experience - Logged Out Page | ||
5 | * | ||
6 | * EYEMG - Interactive Media Group | ||
7 | * Created by Mike Crute (mcrute@eyemg.com) | ||
8 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
9 | --> | ||
10 | <head> | ||
11 | <title>Logged Out - Material Experience</title> | ||
12 | <meta http-equiv="content-type" content="text/html;charset=utf-8" /> | ||
13 | <style type="text/css">@import url('/application.css');</style> | ||
14 | </head> | ||
15 | |||
16 | <body style="text-align: center"> | ||
17 | |||
18 | <img src="/images/logo.gif" alt="Material Experience Logo" style="margin-top:40px;" /> | ||
19 | <h1>Logged Out</h1> | ||
20 | <p> | ||
21 | You have been logged out of the Material Experience site.<br /> | ||
22 | You can <a href="http://materialexperience.santoprene.com">click | ||
23 | here to go back to | ||
24 | Material Experience</a>. | ||
25 | </p> | ||
26 | </body> | ||
27 | </html> \ No newline at end of file | ||
diff --git a/docroot/pngbehavior.htc b/docroot/pngbehavior.htc new file mode 100755 index 0000000..31367d1 --- /dev/null +++ b/docroot/pngbehavior.htc | |||
@@ -0,0 +1,42 @@ | |||
1 | /* | ||
2 | * Material Experience - IE 6 PNG Behavior | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | * | ||
8 | * Fixes the transparent PNG issue with IE 6. We only modified this | ||
9 | * code, it actually came from: | ||
10 | * http://webfx.eae.net/dhtml/pngbehavior/pngbehavior.html | ||
11 | */ | ||
12 | <public:component lightWeight="true"> | ||
13 | <public:attach event="onpropertychange" onevent="propertyChanged()" /> | ||
14 | <script type="text/javascript"> | ||
15 | var supported = /MSIE ((5\.5)|[6789])/.test(navigator.userAgent) && navigator.platform == "Win32"; | ||
16 | var blankSrc = "/images/blank.gif"; | ||
17 | |||
18 | if (supported) fixImage(); | ||
19 | |||
20 | function propertyChanged() { | ||
21 | if (!supported) return; | ||
22 | if (event.propertyName != "src") return; | ||
23 | if (src == blankSrc) fixImage(); | ||
24 | } | ||
25 | |||
26 | function fixImage() { | ||
27 | var src = element.src; | ||
28 | var alt = element.alt; | ||
29 | var realSrc; | ||
30 | |||
31 | if (!new RegExp(blankSrc).test(src)) | ||
32 | realSrc = src; | ||
33 | |||
34 | if (/\.png$/i.test(realSrc)) { | ||
35 | element.src = blankSrc; | ||
36 | element.alt = alt; | ||
37 | element.title = alt; | ||
38 | element.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + ")"; | ||
39 | } | ||
40 | } | ||
41 | </script> | ||
42 | </public:component> | ||
diff --git a/docroot/specialcases.css b/docroot/specialcases.css new file mode 100755 index 0000000..35df927 --- /dev/null +++ b/docroot/specialcases.css | |||
@@ -0,0 +1,16 @@ | |||
1 | /* | ||
2 | * Material Experience - Special Cases Stylesheet | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | * | ||
8 | * Stylesheet that should be sourced by all special cases card content. | ||
9 | * This should do a bare minimum of styles but they are all required | ||
10 | * per the client. | ||
11 | */ | ||
12 | |||
13 | a, a:visited | ||
14 | { | ||
15 | color: rgb(100, 100, 100); | ||
16 | } \ No newline at end of file | ||
@@ -0,0 +1,135 @@ | |||
1 | #!/bin/bash | ||
2 | # | ||
3 | # ExxonMobil Designer Site Build Script | ||
4 | # by Mike Crute, EYEMG (mcrute@eyemg.com) | ||
5 | # | ||
6 | # This is a pretty simple build script just export a | ||
7 | # tag or the trunk from SVN and run ./make, running | ||
8 | # make (w/out the ./) will not work. You can then | ||
9 | # take the generated tarball and explode it on the | ||
10 | # production server w/out any further configuration. | ||
11 | # | ||
12 | |||
13 | cd docroot | ||
14 | |||
15 | # Create the build directory and copy over the cgi-bin | ||
16 | mkdir -p build/{docroot,cgi-bin} | ||
17 | mkdir build/docroot/lib | ||
18 | cp -R ../cgi-bin/* build/cgi-bin/ | ||
19 | |||
20 | # Create a build date file | ||
21 | echo "Designer Site Built `date +'%Y-%m-%d %H:%M:%S'` EST" > build/docroot/build.date | ||
22 | echo "Code Version: `grep 'releaseVersion' classes/sme.namespace.js | cut -d"'" -f 2 `" >> build/docroot/build.date | ||
23 | echo "Subversion Revision: `svn info | grep 'Revision' | awk '{ print $2 }'`" >> build/docroot/build.date | ||
24 | |||
25 | # Remove stuff from the cgi-bin that does not belong in | ||
26 | # production. | ||
27 | rm build/cgi-bin/*.sql | ||
28 | |||
29 | # Also copy over any files that don't need special processing | ||
30 | cp -R images \ | ||
31 | blank.gif \ | ||
32 | favicon.ico \ | ||
33 | custom_content \ | ||
34 | build/docroot/ | ||
35 | |||
36 | cp -R lib/scriptaculous build/docroot/lib/ | ||
37 | |||
38 | # Prepare the .htaccess file for production | ||
39 | # | ||
40 | # We use a sparse layout in development where each class is | ||
41 | # in its own separate file but in production we use a solid | ||
42 | # layout that is also gzipped so we have to un-comment the | ||
43 | # gzip headers in our htaccess file. | ||
44 | |||
45 | #sed ' | ||
46 | # /### PRODUCTION ###/,/### END PRODUCTION ###/ { | ||
47 | # /^###.*/d | ||
48 | # s/#//g | ||
49 | # } | ||
50 | #' .htaccess > build/docroot/.htaccess | ||
51 | |||
52 | cp .htaccess build/docroot/.htaccess | ||
53 | |||
54 | # Minify the PNG Behavior | ||
55 | sed ' | ||
56 | /\/\*/,/\*\// { | ||
57 | /.*/d; | ||
58 | } | ||
59 | ' pngbehavior.htc > build/docroot/pngbehavior.htc | ||
60 | |||
61 | # Concatenate the Javascript files into a single library file | ||
62 | # | ||
63 | # application.js loads all the javascript files in the development | ||
64 | # environment. When we go to production we need to parse out the | ||
65 | # actual script names being loaded and cat them, in order, into | ||
66 | # the final output application.js file. | ||
67 | |||
68 | # GNU sed uses the -r flag for extended regular expressions, | ||
69 | # Darwin uses the -E flag for regular expressions. If neither | ||
70 | # of these hold true we might as well fail. | ||
71 | if [[ `uname` == 'Linux' ]]; then | ||
72 | MYFILES=`sed -r -f ../build_system/get_jsfiles.sed application.js` | ||
73 | elif [[ `uname` == 'Darwin' ]]; then | ||
74 | MYFILES=`sed -E -f ../build_system/get_jsfiles.sed application.js` | ||
75 | else | ||
76 | print 'No valid sed command could be determined.' | ||
77 | exit 1 | ||
78 | fi | ||
79 | |||
80 | for item in $MYFILES; do | ||
81 | cat $item >> build/docroot/application.js.in | ||
82 | done | ||
83 | |||
84 | # Remove development code from the application code | ||
85 | # and append it to the libraries files | ||
86 | sed '/^;;;.*/d' build/docroot/application.js.in >> build/docroot/application.js.out | ||
87 | sed '/^\/\*;;;.*/d' build/docroot/application.js.out >> build/docroot/application.js | ||
88 | rm build/docroot/application.js.in build/docroot/application.js.out | ||
89 | |||
90 | # Minify index.html by removing leading spaces on each line | ||
91 | # as well as the ID comment, blank lines, and any script tags | ||
92 | # that appear in the <head> of the document. | ||
93 | sed 's/^[[:space:]]*//; /^$/d; s/\n$//; /<!--/,/-->/ { /.*/d; }' index.html > build/docroot/index.html | ||
94 | sed 's/^[[:space:]]*//; /^$/d; s/\n$//; /<!--/,/-->/ { /.*/d; }' logged_out.html > build/docroot/logged_out.html | ||
95 | |||
96 | # Minify the application Javascript using the YUI Compressor | ||
97 | java -jar ../build_system/yui_compressor.jar -o application.o build/docroot/application.js > /dev/null 2>&1 | ||
98 | mv application.o build/docroot/application.js | ||
99 | |||
100 | # Minify the JSON data files | ||
101 | mkdir -p build/docroot/data | ||
102 | for item in `ls data/`; do | ||
103 | java -jar ../build_system/yui_compressor.jar -o build/docroot/data/$item data/$item > /dev/null 2>&1 | ||
104 | done | ||
105 | |||
106 | # Can't minimize JSON, the semicolon at the end breaks everything | ||
107 | cat data/card_tables.js > build/docroot/data/card_tables.js | ||
108 | |||
109 | # Minify the application CSS using our custom compressor | ||
110 | ../build_system/cmpcss application.css > build/docroot/application.css | ||
111 | ../build_system/cmpcss specialcases.css > build/docroot/specialcases.css | ||
112 | |||
113 | # Gzip all that should be gzipped. This really should be done server-side | ||
114 | # but for now we work around it by statically compressing them at build time | ||
115 | # and serving it with the correct headers in the .htaccess file | ||
116 | #gzip build/docroot/application.js | ||
117 | #gzip build/docroot/application.css | ||
118 | #gzip build/docroot/index.html | ||
119 | # | ||
120 | #mv build/docroot/application.js.gz build/docroot/application.js | ||
121 | #mv build/docroot/application.css.gz build/docroot/application.css | ||
122 | #mv build/docroot/index.html.gz build/docroot/index.html | ||
123 | |||
124 | # Tar it all up in a development dump | ||
125 | mv build aes_designer | ||
126 | cd aes_designer | ||
127 | |||
128 | # Removing the date since its really not needed | ||
129 | tar -cjvf ../../aes_designer.tbz2 * > /dev/null 2>&1 | ||
130 | #tar -cjvf ../../aes_designer-`date +%Y%m%d_%H%M%S`.tbz2 * > /dev/null 2>&1 | ||
131 | |||
132 | |||
133 | cd .. | ||
134 | |||
135 | rm -rf aes_designer/ | ||
@@ -0,0 +1,10 @@ | |||
1 | #!/bin/bash | ||
2 | |||
3 | ssh mcrute@fred.eyemg.com "svn status /usr/web/designer | grep -v '?'" > /dev/null 2>&1 | ||
4 | |||
5 | if [[ $? == 0 && $1 != '-f' ]]; then | ||
6 | echo "Changes exist on dev. Use SVN instead." | ||
7 | exit 1 | ||
8 | fi | ||
9 | |||
10 | rsync -auvz -e "ssh" --delete-after --exclude "*.svn" "`pwd`/" mcrute@fred.eyemg.com:/usr/web/designer/ | ||