Simple Lightbox - Version 1.5

Version Description

  • Add: Theme support
  • Optimize: Javascript cleanup and file size reductions
  • Optimize: CSS cleanup
Download this release

Release Info

Developer Archetyped
Plugin Icon wp plugin Simple Lightbox
Version 1.5
Comparing to
See all releases

Code changes from version 1.3 to 1.5

css/admin.css CHANGED
@@ -1,3 +1,28 @@
1
  .subhead {
2
  margin-bottom: .2em;
3
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  .subhead {
2
  margin-bottom: .2em;
3
  }
4
+
5
+ #slb_settings {
6
+ display: block;
7
+ padding: 2em 0 0;
8
+ }
9
+
10
+ .slb_option_item .block {
11
+ display: inline-block;
12
+ }
13
+
14
+ .slb_option_item label.title {
15
+ width: 200px;
16
+ padding: 10px;
17
+ }
18
+
19
+ .slb_option_item .input {
20
+ font-size: 11px;
21
+ line-height: 20px;
22
+ margin-bottom: 9px;
23
+ padding: 8px 10px;
24
+ }
25
+
26
+ .slb_option_item .input select {
27
+ min-width: 12em;
28
+ }
css/lb_black.css ADDED
Binary file
css/lightbox.css CHANGED
Binary file
images/closelabel_black.gif ADDED
Binary file
images/loading_black.gif ADDED
Binary file
images/nextlabel_black.gif ADDED
Binary file
images/prevlabel_black.gif ADDED
Binary file
includes/class.base.php CHANGED
@@ -10,6 +10,12 @@ require_once 'class.utilities.php';
10
  */
11
  class SLB_Base {
12
 
 
 
 
 
 
 
13
  /**
14
  * Prefix for Cornerstone-related data (attributes, DB tables, etc.)
15
  * @var string
@@ -33,140 +39,114 @@ class SLB_Base {
33
  * Constructor
34
  */
35
  function __construct() {
36
- $this->util =& new SLB_Utilities();
37
  }
38
 
 
 
39
  /**
40
  * Default initialization method
41
  * To be overriden by child classes
42
  */
43
- function init() {}
44
-
45
- /**
46
- * Returns callback to instance method
47
- * @param string $method Method name
48
- * @return array Callback array
49
- */
50
- function &m($method) {
51
- return $this->util->m($this, $method);
52
  }
53
 
54
- /**
55
- * Retrieves post metadata for internal methods
56
- * Metadata set internally is wrapped in an array so it is unwrapped before returned the retrieved value
57
- * @see get_post_meta()
58
- * @param int $post_id Post ID
59
- * @param string $key Name of metadata to retrieve
60
- * @param boolean $single Whether or not to retrieve single value or not
61
- * @return mixed Retrieved post metadata
62
- */
63
- function post_meta_get($post_id, $key, $single = false) {
64
- $meta_value = get_post_meta($post_id, $this->post_meta_get_key($key), $single);
65
- if (is_array($meta_value) && count($meta_value) == 1)
66
- $meta_value = $meta_value[0];
67
- return $meta_value;
68
  }
69
 
70
- /**
71
- * Wraps metadata in array for storage in database
72
- * @param mixed $meta_value Value to be set as metadata
73
- * @return array Wrapped metadata value
74
- */
75
- function post_meta_prepare_value($meta_value) {
76
- return array($meta_value);
77
- }
78
 
79
  /**
80
- * Adds Metadata for a post to database
81
- * For internal methods
82
- * @see add_post_meta
83
- * @param $post_id
84
- * @param $meta_key
85
- * @param $meta_value
86
- * @param $unique
87
- * @return boolean Result of operation
88
  */
89
- function post_meta_add($post_id, $meta_key, $meta_value, $unique = false) {
90
- $meta_value = $this->post_meta_value_prepare($meta_value);
91
- return add_post_meta($post_id, $meta_key, $meta_value, $unique);
 
 
92
  }
93
 
94
- /**
95
- * Updates post metadata for internal data/methods
96
- * @see update_post_meta()
97
- * @param $post_id
98
- * @param $meta_key
99
- * @param $meta_value
100
- * @param $prev_value
101
- * @return boolean Result of operation
102
- */
103
- function post_meta_update($post_id, $meta_key, $meta_value, $prev_value = '') {
104
- $meta_value = $this->post_meta_prepare_value($meta_value);
105
- return update_post_meta($post_id, $meta_key, $meta_value, $prev_value);
106
- }
107
 
108
  /**
109
- * Builds postmeta key for custom data set by plugin
110
- * @param string $key Base key name
111
- * @return string Formatted postmeta key
112
  */
113
- function post_meta_get_key($key) {
114
- $sep = '_';
115
- if ( strpos($key, $sep . $this->prefix) !== 0 ) {
116
- $key_base = func_get_args();
117
- if ( !empty($key_base) ) {
118
- $key = array_merge((array)$this->prefix, $key_base);
119
- return $sep . implode($sep, $key);
120
- }
121
- }
122
-
123
- return $key;
124
  }
125
 
 
 
126
  /**
127
  * Retrieve class prefix (with separator if set)
128
  * @param bool|string $sep Separator to append to class prefix (Default: no separator)
129
  * @return string Class prefix
130
  */
131
- function get_prefix($sep = false) {
132
- $sep = ( is_string($sep) ) ? $sep : '';
133
- $prefix = ( !empty($this->prefix) ) ? $this->prefix . $sep : '';
134
- return $prefix;
 
 
 
 
 
 
 
 
 
135
  }
136
 
137
  /**
138
  * Prepend plugin prefix to some text
139
  * @param string $text Text to add to prefix
140
- * @param string $sep Text used to separate prefix and text
 
141
  * @return string Text with prefix prepended
142
  */
143
- function add_prefix($text = '', $sep = '_') {
144
- return $this->get_prefix($sep) . $text;
145
- }
146
-
147
- function remove_prefix($text = '', $sep = '_') {
148
- if ( !empty($text) && strpos($text, ( $p = $this->get_prefix($sep) )) === 0 )
149
- $text = substr($text, strlen($p));
150
- return $text;
151
  }
152
 
153
  /**
154
- * Creates a meta key for storing post meta data
155
- * Prefixes standard prefixed text with underscore to hide meta data on post edit forms
156
- * @param string $text Text to use as base of meta key
157
- * @return string Formatted meta key
 
 
 
158
  */
159
- function make_meta_key($text = '') {
160
- return '_' . $this->add_prefix($text);
 
 
161
  }
162
 
163
  /**
164
- * Returns Database prefix for Cornerstone-related DB Tables
165
- * @return string Database prefix
 
166
  */
167
- function get_db_prefix() {
168
- global $wpdb;
169
- return $wpdb->prefix . $this->get_prefix('_');
170
  }
171
  }
172
 
10
  */
11
  class SLB_Base {
12
 
13
+ /**
14
+ * Variable name of base object in global scope
15
+ * @var string
16
+ */
17
+ var $base = 'slb';
18
+
19
  /**
20
  * Prefix for Cornerstone-related data (attributes, DB tables, etc.)
21
  * @var string
39
  * Constructor
40
  */
41
  function __construct() {
42
+ $this->util =& new SLB_Utilities($this);
43
  }
44
 
45
+ /*-** Init **-*/
46
+
47
  /**
48
  * Default initialization method
49
  * To be overriden by child classes
50
  */
51
+ function init() {
52
+ $func = 'register_hooks';
53
+ if ( isset($this) && method_exists($this, $func) ) {
54
+ call_user_func($this->m($func));
55
+ }
 
 
 
 
56
  }
57
 
58
+ function register_hooks() {
59
+ //Activation
60
+ $func_activate = 'activate';
61
+ if ( method_exists($this, $func_activate) )
62
+ register_activation_hook($this->util->get_plugin_base_file(), $this->m($func_activate));
63
+ //Deactivation
64
+ $func_deactivate = 'deactivate';
65
+ if ( method_exists($this, $func_deactivate) )
66
+ register_deactivation_hook($this->util->get_plugin_base_file(), $this->m($func_deactivate));
 
 
 
 
 
67
  }
68
 
69
+ /*-** Reflection **-*/
 
 
 
 
 
 
 
70
 
71
  /**
72
+ * Retrieve base object
73
+ * @return object|bool Base object (FALSE if object does not exist)
 
 
 
 
 
 
74
  */
75
+ function &get_base() {
76
+ $base = false;
77
+ if ( isset($GLOBALS[$this->base]) )
78
+ $base =& $GLOBALS[$this->base];
79
+ return $base;
80
  }
81
 
82
+ /*-** Method/Function calling **-*/
 
 
 
 
 
 
 
 
 
 
 
 
83
 
84
  /**
85
+ * Returns callback to instance method
86
+ * @param string $method Method name
87
+ * @return array Callback array
88
  */
89
+ function &m($method) {
90
+ return $this->util->m($this, $method);
 
 
 
 
 
 
 
 
 
91
  }
92
 
93
+ /*-** Prefix **-*/
94
+
95
  /**
96
  * Retrieve class prefix (with separator if set)
97
  * @param bool|string $sep Separator to append to class prefix (Default: no separator)
98
  * @return string Class prefix
99
  */
100
+ function get_prefix($sep = null) {
101
+ $args = func_get_args();
102
+ return call_user_func_array($this->util->m($this->util, 'get_prefix'), $args);
103
+ }
104
+
105
+ /**
106
+ * Check if a string is prefixed
107
+ * @param string $text Text to check for prefix
108
+ * @param string $sep (optional) Separator used
109
+ */
110
+ function has_prefix($text, $sep = null) {
111
+ $args = func_get_args();
112
+ return call_user_func_array($this->util->m($this->util, 'has_prefix'), $args);
113
  }
114
 
115
  /**
116
  * Prepend plugin prefix to some text
117
  * @param string $text Text to add to prefix
118
+ * @param string $sep (optional) Text used to separate prefix and text
119
+ * @param bool $once (optional) Whether to add prefix to text that already contains a prefix or not
120
  * @return string Text with prefix prepended
121
  */
122
+ function add_prefix($text, $sep = null, $once = true) {
123
+ $args = func_get_args();
124
+ return call_user_func_array($this->util->m($this->util, 'add_prefix'), $args);
 
 
 
 
 
125
  }
126
 
127
  /**
128
+ * Add prefix to variable reference
129
+ * Updates actual variable rather than return value
130
+ * @uses SLB_Utilities::add_prefix_ref();
131
+ * @param string $var Variable to add prefix to
132
+ * @param string $sep (optional) Separator text
133
+ * @param bool $once (optional) Add prefix only once
134
+ * @return void
135
  */
136
+ function add_prefix_ref(&$var, $sep = null, $once = true) {
137
+ $args = func_get_args();
138
+ $args[0] =& $var;
139
+ call_user_func_array($this->util->m($this->util, 'add_prefix_ref'), $args);
140
  }
141
 
142
  /**
143
+ * Remove prefix from specified string
144
+ * @param string $text String to remove prefix from
145
+ * @param string $sep (optional) Separator used with prefix
146
  */
147
+ function remove_prefix($text, $sep = null) {
148
+ $args = func_get_args();
149
+ return call_user_func_array($this->util->m($this->util, 'remove_prefix'), $args);
150
  }
151
  }
152
 
includes/class.fields.php ADDED
@@ -0,0 +1,2251 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once 'class.base.php';
4
+
5
+ /**
6
+ * Fields - Base class
7
+ * Core properties/methods for Content Type derivative classes
8
+ * @package Simple Lightbox
9
+ * @subpackage Fields
10
+ * @author SM
11
+ */
12
+ class SLB_Field_Base extends SLB_Base {
13
+ /**
14
+ * @var string Unique name
15
+ */
16
+ var $id = '';
17
+
18
+ /**
19
+ * ID formatting options
20
+ * Merged with defaults during initialization
21
+ * @see $id_formats_default
22
+ * @var array
23
+ */
24
+ var $id_formats = null;
25
+
26
+ /**
27
+ * Default ID Formatting options
28
+ * Structure:
29
+ * > Key (string): Format name
30
+ * > Val (array): Options
31
+ * @var array
32
+ */
33
+ var $id_formats_default = array(
34
+ 'attr_id' => array(
35
+ 'wrap' => array('open' => '_', 'segment_open' => '_'),
36
+ 'prefix' => array('get_container', 'get_id', 'add_prefix'),
37
+ 'recursive' => true
38
+ ),
39
+ 'attr_name' => array(
40
+ 'wrap' => array('open' => '[', 'close' => ']', 'segment_open' => '[', 'segment_close' => ']'),
41
+ 'recursive' => true,
42
+ 'prefix' => array('get_container', 'get_id', 'add_prefix')
43
+ )
44
+ );
45
+
46
+ /**
47
+ * Reference to parent object that current instance inherits from
48
+ * @var object
49
+ */
50
+ var $parent = null;
51
+
52
+ /**
53
+ * Title
54
+ * @var string
55
+ */
56
+ var $title = '';
57
+
58
+ /**
59
+ * @var string Short description
60
+ */
61
+ var $description = '';
62
+
63
+ /**
64
+ * @var array Object Properties
65
+ */
66
+ var $properties = array();
67
+
68
+ /**
69
+ * Initialization properties
70
+ * @var array
71
+ */
72
+ var $properties_init = null;
73
+
74
+ /**
75
+ * Structure: Property names stored as keys in group
76
+ * Root
77
+ * -> Group Name
78
+ * -> Property Name => Null
79
+ * Reason: Faster searching over large arrays
80
+ * @var array Groupings of Properties
81
+ */
82
+ var $property_groups = array();
83
+
84
+ /**
85
+ * Keys to filter out of properties array before setting properties
86
+ * @var array
87
+ */
88
+ var $property_filter = array('group');
89
+
90
+ /**
91
+ * Data for object
92
+ * May also contain data for nested objects
93
+ * @var mixed
94
+ */
95
+ var $data = null;
96
+
97
+ /**
98
+ * Whether data has been fetched or not
99
+ * @var bool
100
+ */
101
+ var $data_fetched = false;
102
+
103
+ /**
104
+ * @var array Script resources to include for object
105
+ */
106
+ var $scripts = array();
107
+
108
+ /**
109
+ * @var array CSS style resources to include for object
110
+ */
111
+ var $styles = array();
112
+
113
+ /**
114
+ * Hooks (Filters/Actions) for object
115
+ * @var array
116
+ */
117
+ var $hooks = array();
118
+
119
+ /**
120
+ * Mapping of child properties to parent members
121
+ * Allows more flexibility when creating new instances of child objects using property arrays
122
+ * Associative array structure:
123
+ * > Key: Child property to map FROM
124
+ * > Val: Parent property to map TO
125
+ * @var array
126
+ */
127
+ var $map = null;
128
+
129
+ /**
130
+ * Legacy Constructor
131
+ */
132
+ function SLB_Field_Base($id = '', $parent = null) {
133
+ $args = func_get_args();
134
+ call_user_func_array(array(&$this, '__construct'), $args);
135
+ }
136
+
137
+ /**
138
+ * Constructor
139
+ */
140
+ function __construct($id = '', $parent = null) {
141
+ parent::__construct();
142
+ //Normalize Properties
143
+ $args = func_get_args();
144
+ if ( func_num_args() > 1 && empty($parent) ) {
145
+ unset($args[1]);
146
+ $args = array_values($args);
147
+ }
148
+ $properties = $this->make_properties($this->util->func_get_options($args), array('id' => $id, 'parent' => $parent));
149
+ //Remove empty variables
150
+ if ( empty($properties['parent']) )
151
+ unset($properties['parent']);
152
+ //Save init properties
153
+ $this->properties_init = $properties;
154
+ //Set Properties
155
+ $this->set_properties($properties);
156
+ }
157
+
158
+ /* Getters/Setters */
159
+
160
+ /**
161
+ * Checks if the specified path exists in the object
162
+ * @param array $path Path to check for
163
+ * @return bool TRUE if path exists in object, FALSE otherwise
164
+ */
165
+ function path_isset($path = '') {
166
+ //Stop execution if no path is supplied
167
+ if ( empty($path) )
168
+ return false;
169
+ $args = func_get_args();
170
+ $path = $this->util->build_path($args);
171
+ $item =& $this;
172
+ //Iterate over path and check if each level exists before moving on to the next
173
+ for ($x = 0; $x < count($path); $x++) {
174
+ if ( $this->util->property_exists($item, $path[$x]) ) {
175
+ //Set $item as reference to next level in path for next iteration
176
+ $item =& $this->util->get_property($item, $path[$x]);
177
+ //$item =& $item[ $path[$x] ];
178
+ } else {
179
+ return false;
180
+ }
181
+ }
182
+ return true;
183
+ }
184
+
185
+ /**
186
+ * Retrieves a value from object using a specified path
187
+ * Checks to make sure path exists in object before retrieving value
188
+ * @param array $path Path to retrieve value from. Each item in array is a deeper dimension
189
+ * @return mixed Value at specified path
190
+ */
191
+ function &get_path_value($path = '') {
192
+ $ret = '';
193
+ $path = $this->util->build_path(func_get_args());
194
+ if ( $this->path_isset($path) ) {
195
+ $ret =& $this;
196
+ for ($x = 0; $x < count($path); $x++) {
197
+ if ( 0 == $x )
198
+ $ret =& $ret->{ $path[$x] };
199
+ else
200
+ $ret =& $ret[ $path[$x] ];
201
+ }
202
+ }
203
+ return $ret;
204
+ }
205
+
206
+ /**
207
+ * Search for specified member value in field type ancestors
208
+ * @param string $member Name of object member to search (e.g. properties, layout, etc.)
209
+ * @param string $name Value to retrieve from member
210
+ * @return mixed Member value if found (Default: empty string)
211
+ */
212
+ function get_parent_value($member, $name = '', $default = '') {
213
+ $parent =& $this->get_parent();
214
+ return $this->get_object_value($parent, $member, $name, $default, 'parent');
215
+ }
216
+
217
+ /**
218
+ * Retrieves specified member value
219
+ * Handles inherited values
220
+ * Merging corresponding parents if value is an array (e.g. for property groups)
221
+ * @param string|array $member Member to search. May also contain a path to the desired member
222
+ * @param string $name Value to retrieve from member
223
+ * @param mixed $default Default value if no value found (Default: empty string)
224
+ * @param string $dir Direction to move through hierarchy to find value
225
+ * Possible Values:
226
+ * parent (default) - Search through field parents
227
+ * current - Do not search through connected objects
228
+ * container - Search through field containers
229
+ * caller - Search through field callers
230
+ * @return mixed Specified member value
231
+ * @todo Return reference
232
+ */
233
+ function &get_member_value($member, $name = '', $default = '', $dir = 'parent') {
234
+ //Check if path to member is supplied
235
+ $path = array();
236
+ if ( is_array($member) && isset($member['tag']) ) {
237
+ if ( isset($member['attributes']['ref_base']) ) {
238
+ if ( 'root' != $member['attributes']['ref_base'] )
239
+ $path[] = $member['attributes']['ref_base'];
240
+ } else {
241
+ $path[] = 'properties';
242
+ }
243
+
244
+ $path[] = $member['tag'];
245
+ } else {
246
+ $path = $member;
247
+ }
248
+
249
+ $path = $this->util->build_path($path, $name);
250
+ //Set defaults and prepare data
251
+ $val = $default;
252
+ $inherit = false;
253
+ $inherit_tag = '{inherit}';
254
+
255
+ /* Determine whether the value must be retrieved from a parent/container object
256
+ * Conditions:
257
+ * > Path does not exist in current field
258
+ * > Path exists and is not an object, but at least one of the following is true:
259
+ * > Value at path is an array (e.g. properties, elements, etc. array)
260
+ * > Parent/container values should be merged with retrieved array
261
+ * > Value at path is a string that inherits from another field
262
+ * > Value from other field will be retrieved and will replace inheritance placeholder in retrieved value
263
+ */
264
+
265
+ $deeper = false;
266
+
267
+ if ( !$this->path_isset($path) )
268
+ $deeper = true;
269
+ else {
270
+ $val = $this->get_path_value($path);
271
+ if ( !is_object($val) && ( is_array($val) || ($inherit = strpos($val, $inherit_tag)) !== false ) )
272
+ $deeper = true;
273
+ else
274
+ $deeper = false;
275
+ }
276
+ if ( $deeper && 'current' != $dir ) {
277
+ //Get Parent value (recursive)
278
+ $ex_val = ( 'parent' != $dir ) ? $this->get_container_value($member, $name, $default) : $this->get_parent_value($member, $name, $default);
279
+ //Handle inheritance
280
+ if ( is_array($val) ) {
281
+ //Combine Arrays
282
+ if ( is_array($ex_val) )
283
+ $val = array_merge($ex_val, $val);
284
+ } elseif ( $inherit !== false ) {
285
+ //Replace placeholder with inherited string
286
+ $val = str_replace($inherit_tag, $ex_val, $val);
287
+ } else {
288
+ //Default: Set parent value as value
289
+ $val = $ex_val;
290
+ }
291
+ }
292
+
293
+ return $val;
294
+ }
295
+
296
+ /**
297
+ * Search for specified member value in an object
298
+ * @param object $object Reference to object to retrieve value from
299
+ * @param string $member Name of object member to search (e.g. properties, layout, etc.)
300
+ * @param string $name (optional) Value to retrieve from member
301
+ * @param mixed $default (optional) Default value to use if no value found (Default: empty string)
302
+ * @param string $dir Direction to move through hierarchy to find value @see SLB_Field_Type::get_member_value() for possible values
303
+ * @return mixed Member value if found (Default: $default)
304
+ */
305
+ function get_object_value(&$object, $member, $name = '', $default = '', $dir = 'parent') {
306
+ $ret = $default;
307
+ if ( is_object($object) && method_exists($object, 'get_member_value') )
308
+ $ret = $object->get_member_value($member, $name, $default, $dir);
309
+ return $ret;
310
+ }
311
+
312
+ /**
313
+ * Set item ID
314
+ * @param string $id Unique item ID
315
+ */
316
+ function set_id($id) {
317
+ if ( empty($id) || !is_string($id) )
318
+ return false;
319
+ $this->id = trim($id);
320
+ }
321
+
322
+ /**
323
+ * Retrieves field ID
324
+ * @param array|string $options (optional) Options or ID of format to use
325
+ * @return string item ID
326
+ */
327
+ function get_id($options = array()) {
328
+ $item_id = trim($this->id);
329
+
330
+ $formats = $this->get_id_formats();
331
+
332
+ //Setup options
333
+ $wrap_default = array('open' => '', 'close' => '', 'segment_open' => '', 'segment_close' => '');
334
+
335
+ $options_default = array(
336
+ 'format' => null,
337
+ 'wrap' => array(),
338
+ 'segments_pre' => null,
339
+ 'prefix' => '',
340
+ 'recursive' => false
341
+ );
342
+
343
+ //Load options based on format
344
+ if ( !is_array($options) )
345
+ $options = array('format' => $options);
346
+ if ( isset($options['format']) && is_string($options['format']) && isset($formats[$options['format']]) )
347
+ $options_default = wp_parse_args($formats[$options['format']], $options_default);
348
+ else
349
+ unset($options['format']);
350
+ $options = wp_parse_args($options, $options_default);
351
+ //Import options into function
352
+ extract($options);
353
+
354
+ //Validate options
355
+ $wrap = wp_parse_args($wrap, $wrap_default);
356
+
357
+ if ( !is_array($segments_pre) )
358
+ $segments_pre = array($segments_pre);
359
+ $segments_pre = array_reverse($segments_pre);
360
+
361
+ //Format ID based on options
362
+
363
+ $item_id = array($item_id);
364
+
365
+ //Add parent objects to ID
366
+ if ( !!$recursive ) {
367
+ //Create array of ID components
368
+ $c = $this->get_caller();
369
+ while ( !!$c ) {
370
+ //Add ID of current caller to array
371
+ if ( method_exists($c, 'get_id') && ( $itemp = $c->get_id() ) && !empty($itemp) )
372
+ $item_id = $itemp;
373
+ //Get parent object
374
+ $c = ( method_exists($c, 'get_caller') ) ? $c->get_caller() : null;
375
+ $itemp = '';
376
+ }
377
+ unset($c);
378
+ }
379
+
380
+ //Additional segments (Pre)
381
+ foreach ( $segments_pre as $seg ) {
382
+ if ( is_null($seg) )
383
+ continue;
384
+ if ( is_object($seg) )
385
+ $seg = (array)$seg;
386
+ if ( is_array($seg) )
387
+ $item_id = array_merge($item_id, array_reverse($seg));
388
+ elseif ( '' != strval($seg) )
389
+ $item_id[] = strval($seg);
390
+ }
391
+
392
+ //Prefix
393
+ if ( is_array($prefix) ) {
394
+ //Array is sequence of instance methods to call on object
395
+ //Last array member can be an array of parameters to pass to methods
396
+ $count = count($prefix);
397
+ $args = ( $count > 1 && is_array($prefix[$count - 1]) ) ? array_pop($prefix) : array();
398
+ $p = $this;
399
+ $val = '';
400
+ //Iterate through methods
401
+ foreach ( $prefix as $m ) {
402
+ //Build callback
403
+ $m = $this->util->m($p, $m);
404
+ //Call callback
405
+ if ( is_callable($m) )
406
+ $val = call_user_func_array($m, $args);
407
+ //Process returned value
408
+ if ( is_object($val) )
409
+ $p = $val; //Use returned object in next round
410
+ else
411
+ array_unshift($args, $val); //Pass returned value as parameter to next method on using current object
412
+ }
413
+ $prefix = $val;
414
+ unset($p, $val);
415
+ }
416
+ if ( is_numeric($prefix) )
417
+ $prefix = strval($prefix);
418
+ if ( empty($prefix) || !is_string($prefix) )
419
+ $prefix = '';
420
+
421
+ //Convert array to string
422
+ $item_id = $prefix . $wrap['open'] . implode($wrap['segment_close'] . $wrap['segment_open'], array_reverse($item_id)) . $wrap['close'];
423
+ return $item_id;
424
+ }
425
+
426
+ /**
427
+ * Retrieve ID formatting options for class
428
+ * Format options arrays are merged together and saved to $id_formats
429
+ * @uses $id_formats
430
+ * @uses $id_formats_default
431
+ * @return array ID Formatting options
432
+ */
433
+ function &get_id_formats() {
434
+ if ( is_null($this->id_formats) ) {
435
+ $this->id_formats = wp_parse_args($this->id_formats, $this->id_formats_default);
436
+ }
437
+ return $this->id_formats;
438
+ }
439
+
440
+ /**
441
+ * Retrieve value from data member
442
+ * @param string $context Context to format data for
443
+ * @param bool $top (optional) Whether to traverse through the field hierarchy to get data for field (Default: TRUE)
444
+ * @return mixed Value at specified path
445
+ */
446
+ function get_data($context = '', $top = true) {
447
+ $opt_d = array('context' => '', 'top' => true);
448
+ $args = func_get_args();
449
+ $a = false;
450
+ if ( count($args) == 1 && is_array($args[0]) && !empty($args[0]) ) {
451
+ $a = true;
452
+ $args = wp_parse_args($args[0], $opt_d);
453
+ extract($args);
454
+ }
455
+
456
+ if ( is_string($top) ) {
457
+ if ( 'false' == $top )
458
+ $top = false;
459
+ elseif ( 'true' == $top )
460
+ $top = true;
461
+ elseif ( is_numeric($top) )
462
+ $top = intval($top);
463
+ }
464
+ $top = !!$top;
465
+ $obj =& $this;
466
+ $obj_path = array(&$this);
467
+ $path = array();
468
+ if ( $top ) {
469
+ //Iterate through hiearchy to get top-most object
470
+ while ( !empty($obj) ) {
471
+ $new = null;
472
+ //Try to get caller first
473
+ if ( method_exists($obj, 'get_caller') ) {
474
+ $checked = true;
475
+ $new =& $obj->get_caller();
476
+ }
477
+ //Try to get container if no caller found
478
+ if ( empty($new) && method_exists($obj, 'get_container') ) {
479
+ $checked = true;
480
+ $new =& $obj->get_container();
481
+ //Load data
482
+ if ( method_exists($new, 'load_data') ) {
483
+ $new->load_data();
484
+ }
485
+ }
486
+
487
+ $obj =& $new;
488
+ unset($new);
489
+ //Stop iteration
490
+ if ( !empty($obj) ) {
491
+ //Add object to path if it is valid
492
+ $obj_path[] =& $obj;
493
+ }
494
+ }
495
+ unset($obj);
496
+ }
497
+
498
+ //Check each object (starting with top-most) for matching data for current field
499
+
500
+ //Reverse array
501
+ $obj_path = array_reverse($obj_path);
502
+ //Build path for data location
503
+ foreach ( $obj_path as $obj ) {
504
+ if ( method_exists($obj, 'get_id') )
505
+ $path[] = $obj->get_id();
506
+ }
507
+ //Iterate through objects
508
+ while ( !empty($obj_path) ) {
509
+ //Get next object
510
+ $obj =& array_shift($obj_path);
511
+ //Shorten path
512
+ array_shift($path);
513
+ //Check for value in object and stop iteration if matching data found
514
+ $val = $this->get_object_value($obj, 'data', $path, null, 'current');
515
+ if ( !is_null($val) ) {
516
+ break;
517
+ }
518
+ }
519
+ return $this->format($val, $context);
520
+ }
521
+
522
+ /**
523
+ * Sets value in data member
524
+ * Sets value to data member itself by default
525
+ * @param mixed $value Value to set
526
+ * @param string|array $name Name of value to set (Can also be path to value)
527
+ */
528
+ function set_data($value, $name = '') {
529
+ $ref =& $this->get_path_value('data', $name);
530
+ $ref = $value;
531
+ }
532
+
533
+ /**
534
+ * Sets parent object of current instance
535
+ * Parent objects must be the same object type as current instance
536
+ * @uses SLB to get field type definition
537
+ * @uses SLB_Fields::has() to check if field type exists
538
+ * @uses SLB_Fields::get() to retrieve field type object reference
539
+ * @param string|object $parent Parent ID or reference
540
+ */
541
+ function set_parent($parent = null) {
542
+ //Stop processing if parent empty
543
+ if ( empty($parent) && !is_string($this->parent) )
544
+ return false;
545
+ //Parent passed as object reference wrapped in array
546
+ if ( is_array($parent) && isset($parent[0]) && is_object($parent[0]) )
547
+ $parent =& $parent[0];
548
+
549
+ //No parent set but parent ID (previously) set in object
550
+ if ( empty($parent) && is_string($this->parent) )
551
+ $parent = $this->parent;
552
+
553
+ //Retrieve reference object if ID was supplied
554
+ if ( is_string($parent) ) {
555
+ $parent = trim($parent);
556
+ //Get parent object reference
557
+ /**
558
+ * @var SLB
559
+ */
560
+ $b =& $this->get_base();
561
+ if ( $b && $b->fields->has($parent) ) {
562
+ $parent =& $b->fields->get($parent);
563
+ }
564
+ }
565
+
566
+ //Set parent value on object
567
+ if ( is_string($parent) || is_object($parent) )
568
+ $this->parent =& $parent;
569
+ }
570
+
571
+ /**
572
+ * Retrieve field type parent
573
+ * @return SLB_Field_Type Reference to parent field
574
+ */
575
+ function &get_parent() {
576
+ return $this->parent;
577
+ }
578
+
579
+ /**
580
+ * Set object title
581
+ * @param string $title Title for object
582
+ * @param string $plural Plural form of title
583
+ */
584
+ function set_title($title = '') {
585
+ $this->title = strip_tags(trim($title));
586
+ }
587
+
588
+ /**
589
+ * Retrieve object title
590
+ */
591
+ function get_title() {
592
+ return $this->get_member_value('title', '','', 'current');
593
+ }
594
+
595
+ /**
596
+ * Set object description
597
+ * @param string $description Description for object
598
+ */
599
+ function set_description($description = '') {
600
+ $this->description = strip_tags(trim($description));
601
+ }
602
+
603
+ /**
604
+ * Retrieve object description
605
+ * @return string Object description
606
+ */
607
+ function get_description() {
608
+ $dir = 'current';
609
+ return $this->get_member_value('description', '','', $dir);
610
+ return $desc;
611
+ }
612
+
613
+ /**
614
+ * Sets multiple properties on field type at once
615
+ * @param array $properties Properties. Each element is an array containing the arguments to set a new property
616
+ * @return boolean TRUE if successful, FALSE otherwise
617
+ */
618
+ function set_properties($properties) {
619
+ if ( !is_array($properties) )
620
+ return false;
621
+
622
+ //Set Member properties
623
+ foreach ( $properties as $prop => $val ) {
624
+ if ( ( $m = 'set_' . $prop ) && method_exists($this, $m) ) {
625
+ $this->{$m}($val);
626
+ //Remove member property from array
627
+ unset($properties[$prop]);
628
+ }
629
+ }
630
+
631
+ //Filter properties
632
+ $properties = $this->filter_properties($properties);
633
+
634
+ //Set additional instance properties
635
+ foreach ( $properties as $name => $val) {
636
+ $this->set_property($name, $val);
637
+ }
638
+ }
639
+
640
+ /**
641
+ * Remap properties based on $map
642
+ * @uses $map For determine how child properties should map to parent properties
643
+ * @uses SLB_Utlities::array_remap() to perform array remapping
644
+ * @param array $properties Associative array of properties
645
+ * @return array Remapped properties
646
+ */
647
+ function remap_properties($properties) {
648
+ //Return remapped properties
649
+ return $this->util->array_remap($properties, $this->map);
650
+ }
651
+
652
+ /**
653
+ * Build properties array
654
+ * Accepts a variable number of additional arrays of default properties
655
+ * that will be merged in order from last to first
656
+ * (e.g. first array overwrites duplicate members in last)
657
+ * @uses SLB_Field_Base::remap() to remap properties members if necessary
658
+ * @param array $props Instance properties
659
+ * @param array $defaults Default properties
660
+ * @return array Normalized properties
661
+ */
662
+ function make_properties($props, $defaults = array()) {
663
+ $args = func_get_args();
664
+ $args = array_reverse($args);
665
+ $props = array();
666
+ foreach ( $args as $arg ) {
667
+ $props = wp_parse_args($arg, $props);
668
+ }
669
+ return $this->remap_properties($props);
670
+ }
671
+
672
+ /**
673
+ * Filter property members
674
+ * @uses $property_filter to remove define members to remove from $properties
675
+ * @param array $props Properties
676
+ * @return array Filtered properties
677
+ */
678
+ function filter_properties($props = array()) {
679
+ return $this->util->array_filter_keys($props, $this->property_filter);
680
+ }
681
+
682
+ /**
683
+ * Add/Set a property on the field definition
684
+ * @param string $name Name of property
685
+ * @param mixed $value Default value for property
686
+ * @param string|array $group Group(s) property belongs to
687
+ * @return boolean TRUE if property is successfully added to field type, FALSE otherwise
688
+ */
689
+ function set_property($name, $value = '', $group = null) {
690
+ //Do not add if property name is not a string
691
+ if ( !is_string($name) )
692
+ return false;
693
+ //Create property array
694
+ $prop_arr = array();
695
+ $prop_arr['value'] = $value;
696
+ //Add to properties array
697
+ $this->properties[$name] = $value;
698
+ //Add property to specified groups
699
+ if ( !empty($group) ) {
700
+ $this->set_group_property($group, $name);
701
+ }
702
+ return true;
703
+ }
704
+
705
+ /**
706
+ * Retreives property from field type
707
+ * @param string $name Name of property to retrieve
708
+ * @return mixed Specified Property if exists (Default: Empty string)
709
+ */
710
+ function get_property($name) {
711
+ $val = $this->get_member_value('properties', $name);
712
+ return $val;
713
+ }
714
+
715
+ /**
716
+ * Removes a property from item
717
+ * @param string $name Property ID
718
+ */
719
+ function remove_property($name) {
720
+ //Remove property
721
+ if ( isset($this->properties[$name]) )
722
+ unset($this->properties[$name]);
723
+ //Remove from group
724
+ foreach ( array_keys($this->property_groups) as $g ) {
725
+ if ( isset($this->property_groups[$g][$name]) ) {
726
+ unset($this->property_groups[$g][$name]);
727
+ break;
728
+ }
729
+ }
730
+ }
731
+
732
+ /**
733
+ * Adds Specified Property to a Group
734
+ * @param string|array $group Group(s) to add property to
735
+ * @param string $property Property to add to group
736
+ */
737
+ function set_group_property($group, $property) {
738
+ if ( is_string($group) && isset($this->property_groups[$group][$property]) )
739
+ return;
740
+ if ( !is_array($group) ) {
741
+ $group = array($group);
742
+ }
743
+
744
+ foreach ($group as $g) {
745
+ $g = trim($g);
746
+ //Initialize group if it doesn't already exist
747
+ if ( !isset($this->property_groups[$g]) )
748
+ $this->property_groups[$g] = array();
749
+
750
+ //Add property to group
751
+ $this->property_groups[$g][$property] = null;
752
+ }
753
+ }
754
+
755
+ /**
756
+ * Retrieve property group
757
+ * @param string $group Group to retrieve
758
+ * @return array Array of properties in specified group
759
+ */
760
+ function get_group($group) {
761
+ return $this->get_member_value('property_groups', $group, array());
762
+ }
763
+
764
+ /**
765
+ * Save field data
766
+ * Child classes will define their own
767
+ * functionality for this method
768
+ * @return bool TRUE if save was successful (FALSE otherwise)
769
+ */
770
+ function save() {
771
+ return true;
772
+ }
773
+
774
+ /*-** Hooks **-*/
775
+
776
+ /**
777
+ * Retrieve hooks added to object
778
+ * @return array Hooks
779
+ */
780
+ function get_hooks() {
781
+ return $this->get_member_value('hooks', '', array());
782
+ }
783
+
784
+ /**
785
+ * Add hook for object
786
+ * @see add_filter() for parameter defaults
787
+ * @param $tag
788
+ * @param $function_to_add
789
+ * @param $priority
790
+ * @param $accepted_args
791
+ */
792
+ function add_hook($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
793
+ //Create new array for tag (if not already set)
794
+ if ( !isset($this->hooks[$tag]) )
795
+ $this->hooks[$tag] = array();
796
+ //Build Unique ID
797
+ if ( is_string($function_to_add) )
798
+ $id = $function_to_add;
799
+ elseif ( is_array($function_to_add) && !empty($function_to_add) )
800
+ $id = strval($function_to_add[count($function_to_add) - 1]);
801
+ else
802
+ $id = 'function_' . ( count($this->hooks[$tag]) + 1 );
803
+ //Add hook
804
+ $this->hooks[$tag][$id] = func_get_args();
805
+ }
806
+
807
+ /**
808
+ * Convenience method for adding an action for object
809
+ * @see add_filter() for parameter defaults
810
+ * @param $tag
811
+ * @param $function_to_add
812
+ * @param $priority
813
+ * @param $accepted_args
814
+ */
815
+ function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
816
+ $this->add_hook($tag, $function_to_add, $priority, $accepted_args);
817
+ }
818
+
819
+ /**
820
+ * Convenience method for adding a filter for object
821
+ * @see add_filter() for parameter defaults
822
+ * @param $tag
823
+ * @param $function_to_add
824
+ * @param $priority
825
+ * @param $accepted_args
826
+ */
827
+ function add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
828
+ $this->add_hook($tag, $function_to_add, $priority, $accepted_args);
829
+ }
830
+
831
+ /*-** Dependencies **-*/
832
+
833
+ /**
834
+ * Adds dependency to object
835
+ * @param string $type Type of dependency to add (script, style)
836
+ * @param array|string $context When dependency will be added (@see SLB_Utilities::get_action() for possible contexts)
837
+ * @see wp_enqueue_script for the following of the parameters
838
+ * @param $handle
839
+ * @param $src
840
+ * @param $deps
841
+ * @param $ver
842
+ * @param $ex
843
+ */
844
+ function add_dependency($type, $context, $handle, $src = false, $deps = array(), $ver = false, $ex = false) {
845
+ $args = func_get_args();
846
+ //Remove type/context from arguments
847
+ $args = array_slice($args, 2);
848
+
849
+ //Set context
850
+ if ( !is_array($context) ) {
851
+ //Wrap single contexts in an array
852
+ if ( is_string($context) )
853
+ $context = array($context);
854
+ else
855
+ $context = array();
856
+ }
857
+ //Add file to instance property
858
+ if ( isset($this->{$type}) && is_array($this->{$type}) )
859
+ $this->{$type}[$handle] = array('context' => $context, 'params' => $args);
860
+ }
861
+
862
+ /**
863
+ * Add script to object to be added in specified contexts
864
+ * @param array|string $context Array of contexts to add script to page
865
+ * @see wp_enqueue_script for the following of the parameters
866
+ * @param $handle
867
+ * @param $src
868
+ * @param $deps
869
+ * @param $ver
870
+ * @param $in_footer
871
+ */
872
+ function add_script( $context, $handle, $src = false, $deps = array(), $ver = false, $in_footer = false ) {
873
+ $args = func_get_args();
874
+ //Add file type to front of arguments array
875
+ array_unshift($args, 'scripts');
876
+ call_user_func_array($this->m('add_dependency'), $args);
877
+ }
878
+
879
+ /**
880
+ * Retrieve script dependencies for object
881
+ * @return array Script dependencies
882
+ */
883
+ function get_scripts() {
884
+ return $this->get_member_value('scripts', '', array());
885
+ }
886
+
887
+ /**
888
+ * Add style to object to be added in specified contexts
889
+ * @param array|string $context Array of contexts to add style to page
890
+ * @see wp_enqueue_style for the following of the parameters
891
+ * @param $handle
892
+ * @param $src
893
+ * @param $deps
894
+ * @param $ver
895
+ * @param $in_footer
896
+ */
897
+ function add_style( $handle, $src = false, $deps = array(), $ver = false, $media = false ) {
898
+ $args = func_get_args();
899
+ array_unshift($args, 'styles');
900
+ call_user_func_array($this->m('add_dependency'), $args);
901
+ }
902
+
903
+ /**
904
+ * Retrieve Style dependencies for object
905
+ * @return array Style dependencies
906
+ */
907
+ function get_styles() {
908
+ return $this->get_member_value('styles', '', array());
909
+ }
910
+
911
+ /* Helpers */
912
+
913
+ /**
914
+ * Format value based on specified context
915
+ * @param mixed $value Value to format
916
+ * @param string $context Current context
917
+ * @return mixed Formatted value
918
+ */
919
+ function format($value, $context = '') {
920
+ $handler = 'format_' . trim(strval($context));
921
+ //Only process if context is valid and has a handler
922
+ if ( !empty($context) && method_exists($this, $handler) ) {
923
+ //Pass value to handler
924
+ $value = $this->{$handler}($value, $context);
925
+ }
926
+ //Return formatted value
927
+ return $value;
928
+ }
929
+
930
+ /**
931
+ * Format value for output in form field
932
+ * @param mixed $value Value to format
933
+ * @return mixed Formatted value
934
+ */
935
+ function format_form($value) {
936
+ if ( is_string($value) )
937
+ $value = htmlspecialchars($value);
938
+ return $value;
939
+ }
940
+ }
941
+
942
+ /**
943
+ * Field Types
944
+ * Stores properties for a specific field
945
+ * @package Simple Lightbox
946
+ * @subpackage Fields
947
+ * @author SM
948
+ */
949
+ class SLB_Field_Type extends SLB_Field_Base {
950
+ /* Properties */
951
+
952
+ /**
953
+ * @var array Array of Field types that make up current Field type
954
+ */
955
+ var $elements = array();
956
+
957
+ /**
958
+ * @var array Field type layouts
959
+ */
960
+ var $layout = array();
961
+
962
+ /**
963
+ * @var SLB_Field_Type Parent field type (reference)
964
+ */
965
+ var $parent = null;
966
+
967
+ /**
968
+ * Object that field is in
969
+ * @var SLB_Field|SLB_Field_Type|SLB_Field_Collection
970
+ */
971
+ var $container = null;
972
+
973
+ /**
974
+ * Object that called field
975
+ * Used to determine field hierarchy/nesting
976
+ * @var SLB_Field|SLB_Field_Type|SLB_Field_Collection
977
+ */
978
+ var $caller = null;
979
+
980
+ /**
981
+ * Legacy Constructor
982
+ */
983
+ function SLB_Field_Type($id = '', $parent = null) {
984
+ $args = func_get_args();
985
+ call_user_func_array(array(&$this, '__construct'), $args);
986
+ }
987
+
988
+ function __construct($id = '', $parent = null) {
989
+ parent::__construct($id, $parent);
990
+ }
991
+
992
+ /* Getters/Setters */
993
+
994
+ /**
995
+ * Search for specified member value in field's container object (if exists)
996
+ * @param string $member Name of object member to search (e.g. properties, layout, etc.)
997
+ * @param string $name Value to retrieve from member
998
+ * @return mixed Member value if found (Default: empty string)
999
+ */
1000
+ function get_container_value($member, $name = '', $default = '') {
1001
+ $container =& $this->get_container();
1002
+ return $this->get_object_value($container, $member, $name, $default, 'container');
1003
+ }
1004
+
1005
+ /**
1006
+ * Search for specified member value in field's container object (if exists)
1007
+ * @param string $member Name of object member to search (e.g. properties, layout, etc.)
1008
+ * @param string $name Value to retrieve from member
1009
+ * @return mixed Member value if found (Default: empty string)
1010
+ */
1011
+ function get_caller_value($member, $name = '', $default = '') {
1012
+ $caller =& $this->get_caller();
1013
+ return $this->get_object_value($caller, $member, $name, $default, 'caller');
1014
+ }
1015
+
1016
+ /**
1017
+ * Sets reference to container object of current field
1018
+ * Reference is cleared if no valid object is passed to method
1019
+ * @param object $container
1020
+ */
1021
+ function set_container(&$container) {
1022
+ if ( !empty($container) && is_object($container) ) {
1023
+ //Set as param as container for current field
1024
+ $this->container =& $container;
1025
+ } else {
1026
+ //Clear container member if argument is invalid
1027
+ $this->clear_container();
1028
+ }
1029
+ }
1030
+
1031
+ /**
1032
+ * Clears reference to container object of current field
1033
+ */
1034
+ function clear_container() {
1035
+ $this->container = null;
1036
+ }
1037
+
1038
+ /**
1039
+ * Retrieves reference to container object of current field
1040
+ * @return object Reference to container object
1041
+ */
1042
+ function &get_container() {
1043
+ $ret = null;
1044
+ if ( $this->has_container() )
1045
+ $ret =& $this->container;
1046
+ return $ret;
1047
+ }
1048
+
1049
+ /**
1050
+ * Checks if field has a container reference
1051
+ * @return bool TRUE if field is contained, FALSE otherwise
1052
+ */
1053
+ function has_container() {
1054
+ return !empty($this->container);
1055
+ }
1056
+
1057
+ /**
1058
+ * Sets reference to calling object of current field
1059
+ * Any existing reference is cleared if no valid object is passed to method
1060
+ * @param object $caller Calling object
1061
+ */
1062
+ function set_caller(&$caller) {
1063
+ if ( !empty($caller) && is_object($caller) )
1064
+ $this->caller =& $caller;
1065
+ else
1066
+ $this->clear_caller();
1067
+ }
1068
+
1069
+ /**
1070
+ * Clears reference to calling object of current field
1071
+ */
1072
+ function clear_caller() {
1073
+ unset($this->caller);
1074
+ }
1075
+
1076
+ /**
1077
+ * Retrieves reference to caller object of current field
1078
+ * @return object Reference to caller object
1079
+ */
1080
+ function &get_caller() {
1081
+ $ret = null;
1082
+ if ( $this->has_caller() )
1083
+ $ret =& $this->caller;
1084
+ return $ret;
1085
+ }
1086
+
1087
+ /**
1088
+ * Checks if field has a caller reference
1089
+ * @return bool TRUE if field is called by another field, FALSE otherwise
1090
+ */
1091
+ function has_caller() {
1092
+ return !empty($this->caller);
1093
+ }
1094
+
1095
+
1096
+
1097
+ /**
1098
+ * Sets an element for the field type
1099
+ * @param string $name Name of element
1100
+ * @param SLB_Field_Type $type Reference of field type to use for element
1101
+ * @param array $properties Properties for element (passed as keyed associative array)
1102
+ * @param string $id_prop Name of property to set $name to (e.g. ID, etc.)
1103
+ */
1104
+ function set_element($name, $type, $properties = array(), $id_prop = 'id') {
1105
+ $name = trim(strval($name));
1106
+ if ( empty($name) )
1107
+ return false;
1108
+ //Create new field for element
1109
+ $el = new SLB_Field($name, $type);
1110
+ //Set container to current field instance
1111
+ $el->set_container($this);
1112
+ //Add properties to element
1113
+ $el->set_properties($properties);
1114
+ //Save element to current instance
1115
+ $this->elements[$name] =& $el;
1116
+ }
1117
+
1118
+ /**
1119
+ * Add a layout to the field
1120
+ * @param string $name Name of layout
1121
+ * @param string $value Layout text
1122
+ */
1123
+ function set_layout($name, $value = '') {
1124
+ if ( !is_string($name) )
1125
+ return false;
1126
+ $name = trim($name);
1127
+ $this->layout[$name] = $value;
1128
+ return true;
1129
+ }
1130
+
1131
+ /**
1132
+ * Retrieve specified layout
1133
+ * @param string $name Layout name
1134
+ * @param bool $parse_nested (optional) Whether nested layouts should be expanded in retreived layout or not (Default: TRUE)
1135
+ * @return string Specified layout text
1136
+ */
1137
+ function get_layout($name = 'form', $parse_nested = true) {
1138
+ //Retrieve specified layout (use $name value if no layout by that name exists)
1139
+ $layout = $this->get_member_value('layout', $name, $name);
1140
+
1141
+ //Find all nested layouts in current layout
1142
+ if ( !empty($layout) && !!$parse_nested ) {
1143
+ $ph = $this->get_placeholder_defaults();
1144
+
1145
+ while ($ph->match = $this->parse_layout($layout, $ph->pattern_layout)) {
1146
+ //Iterate through the different types of layout placeholders
1147
+ foreach ($ph->match as $tag => $instances) {
1148
+ //Iterate through instances of a specific type of layout placeholder
1149
+ foreach ($instances as $instance) {
1150
+ //Get nested layout
1151
+ $nested_layout = $this->get_member_value($instance);
1152
+
1153
+ //Replace layout placeholder with retrieved item data
1154
+ if ( !empty($nested_layout) )
1155
+ $layout = str_replace($ph->start . $instance['match'] . $ph->end, $nested_layout, $layout);
1156
+ }
1157
+ }
1158
+ }
1159
+ }
1160
+
1161
+ return $layout;
1162
+ }
1163
+
1164
+ /**
1165
+ * Checks if specified layout exists
1166
+ * Finds layout if it exists in current object or any of its parents
1167
+ * @param string $layout Name of layout to check for
1168
+ * @return bool TRUE if layout exists, FALSE otherwise
1169
+ */
1170
+ function has_layout($layout) {
1171
+ $ret = false;
1172
+ if ( is_string($layout) && ($layout = trim($layout)) && !empty($layout) ) {
1173
+ $layout = $this->get_member_value('layout', $layout, false);
1174
+ if ( $layout !== false )
1175
+ $ret = true;
1176
+ }
1177
+
1178
+ return $ret;
1179
+ }
1180
+
1181
+ /**
1182
+ * Checks if layout content is valid
1183
+ * Layouts need to have placeholders to be valid
1184
+ * @param string $layout_content Layout content (markup)
1185
+ * @return bool TRUE if layout is valid, FALSE otherwise
1186
+ */
1187
+ function is_valid_layout($layout_content) {
1188
+ $ph = $this->get_placeholder_defaults();
1189
+ return preg_match($ph->pattern_general, $layout_content);
1190
+ }
1191
+
1192
+ /**
1193
+ * Parse field layout with a regular expression
1194
+ * @param string $layout Layout data
1195
+ * @param string $search Regular expression pattern to search layout for
1196
+ * @return array Associative array containing all of the regular expression matches in the layout data
1197
+ * Array Structure:
1198
+ * root => placeholder tags
1199
+ * => Tag instances (array)
1200
+ * 'tag' => (string) tag name
1201
+ * 'match' => (string) placeholder match
1202
+ * 'attributes' => (array) attributes
1203
+ */
1204
+ function parse_layout($layout, $search) {
1205
+ $ph_xml = '';
1206
+ $parse_match = '';
1207
+ $ph_root_tag = 'ph_root_element';
1208
+ $ph_start_xml = '<';
1209
+ $ph_end_xml = ' />';
1210
+ $ph_wrap_start = '<' . $ph_root_tag . '>';
1211
+ $ph_wrap_end = '</' . $ph_root_tag . '>';
1212
+ $parse_result = false;
1213
+
1214
+ //Find all nested layouts in layout
1215
+ $match_value = preg_match_all($search, $layout, $parse_match, PREG_PATTERN_ORDER);
1216
+
1217
+ if ($match_value !== false && $match_value > 0) {
1218
+ $parse_result = array();
1219
+ //Get all matched elements
1220
+ $parse_match = $parse_match[1];
1221
+
1222
+ //Build XML string from placeholders
1223
+ foreach ($parse_match as $ph) {
1224
+ $ph_xml .= $ph_start_xml . $ph . $ph_end_xml . ' ';
1225
+ }
1226
+ $ph_xml = $ph_wrap_start . $ph_xml . $ph_wrap_end;
1227
+ //Parse XML data
1228
+ $ph_prs = xml_parser_create();
1229
+ xml_parser_set_option($ph_prs, XML_OPTION_SKIP_WHITE, 1);
1230
+ xml_parser_set_option($ph_prs, XML_OPTION_CASE_FOLDING, 0);
1231
+ $ret = xml_parse_into_struct($ph_prs, $ph_xml, $parse_result['values'], $parse_result['index']);
1232
+ xml_parser_free($ph_prs);
1233
+
1234
+ //Build structured array with all parsed data
1235
+
1236
+ unset($parse_result['index'][$ph_root_tag]);
1237
+
1238
+ //Build structured array
1239
+ $result = array();
1240
+ foreach ($parse_result['index'] as $tag => $instances) {
1241
+ $result[$tag] = array();
1242
+ //Instances
1243
+ foreach ($instances as $instance) {
1244
+ //Skip instance if it doesn't exist in parse results
1245
+ if (!isset($parse_result['values'][$instance]))
1246
+ continue;
1247
+
1248
+ //Stop processing instance if a previously-saved instance with the same options already exists
1249
+ foreach ($result[$tag] as $tag_match) {
1250
+ if ($tag_match['match'] == $parse_match[$instance - 1])
1251
+ continue 2;
1252
+ }
1253
+
1254
+ //Init instance data array
1255
+ $inst_data = array();
1256
+
1257
+ //Add Tag to array
1258
+ $inst_data['tag'] = $parse_result['values'][$instance]['tag'];
1259
+
1260
+ //Add instance data to array
1261
+ $inst_data['attributes'] = (isset($parse_result['values'][$instance]['attributes'])) ? $inst_data['attributes'] = $parse_result['values'][$instance]['attributes'] : '';
1262
+
1263
+ //Add match to array
1264
+ $inst_data['match'] = $parse_match[$instance - 1];
1265
+
1266
+ //Add to result array
1267
+ $result[$tag][] = $inst_data;
1268
+ }
1269
+ }
1270
+ $parse_result = $result;
1271
+ }
1272
+
1273
+ return $parse_result;
1274
+ }
1275
+
1276
+ /**
1277
+ * Retrieves default properties to use when evaluating layout placeholders
1278
+ * @return object Object with properties for evaluating layout placeholders
1279
+ */
1280
+ function get_placeholder_defaults() {
1281
+ $ph = new stdClass();
1282
+ $ph->start = '{';
1283
+ $ph->end = '}';
1284
+ $ph->reserved = array('ref' => 'ref_base');
1285
+ $ph->pattern_general = '/' . $ph->start . '([a-zA-Z0-9_].*?)' . $ph->end . '/i';
1286
+ $ph->pattern_layout = '/' . $ph->start . '([a-zA-Z0-9].*?\s+' . $ph->reserved['ref'] . '="layout.*?".*?)' . $ph->end . '/i';
1287
+ return $ph;
1288
+ }
1289
+
1290
+ /**
1291
+ * Build item output
1292
+ * @param string $layout (optional) Layout to build
1293
+ * @param string $data Data to pass to layout
1294
+ * @return string Generated output
1295
+ */
1296
+ function build($layout = 'form', $data = null) {
1297
+ $out = array(
1298
+ $this->build_pre($layout, $data),
1299
+ $this->build_layout($layout,$data),
1300
+ $this->build_post($layout, $data)
1301
+ );
1302
+ return implode('', $out);
1303
+ }
1304
+
1305
+ /**
1306
+ * Content to add before layout output
1307
+ * @return string
1308
+ */
1309
+ function build_pre($layout = 'form', $data = null) {
1310
+ return '';
1311
+ }
1312
+
1313
+ /**
1314
+ * Content to add after layout output
1315
+ * @return string
1316
+ */
1317
+ function build_post($layout = 'form', $data = null) {
1318
+ return '';
1319
+ }
1320
+
1321
+ /**
1322
+ * Builds HTML for a field based on its properties
1323
+ * @param string $layout (optional) Name of layout to build
1324
+ * @param array $data Additional data for current item
1325
+ */
1326
+ function build_layout($layout = 'form', $data = null) {
1327
+ $out_default = '';
1328
+ //Get base layout
1329
+ $out = $this->get_layout($layout);
1330
+ //Only parse valid layouts
1331
+ if ( $this->is_valid_layout($out) ) {
1332
+ //Parse Layout
1333
+ $ph = $this->get_placeholder_defaults();
1334
+
1335
+ //Search layout for placeholders
1336
+ while ( $ph->match = $this->parse_layout($out, $ph->pattern_general) ) {
1337
+ //Iterate through placeholders (tag, id, etc.)
1338
+ foreach ( $ph->match as $tag => $instances ) {
1339
+ //Iterate through instances of current placeholder
1340
+ foreach ( $instances as $instance ) {
1341
+ //Process value based on placeholder name
1342
+ $target_property = apply_filters($this->add_prefix('process_placeholder_' . $tag), '', $this, $instance, $layout, $data);
1343
+ //Process value using default processors (if necessary)
1344
+ if ( '' == $target_property ) {
1345
+ $target_property = apply_filters($this->add_prefix('process_placeholder'), $target_property, $this, $instance, $layout, $data);
1346
+ }
1347
+
1348
+ //Clear value if value not a string
1349
+ if ( !is_scalar($target_property) ) {
1350
+ $target_property = '';
1351
+ }
1352
+ //Replace layout placeholder with retrieved item data
1353
+ $out = str_replace($ph->start . $instance['match'] . $ph->end, $target_property, $out);
1354
+ }
1355
+ }
1356
+ }
1357
+ } else {
1358
+ $out = $out_default;
1359
+ }
1360
+ /* Return generated value */
1361
+ return $out;
1362
+ }
1363
+ }
1364
+
1365
+ class SLB_Field extends SLB_Field_Type {}
1366
+
1367
+ /**
1368
+ * Managed collection of fields
1369
+ * @package Simple Lightbox
1370
+ * @subpackage Fields
1371
+ * @author SM
1372
+ */
1373
+ class SLB_Field_Collection extends SLB_Field_Base {
1374
+
1375
+ /**
1376
+ * Indexed array of items in collection
1377
+ * @var array
1378
+ */
1379
+ var $items = array();
1380
+
1381
+ /**
1382
+ * Associative array of groups in content type
1383
+ * Key: Group name
1384
+ * Value: object of group properties
1385
+ * > title
1386
+ * > description string Group description
1387
+ * > items array Items in group
1388
+ * @var array
1389
+ */
1390
+ var $groups = array();
1391
+
1392
+ /**
1393
+ * Item type
1394
+ * @var string
1395
+ */
1396
+ var $item_type = 'SLB_Field';
1397
+
1398
+ /* Constructors */
1399
+
1400
+ /**
1401
+ * Legacy constructor
1402
+ * @uses __construct() to init instance
1403
+ * @param string $id Content type ID
1404
+ */
1405
+ function SLB_Field_Collection($id, $title = '', $properties = null) {
1406
+ $args = func_get_args();
1407
+ call_user_func_array(array(&$this, '__construct'), $args);
1408
+ }
1409
+
1410
+ /**
1411
+ * Class constructor
1412
+ * @param string $id Content type ID
1413
+ * @param array $properties (optional) Properties to set for content type (Default: none)
1414
+ */
1415
+ function __construct($id, $properties = null) {
1416
+ //Parent constructor
1417
+ parent::__construct($id, $properties);
1418
+
1419
+ //Init
1420
+ $this->init();
1421
+
1422
+ //Setup object based on properties
1423
+ if ( is_array($properties) && !empty($properties) ) {
1424
+ //Groups
1425
+ if ( isset($properties['groups']) )
1426
+ $this->add_groups($properties['groups']);
1427
+ //Items
1428
+ if ( isset($properties['items']) )
1429
+ $this->add_items($properties['items']);
1430
+ }
1431
+ }
1432
+
1433
+ /*-** Getters/Setters **-*/
1434
+
1435
+ /* Data */
1436
+
1437
+ /**
1438
+ * Retrieve external data for items in collection
1439
+ * Retrieved data is saved to the collection's $data property
1440
+ * Uses class properties to determine how data is retrieved
1441
+ * Examples:
1442
+ * > DB
1443
+ * > XML
1444
+ * > JSON
1445
+ * @return void
1446
+ */
1447
+ function load_data() {
1448
+ $this->data_fetched = true;
1449
+ }
1450
+
1451
+ /**
1452
+ * Set data for an item
1453
+ * @param string|object $item Reference or ID of Field to set data for
1454
+ * @param mixed $value Data to set
1455
+ */
1456
+ function set_data($item, $value = '', $save = true) {
1457
+ //Set data for entire collection
1458
+ if ( 1 == func_num_args() && is_array($item) )
1459
+ $this->data = wp_parse_args($item, $this->data);
1460
+ //Get $item's ID
1461
+ elseif ( is_object($item) && method_exists($item, 'get_id') )
1462
+ $item = $item->get_id();
1463
+ //Set data
1464
+ if ( is_string($item) && !empty($item) && isset($this->items[$item]) )
1465
+ $this->data[$item] = $value;
1466
+ if ( $save )
1467
+ $this->save();
1468
+ }
1469
+
1470
+ /* Item */
1471
+
1472
+ /**
1473
+ * Adds item to collection
1474
+ * @param string $id Unique name for item
1475
+ * @param object|string $parent Field type that this item is based on
1476
+ * @param array $properties (optional) Item properties
1477
+ * @param string $group (optional) Group ID to add item to
1478
+ * @return object Reference to new item
1479
+ */
1480
+ function &add($id, $parent = null, $properties = array(), $group = null) {
1481
+ $args = func_get_args();
1482
+ $properties = $this->make_properties($this->util->func_get_options($args), $properties, array('group' => $group));
1483
+ $it = ( is_object($id) ) ? 'O:' . $id->get_id() . '(' . get_class($id) . ')' : $id;
1484
+ //Check if previously created item is being added
1485
+ if ( is_object($id) && strtolower(get_class($id)) == strtolower($this->item_type) ) {
1486
+ $item =& $id;
1487
+ } else {
1488
+ //Create item
1489
+ if ( !class_exists($this->item_type) )
1490
+ return false;
1491
+ $type = $this->item_type;
1492
+ /**
1493
+ * @var SLB_Field
1494
+ */
1495
+ $item =& new $type($id, $properties);
1496
+ }
1497
+ if ( strlen($item->get_id()) == 0 ) {
1498
+ return false;
1499
+ }
1500
+
1501
+ $item->set_container($this);
1502
+
1503
+ //Add item to collection
1504
+ $this->items[$item->get_id()] =& $item;
1505
+
1506
+ //Add item to group
1507
+ if ( empty($group) ) {
1508
+ //Check properties array for group
1509
+ if ( isset($properties['group']) ) {
1510
+ $group = $properties['group'];
1511
+ //Remove group property from array
1512
+ unset($properties['group']);
1513
+ }
1514
+ }
1515
+ $this->add_to_group($group, $item->id);
1516
+ return $item;
1517
+ }
1518
+
1519
+ /**
1520
+ * Removes item from collection
1521
+ * @param string|object $item Object or item ID to remove
1522
+ */
1523
+ function remove($item) {
1524
+ if ( $this->has($item) ) {
1525
+ $item = $this->get($item);
1526
+ $item = $item->get_id();
1527
+ //Remove from items array
1528
+ unset($this->items[$item]);
1529
+ //Remove item from groups
1530
+ $this->remove_from_group($item);
1531
+ }
1532
+ }
1533
+
1534
+ /**
1535
+ * Checks if item exists in the collection
1536
+ * @param string $item Item ID
1537
+ * @return bool TRUE if item exists, FALSE otherwise
1538
+ */
1539
+ function has($item) {
1540
+ return ( !is_string($item) || empty($item) || is_null($this->get_member_value('items', $item, null)) ) ? false : true;
1541
+ }
1542
+
1543
+ /**
1544
+ * Retrieve specified item in collection
1545
+ * @param string|object $item Item object or ID to retrieve
1546
+ * @return object Specified item
1547
+ */
1548
+ function &get($item) {
1549
+ if ( $this->has($item) ) {
1550
+ if ( !is_object($item) || !is_a($item, $this->item_type) ) {
1551
+ if ( is_string($item) ) {
1552
+ $item = trim($item);
1553
+ $item =& $this->items[$item];
1554
+ }
1555
+ else {
1556
+ $item = false;
1557
+ }
1558
+ }
1559
+ }
1560
+
1561
+ if ( empty($item) ) {
1562
+ //Return empty item if no item exists
1563
+ $item =& new $this->item_type;
1564
+ }
1565
+ return $item;
1566
+ }
1567
+
1568
+ /**
1569
+ * Retrieve item data
1570
+ * @param $item
1571
+ * @param $context
1572
+ * @param $top
1573
+ */
1574
+ function get_data($item = null, $context = '', $top = true) {
1575
+ $this->load_data();
1576
+ $ret = null;
1577
+ if ( $this->has($item) ) {
1578
+ $item =& $this->get($item);
1579
+ $ret = $item->get_data($context, $top);
1580
+ } elseif ( is_null($item) ) {
1581
+ $ret = parent::get_data($context, $top);
1582
+ }
1583
+ return $ret;
1584
+ }
1585
+
1586
+ /* Items (Collection) */
1587
+
1588
+ /**
1589
+ * Add multiple items to collection
1590
+ * @param array $items Items to add to collection
1591
+ * Array Structure:
1592
+ * > Key (string): Item ID
1593
+ * > Val (array): Item properties
1594
+ * @return void
1595
+ */
1596
+ function add_items($items = array()) {
1597
+ //Validate
1598
+ if ( !is_array($items) || empty($items) )
1599
+ return false;
1600
+ //Iterate
1601
+ foreach ( $items as $id => $props ) {
1602
+ $this->add($id, $props);
1603
+ }
1604
+ }
1605
+
1606
+ /**
1607
+ * Retrieve reference to items in collection
1608
+ * @return array Collection items (reference)
1609
+ */
1610
+ function &get_items($group = null) {
1611
+ if ( $this->group_exists($group) ) {
1612
+ return $this->get_group_items($group);
1613
+ }
1614
+ return $this->items;
1615
+ }
1616
+
1617
+ /**
1618
+ * Default bulk item building method
1619
+ * Children classes should implement their own functionality
1620
+ * If no group specified, all items in collection are built
1621
+ * @param string|object $group (optional) Group to build items for (ID or instance object)
1622
+ * @return void
1623
+ */
1624
+ function build_items($group = null) {
1625
+ $items =& $this->get_items($group);
1626
+ $out = array();
1627
+ foreach ( $items as $item ) {
1628
+ $out[] = $item->build();
1629
+ }
1630
+ return implode('', $out);
1631
+ }
1632
+
1633
+ /* Group */
1634
+
1635
+ /**
1636
+ * Add groups to collection
1637
+ * @param array $groups Associative array of group properties
1638
+ * Array structure:
1639
+ * > Key (string): group ID
1640
+ * > Val (string): Group Title
1641
+ */
1642
+ function add_groups($groups = array()) {
1643
+ //Validate
1644
+ if ( !is_array($groups) || empty($groups) )
1645
+ return false;
1646
+ //Iterate
1647
+ foreach ( $groups as $id => $props ) {
1648
+ $this->add_group($id, $props);
1649
+ }
1650
+ }
1651
+
1652
+ /**
1653
+ * Adds group to content type
1654
+ * Groups are used to display related items in the UI
1655
+ * @param string $id Unique name for group
1656
+ * @param string $title Group title
1657
+ * @param string $description Short description of group's purpose
1658
+ * @param array $items (optional) ID's of existing items to add to group
1659
+ * @return object Group object
1660
+ */
1661
+ function &add_group($id, $title = '', $description = '', $items = array()) {
1662
+ //Create new group and set properties
1663
+ $id = trim($id);
1664
+ $this->groups[$id] =& $this->create_group($title, $description);
1665
+ //Add items to group (if supplied)
1666
+ if ( !empty($items) && is_array($items) )
1667
+ $this->add_to_group($id, $items);
1668
+ return $this->groups[$id];
1669
+ }
1670
+
1671
+ /**
1672
+ * Remove specified group from content type
1673
+ * @param string $id Group ID to remove
1674
+ */
1675
+ function remove_group($id) {
1676
+ $id = trim($id);
1677
+ if ( $this->group_exists($id) ) {
1678
+ unset($this->groups[$id]);
1679
+ }
1680
+ }
1681
+
1682
+ /**
1683
+ * Standardized method to create a new item group
1684
+ * @param string $title Group title (used in meta boxes, etc.)
1685
+ * @param string $description Short description of group's purpose
1686
+ * @return object Group object
1687
+ */
1688
+ function &create_group($title = '', $description = '') {
1689
+ //Create new group object
1690
+ $group = new stdClass();
1691
+ /* Set group properties */
1692
+
1693
+ //Set Title
1694
+ $title = ( is_scalar($title) ) ? trim($title) : '';
1695
+ $group->title = $title;
1696
+ //Set Description
1697
+ $description = ( is_scalar($description) ) ? trim($description) : '';
1698
+ $group->description = $description;
1699
+ //Create array to hold items
1700
+ $group->items = array();
1701
+ return $group;
1702
+ }
1703
+
1704
+ /**
1705
+ * Checks if group exists in collection
1706
+ * @param string $id Group name
1707
+ * @return bool TRUE if group exists, FALSE otherwise
1708
+ */
1709
+ function group_exists($group) {
1710
+ $ret = false;
1711
+ if ( is_object($group) )
1712
+ $ret = true;
1713
+ elseif ( is_string($group) && ($group = trim($group)) && strlen($group) > 0 ) {
1714
+ $group = trim($group);
1715
+ //Check if group exists
1716
+ $ret = !is_null($this->get_member_value('groups', $group, null));
1717
+ }
1718
+ return $ret;
1719
+ }
1720
+
1721
+ /**
1722
+ * Adds item to a group in the collection
1723
+ * Group is created if it does not already exist
1724
+ * @param string|array $group ID of group (or group parameters if new group) to add item to
1725
+ * @param string|array $items Name or array of item(s) to add to group
1726
+ */
1727
+ function add_to_group($group, $items) {
1728
+ //Validate parameters
1729
+ $group_id = '';
1730
+ if ( !empty($group) ) {
1731
+ if ( !is_array($group) ) {
1732
+ $group = array($group, $group);
1733
+ }
1734
+
1735
+ $group[0] = $group_id = trim(sanitize_title_with_dashes($group[0]));
1736
+ }
1737
+ if ( empty($group_id) || empty($items) )
1738
+ return false;
1739
+ //Create group if it doesn't exist
1740
+ if ( !$this->group_exists($group_id) ) {
1741
+ call_user_func_array($this->m('add_group'), $group);
1742
+ }
1743
+ if ( ! is_array($items) )
1744
+ $items = array($items);
1745
+ foreach ( $items as $item ) {
1746
+ if ( ! $this->has($item) )
1747
+ continue;
1748
+ $iref =& $this->get($item);
1749
+ //Remove item from any other group it's in (items can only be in one group)
1750
+ foreach ( array_keys($this->groups) as $group_name ) {
1751
+ if ( isset($this->groups[$group_name]->items[$iref->id]) )
1752
+ unset($this->groups[$group_name]->items[$iref->id]);
1753
+ }
1754
+ //Add reference to item in group
1755
+ $this->groups[$group_id]->items[$iref->id] =& $iref;
1756
+ unset($iref);
1757
+ }
1758
+ }
1759
+
1760
+ /**
1761
+ * Remove item from a group
1762
+ * If no group is specified, then item is removed from all groups
1763
+ * @param string|object $item Object or ID of item to remove from group
1764
+ * @param string $group (optional) Group ID to remove item from
1765
+ */
1766
+ function remove_from_group($item, $group = '') {
1767
+ //Get ID of item to remove or stop execution if item invalid
1768
+ $item = $this->get($item);
1769
+ $item = $item->get_id();
1770
+ if ( !$item )
1771
+ return false;
1772
+
1773
+ //Remove item from group
1774
+ if ( !empty($group) ) {
1775
+ //Remove item from single group
1776
+ if ( ($group =& $this->get_group($group)) && isset($group->items[$item]) ) {
1777
+ unset($group->items[$item]);
1778
+ }
1779
+ } else {
1780
+ //Remove item from all groups
1781
+ foreach ( array_keys($this->groups) as $group ) {
1782
+ if ( ($group =& $this->get_group($group)) && isset($group->items[$item]) ) {
1783
+ unset($group->items[$item]);
1784
+ }
1785
+ }
1786
+ }
1787
+ }
1788
+
1789
+ /**
1790
+ * Retrieve specified group
1791
+ * @param string $group ID of group to retrieve
1792
+ * @return object Reference to specified group
1793
+ */
1794
+ function &get_group($group) {
1795
+ if ( is_object($group) )
1796
+ return $group;
1797
+ if ( is_string($group) )
1798
+ $group = trim($group);
1799
+ //Create group if it doesn't already exist
1800
+ if ( ! $this->group_exists($group) )
1801
+ $this->add_group($group);
1802
+ return $this->get_member_value('groups', $group);
1803
+ }
1804
+
1805
+ /**
1806
+ * Retrieve a group's items
1807
+ * @uses SLB_Field_Collection::get_group() to retrieve group object
1808
+ * @param object|string $group Group object or group ID
1809
+ * @return array Group's items
1810
+ */
1811
+ function &get_group_items($group) {
1812
+ $group =& $this->get_group($group);
1813
+ return $group->items;
1814
+ }
1815
+
1816
+ /**
1817
+ * Retrieve all groups in content type
1818
+ * @return array Reference to group objects
1819
+ */
1820
+ function &get_groups() {
1821
+ return $this->get_member_value('groups');
1822
+ }
1823
+
1824
+ /**
1825
+ * Output items in a group
1826
+ * @param string $group ID of Group to output
1827
+ * @return string Group output
1828
+ * @todo Refactor to be general builder
1829
+ */
1830
+ function build_group($group) {
1831
+ $out = array();
1832
+ $classnames = (object) array(
1833
+ 'multi' => 'multi_field',
1834
+ 'single' => 'single_field',
1835
+ 'elements' => 'has_elements'
1836
+ );
1837
+
1838
+ //Stop execution if group does not exist
1839
+ if ( $this->group_exists($group) && $group =& $this->get_group($group) ) {
1840
+ $group_items = ( count($group->items) > 1 ) ? $classnames->multi : $classnames->single . ( ( ( $fs = array_keys($group->items) ) && ( $f =& $group->items[$fs[0]] ) && ( $els = $f->get_member_value('elements', '', null) ) && !empty($els) ) ? '_' . $classnames->elements : '' );
1841
+ $classname = array($this->add_prefix('attributes_wrap'), $group_items);
1842
+ $out[] = '<div class="' . implode(' ', $classname) . '">'; //Wrap all items in group
1843
+
1844
+ //Build layout for each item in group
1845
+ foreach ( array_keys($group->items) as $item_id ) {
1846
+ $item =& $group->items[$item_id];
1847
+ $item->set_caller($this);
1848
+ //Start item output
1849
+ $id = $this->add_prefix('field_' . $item->get_id());
1850
+ $out[] = '<div id="' . $id . '_wrap" class=' . $this->add_prefix('attribute_wrap') . '>';
1851
+ //Build item layout
1852
+ $out[] = $item->build_layout();
1853
+ //end item output
1854
+ $out[] = '</div>';
1855
+ $item->clear_caller();
1856
+ }
1857
+ $out[] = '</div>'; //Close items container
1858
+ //Add description if exists
1859
+ if ( !empty($group->description) )
1860
+ $out[] = '<p class=' . $this->add_prefix('group_description') . '>' . $group->description . '</p>';
1861
+ }
1862
+
1863
+ //Return group output
1864
+ return implode($out);
1865
+ }
1866
+
1867
+ /* Collection */
1868
+
1869
+ /**
1870
+ * Build entire collection of items
1871
+ */
1872
+ function build() {
1873
+ //Get Groups
1874
+ $groups = array_keys($this->get_groups());
1875
+ //Build groups
1876
+ foreach ( $groups as $group ) {
1877
+ $this->build_group($group);
1878
+ }
1879
+ }
1880
+ }
1881
+
1882
+ /**
1883
+ * Collection of default system-wide fields
1884
+ * @package Simple Lightbox
1885
+ * @subpackage Fields
1886
+ * @author SM
1887
+ *
1888
+ */
1889
+ class SLB_Fields extends SLB_Field_Collection {
1890
+
1891
+ var $item_type = 'SLB_Field_Type';
1892
+
1893
+ /**
1894
+ * Placeholder handlers
1895
+ * @var array
1896
+ */
1897
+ var $placholders = null;
1898
+
1899
+ /* Constructor */
1900
+
1901
+ function SLB_Fields() {
1902
+ $this->__construct();
1903
+ }
1904
+
1905
+ function __construct() {
1906
+ parent::__construct('fields');
1907
+ }
1908
+
1909
+ function register_hooks() {
1910
+ parent::register_hooks();
1911
+
1912
+ //Init fields
1913
+ add_action('init', $this->m('register_types'));
1914
+ //Init placeholders
1915
+ add_action('init', $this->m('register_placeholders'));
1916
+ }
1917
+
1918
+ /* Field Types */
1919
+
1920
+ /**
1921
+ * Initialize fields and content types
1922
+ */
1923
+ function register_types() {
1924
+ /* Field Types */
1925
+
1926
+ //Base
1927
+ $base =& new SLB_Field_Type('base');
1928
+ $base->set_description('Default Element');
1929
+ $base->set_property('tag', 'span');
1930
+ $base->set_property('class', '', 'attr');
1931
+ $base->set_layout('form_attr', '{tag} name="{field_name}" id="{field_id}" {properties ref_base="root" group="attr"}');
1932
+ $base->set_layout('form', '<{form_attr ref_base="layout"} />');
1933
+ $base->set_layout('label', '<label for="{field_id}">{label}</label>');
1934
+ $base->set_layout('display', '{data context="display"}');
1935
+ $this->add($base);
1936
+
1937
+ //Base closed
1938
+ $base_closed =& new SLB_Field_Type('base_closed');
1939
+ $base_closed->set_parent('base');
1940
+ $base_closed->set_description('Default Element (Closed Tag)');
1941
+ $base_closed->set_layout('form_start', '<{tag} id="{field_id}" name="{field_name}" {properties ref_base="root" group="attr"}>');
1942
+ $base_closed->set_layout('form_end', '</{tag}>');
1943
+ $base_closed->set_layout('form', '{form_start ref_base="layout"}{data}{form_end ref_base="layout"}');
1944
+ $this->add($base_closed);
1945
+
1946
+ //Input
1947
+ $input =& new SLB_Field_Type('input', 'base');
1948
+ $input->set_description('Default Input Element');
1949
+ $input->set_property('tag', 'input');
1950
+ $input->set_property('type', 'text', 'attr');
1951
+ $input->set_property('value', '{data}', 'attr');
1952
+ $this->add($input);
1953
+
1954
+ //Text input
1955
+ $text =& new SLB_Field_Type('text', 'input');
1956
+ $text->set_description('Text Box');
1957
+ $text->set_property('size', 15, 'attr');
1958
+ $text->set_property('label');
1959
+ $text->set_layout('form', '{label ref_base="layout"} {inherit}');
1960
+ $this->add($text);
1961
+
1962
+ //Checkbox
1963
+ $cb =& new SLB_Field_Type('checkbox', 'input');
1964
+ $cb->set_property('type', 'checkbox');
1965
+ $cb->set_property('value', null);
1966
+ $cb->set_layout('form_attr', '{inherit} {checked}');
1967
+ $cb->set_layout('form', '{label ref_base="layout"} <{form_attr ref_base="layout"} />');
1968
+ $this->add($cb);
1969
+
1970
+ //Textarea
1971
+ $ta =& new SLB_Field_Type('textarea', 'base_closed');
1972
+ $ta->set_property('tag', 'textarea');
1973
+ $ta->set_property('cols', 40, 'attr');
1974
+ $ta->set_property('rows', 3, 'attr');
1975
+ $this->add($ta);
1976
+
1977
+ //Rich Text
1978
+ $rt =& new SLB_Field_Type('richtext', 'textarea');
1979
+ $rt->set_property('class', 'theEditor {inherit}');
1980
+ $rt->set_layout('form', '<div class="rt_container">{inherit}</div>');
1981
+ $rt->add_action('admin_print_footer_scripts', 'wp_tiny_mce', 25);
1982
+ $this->add($rt);
1983
+
1984
+ //Hidden
1985
+ $hidden =& new SLB_Field_Type('hidden');
1986
+ $hidden->set_parent('input');
1987
+ $hidden->set_description('Hidden Field');
1988
+ $hidden->set_property('type', 'hidden');
1989
+ $this->add($hidden);
1990
+
1991
+ //Select
1992
+ $select =& new SLB_Field_Type('select', 'base_closed');
1993
+ $select->set_description('Select tag');
1994
+ $select->set_property('tag', 'select');
1995
+ $select->set_property('tag_option', 'option');
1996
+ $select->set_property('options', array());
1997
+ $select->set_layout('form', '{label ref_base="layout"} {form_start ref_base="layout"}{option_loop ref_base="layout"}{form_end ref_base="layout"}');
1998
+ $select->set_layout('option_loop', '{loop data="properties.options" layout="option" layout_data="option_data"}');
1999
+ $select->set_layout('option', '<{tag_option} value="{data_ext id="option_value"}">{data_ext id="option_text"}</{tag_option}>');
2000
+ $select->set_layout('option_data', '<{tag_option} value="{data_ext id="option_value"}" selected="selected">{data_ext id="option_text"}</{tag_option}>');
2001
+ $this->add($select);
2002
+
2003
+ //Span
2004
+ $span =& new SLB_Field_Type('span', 'base_closed');
2005
+ $span->set_description('Inline wrapper');
2006
+ $span->set_property('tag', 'span');
2007
+ $span->set_property('value', 'Hello there!');
2008
+ $this->add($span);
2009
+
2010
+ //Enable plugins to modify (add, remove, etc.) field types
2011
+ do_action_ref_array($this->add_prefix('register_fields'), array(&$this));
2012
+
2013
+ //Signal completion of field registration
2014
+ do_action_ref_array($this->add_prefix('fields_registered'), array(&$this));
2015
+ }
2016
+
2017
+ /* Placeholder handlers */
2018
+
2019
+ function register_placeholders() {
2020
+ //Default placeholder handlers
2021
+ $this->register_placeholder('all', $this->m('process_placeholder_default'), 11);
2022
+ $this->register_placeholder('field_id', $this->m('process_placeholder_id'));
2023
+ $this->register_placeholder('field_name', $this->m('process_placeholder_name'));
2024
+ $this->register_placeholder('data', $this->m('process_placeholder_data'));
2025
+ $this->register_placeholder('data_ext',$this->m('process_placeholder_data_ext'));
2026
+ $this->register_placeholder('loop', $this->m('process_placeholder_loop'));
2027
+ $this->register_placeholder('label', $this->m('process_placeholder_label'));
2028
+ $this->register_placeholder('checked', $this->m('process_placeholder_checked'));
2029
+
2030
+ //Allow other code to register placeholders
2031
+ do_action_ref_array($this->add_prefix('register_field_placeholders'), array(&$this));
2032
+
2033
+ //Signal completion of field placeholder registration
2034
+ do_action_ref_array($this->add_prefix('field_placeholders_registered'), array(&$this));
2035
+ }
2036
+
2037
+ /**
2038
+ * Register a function to handle a placeholder
2039
+ * Multiple handlers may be registered for a single placeholder
2040
+ * Adds filter hook to WP for handling specified placeholder
2041
+ * Placeholders are in layouts and are replaced with data at runtime
2042
+ * @uses add_filter()
2043
+ * @param string $placeholder Name of placeholder to add handler for (Using 'all' will set the function as a handler for all placeholders
2044
+ * @param callback $callback Function to set as a handler
2045
+ * @param int $priority (optional) Priority of handler
2046
+ * @return void
2047
+ */
2048
+ function register_placeholder($placeholder, $callback, $priority = 10) {
2049
+ if ( 'all' == $placeholder )
2050
+ $placeholder = '';
2051
+ else
2052
+ $placeholder = '_' . $placeholder;
2053
+ $hook = $this->add_prefix('process_placeholder' . $placeholder);
2054
+ add_filter($hook, $callback, $priority, 5);
2055
+ }
2056
+
2057
+ /**
2058
+ * Default placeholder processing
2059
+ * To be executed when current placeholder has not been handled by another handler
2060
+ * @param string $output Value to be used in place of placeholder
2061
+ * @param SLB_Field $item Field containing placeholder
2062
+ * @param array $placeholder Current placeholder
2063
+ * @see SLB_Field::parse_layout for structure of $placeholder array
2064
+ * @param string $layout Layout to build
2065
+ * @param array $data Extended data for item
2066
+ * @return string Value to use in place of current placeholder
2067
+ */
2068
+ function process_placeholder_default($output, $item, $placeholder, $layout, $data) {
2069
+ //Validate parameters before processing
2070
+ if ( empty($output) && is_a($item, 'SLB_Field_Type') && is_array($placeholder) ) {
2071
+ //Build path to replacement data
2072
+ $output = $item->get_member_value($placeholder);
2073
+
2074
+ //Check if value is group (properties, etc.)
2075
+ //All groups must have additional attributes (beyond reserved attributes) that define how items in group are used
2076
+ if (is_array($output)
2077
+ && !empty($placeholder['attributes'])
2078
+ && is_array($placeholder['attributes'])
2079
+ && ($ph = $item->get_placeholder_defaults())
2080
+ && $attribs = array_diff(array_keys($placeholder['attributes']), array_values($ph->reserved))
2081
+ ) {
2082
+ /* Targeted property is an array, but the placeholder contains additional options on how property is to be used */
2083
+
2084
+ //Find items matching criteria in $output
2085
+ //Check for group criteria
2086
+ if ( 'properties' == $placeholder['tag'] && ($prop_group = $item->get_group($placeholder['attributes']['group'])) && !empty($prop_group) ) {
2087
+ /* Process group */
2088
+ $group_out = array();
2089
+ //Iterate through properties in group and build string
2090
+ foreach ( array_keys($prop_group) as $prop_key ) {
2091
+ $prop_val = $item->get_property($prop_key);
2092
+ if ( !is_null($prop_val) )
2093
+ $group_out[] = $prop_key . '="' . $prop_val . '"';
2094
+ }
2095
+ $output = implode(' ', $group_out);
2096
+ }
2097
+ } elseif ( is_object($output) && is_a($output, $item->base_class) ) {
2098
+ /* Targeted property is actually a nested item */
2099
+ //Set caller to current item
2100
+ $output->set_caller($item);
2101
+ //Build layout for nested element
2102
+ $output = $output->build_layout($layout);
2103
+ }
2104
+ }
2105
+
2106
+ return $output;
2107
+ }
2108
+
2109
+ /**
2110
+ * Build Field ID attribute
2111
+ * @see SLB_Field_Type::process_placeholder_default for parameter descriptions
2112
+ * @return string Placeholder output
2113
+ */
2114
+ function process_placeholder_id($output, $item, $placeholder, $layout, $data) {
2115
+ //Get attributes
2116
+ $args = wp_parse_args($placeholder['attributes'], array('format' => 'attr_id'));
2117
+ return $item->get_id($args);
2118
+ }
2119
+
2120
+ /**
2121
+ * Build Field name attribute
2122
+ * Name is formatted as an associative array for processing by PHP after submission
2123
+ * @see SLB_Field_Type::process_placeholder_default for parameter descriptions
2124
+ * @return string Placeholder output
2125
+ */
2126
+ function process_placeholder_name($output, $item, $placeholder, $layout, $data) {
2127
+ //Get attributes
2128
+ $args = wp_parse_args($placeholder['attributes'], array('format' => 'attr_name'));
2129
+ return $item->get_id($args);
2130
+ }
2131
+
2132
+ /**
2133
+ * Build item label
2134
+ * @see SLB_Fields::process_placeholder_default for parameter descriptions
2135
+ * @return string Field label
2136
+ */
2137
+ function process_placeholder_label($output, $item, $placeholder, $layout, $data) {
2138
+ //Check if item has label property (e.g. sub-elements)
2139
+ $out = $item->get_property('label');
2140
+ //If property not set, use item title
2141
+ if ( empty($out) )
2142
+ $out = $item->get_title();
2143
+ return $out;
2144
+ }
2145
+
2146
+ /**
2147
+ * Retrieve data for item
2148
+ * @see SLB_Field_Type::process_placeholder_default for parameter descriptions
2149
+ * @return string Placeholder output
2150
+ */
2151
+ function process_placeholder_data($output, $item, $placeholder, $layout) {
2152
+ $attr_default = array (
2153
+ 'context' => '',
2154
+ );
2155
+ $opts = wp_parse_args($placeholder['attributes'], $attr_default);
2156
+ //Save context to separate variable
2157
+ $context = $opts['context'];
2158
+ unset($opts['context']);
2159
+ //Get data
2160
+ $out = $item->get_data($opts);
2161
+ if ( !is_null($out) ) {
2162
+ //Get specific member in value (e.g. value from a specific item element)
2163
+ if ( isset($opts['element']) && is_array($out) && ( $el = $opts['element'] ) && isset($out[$el]) )
2164
+ $out = $out[$el];
2165
+ }
2166
+
2167
+ //Format data based on context
2168
+ $out = $item->format($out, $context);
2169
+ //Return data
2170
+ return $out;
2171
+ }
2172
+
2173
+ /**
2174
+ * Set checked attribute on item
2175
+ * Evaluates item's data to see if item should be checked or not
2176
+ * @see SLB_Fields::process_placeholder_default for parameter descriptions
2177
+ * @return string Appropriate checkbox attribute
2178
+ */
2179
+ function process_placeholder_checked($output, $item, $placeholder, $layout, $data) {
2180
+ $out = '';
2181
+ $c = $item->get_container();
2182
+ $d = ( isset($c->data[$item->get_id()]) ) ? $c->data[$item->get_id()] : null;
2183
+ $item->set_property('d', true);
2184
+ if ( $item->get_data() )
2185
+ $out = 'checked="checked"';
2186
+ $item->set_property('d', false);
2187
+ return $out;
2188
+ }
2189
+
2190
+ /**
2191
+ * Loops over data to build item output
2192
+ * Options:
2193
+ * data - Dot-delimited path in item that contains data to loop through
2194
+ * layout - Name of layout to use for each data item in loop
2195
+ * layout_data - Name of layout to use for data item that matches previously-saved item data
2196
+ * @see SLB_Field_Type::process_placeholder_default for parameter descriptions
2197
+ * @return string Placeholder output
2198
+ */
2199
+ function process_placeholder_loop($output, $item, $placeholder, $layout, $data) {
2200
+ //Setup loop options
2201
+ $attr_defaults = array (
2202
+ 'layout' => '',
2203
+ 'layout_data' => null,
2204
+ 'data' => ''
2205
+ );
2206
+ $attr = wp_parse_args($placeholder['attributes'], $attr_defaults);
2207
+ if ( is_null($attr['layout_data']) )
2208
+ $attr['layout_data'] =& $attr['layout'];
2209
+ //Get data for loop
2210
+ $path = explode('.', $attr['data']);
2211
+ $loop_data = $item->get_member_value($path);
2212
+
2213
+ //Check if data is callback
2214
+ if ( is_callable($loop_data) )
2215
+ $loop_data = call_user_func($loop_data);
2216
+
2217
+ //Get item data
2218
+ $data = $item->get_data();
2219
+
2220
+ //Iterate over data and build output
2221
+ $out = array();
2222
+ if ( is_array($loop_data) && !empty($loop_data) ) {
2223
+ foreach ( $loop_data as $value => $label ) {
2224
+ //Load appropriate layout based on item value
2225
+ $layout = ( ($data === 0 && $value === $data) xor $data == $value ) ? $attr['layout_data'] : $attr['layout'];
2226
+ //Stop processing if no valid layout is returned
2227
+ if ( empty($layout) )
2228
+ continue;
2229
+ //Prep extended item data
2230
+ $data_ext = array('option_value' => $value, 'option_text' => $label);
2231
+ $out[] = $item->build_layout($layout, $data_ext);
2232
+ }
2233
+ }
2234
+
2235
+ //Return output
2236
+ return implode($out);
2237
+ }
2238
+
2239
+ /**
2240
+ * Returns specified value from extended data array for item
2241
+ * @see SLB_Field_Type::process_placeholder_default for parameter descriptions
2242
+ * @return string Placeholder output
2243
+ */
2244
+ function process_placeholder_data_ext($output, $item, $placeholder, $layout, $data) {
2245
+ if ( isset($placeholder['attributes']['id']) && ($key = $placeholder['attributes']['id']) && isset($data[$key]) ) {
2246
+ $output = strval($data[$key]);
2247
+ }
2248
+
2249
+ return $output;
2250
+ }
2251
+ }
includes/class.options.php ADDED
Binary file
includes/class.utilities.php CHANGED
@@ -10,12 +10,39 @@
10
  */
11
  class SLB_Utilities {
12
 
13
- function SLB_Utilities() {
14
- $this->__construct();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  }
16
 
17
- function __construct() {
18
-
 
19
  }
20
 
21
  /**
@@ -33,6 +60,88 @@ class SLB_Utilities {
33
 
34
  /* Helper Functions */
35
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  /*-** WP **-*/
37
 
38
  /**
@@ -59,10 +168,112 @@ class SLB_Utilities {
59
  return true;
60
  }
61
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  /*-** Request **-*/
63
 
64
  /**
65
- * Checks $_SERVER['SCRIPT_NAME'] to see if file base name matches specified file name
66
  * @param string $filename Filename to check for
67
  * @return bool TRUE if current page matches specified filename, FALSE otherwise
68
  */
@@ -131,18 +342,30 @@ class SLB_Utilities {
131
  $parts = str_replace($sl_b, $sl_f, $parts);
132
  //Add trailing slash (if necessary)
133
  if ( $trailing_slash )
134
- $parts . $sl_f;
135
  return $parts;
136
- }
137
 
138
  /**
139
  * Returns URL of file (assumes that it is in plugin directory)
140
  * @param string $file name of file get URL
141
- * @return string File path
142
  */
143
  function get_file_url($file) {
144
- if (is_string($file) && '' != trim($file)) {
145
- $file = $this->normalize_path($this->get_url_base(), $file);
 
 
 
 
 
 
 
 
 
 
 
 
146
  }
147
  return $file;
148
  }
@@ -155,33 +378,45 @@ class SLB_Utilities {
155
  function get_file_extension($file) {
156
  $ret = '';
157
  $sep = '.';
158
- if ( is_string($icon) && ( $rpos = strrpos($file, $sep) ) !== false )
159
  $ret = substr($file, $rpos + 1);
160
  return $ret;
161
  }
162
 
163
  /**
164
  * Checks if file has specified extension
 
165
  * @param string $file File name/path
166
- * @param string $extension File ending to check $file for
167
  * @return bool TRUE if file has extension
168
  */
169
  function has_file_extension($file, $extension) {
170
- return ( $this->get_file_extension($file) == $extension ) ? true : false;
 
 
171
  }
172
 
173
  /**
174
  * Retrieve base URL for plugin-specific files
 
 
175
  * @return string Base URL
176
  */
177
  function get_url_base() {
178
  static $url_base = '';
179
  if ( '' == $url_base ) {
180
- $url_base = $this->normalize_path(WP_PLUGIN_URL, $this->get_plugin_base());
181
  }
182
  return $url_base;
183
  }
184
 
 
 
 
 
 
 
 
185
  function get_path_base() {
186
  static $path_base = '';
187
  if ( '' == $path_base ) {
@@ -189,7 +424,13 @@ class SLB_Utilities {
189
  }
190
  return $path_base;
191
  }
192
-
 
 
 
 
 
 
193
  function get_plugin_base() {
194
  static $plugin_dir = '';
195
  if ( '' == $plugin_dir ) {
@@ -198,14 +439,98 @@ class SLB_Utilities {
198
  return $plugin_dir;
199
  }
200
 
 
 
 
 
 
 
201
  function get_plugin_base_file() {
202
- $file = 'main.php';
203
- return $this->get_path_base() . '/' . $file;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204
  }
205
 
 
 
 
 
 
 
 
206
  function get_plugin_base_name() {
207
- $file = $this->get_plugin_base_file();
208
- return plugin_basename($file);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
  }
210
 
211
  /**
@@ -246,6 +571,22 @@ class SLB_Utilities {
246
 
247
  /*-** General **-*/
248
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
249
  /**
250
  * Checks if a property exists in a class or object
251
  * (Compatibility method for PHP 4
@@ -284,6 +625,60 @@ class SLB_Utilities {
284
  }
285
  }
286
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
287
  /**
288
  * Merges 1 or more arrays together
289
  * Methodology
@@ -295,7 +690,6 @@ class SLB_Utilities {
295
  * - Merge item in base array with current item based on key name
296
  * - If the current item's value AND the corresponding item in the base array are BOTH arrays, recursively merge the the arrays
297
  * - If the current item's value OR the corresponding item in the base array is NOT an array, current item overwrites base item
298
- * @todo Append numerical elements (as opposed to overwriting element at same index in base array)
299
  * @param array Variable number of arrays
300
  * @param array $arr1 Default array
301
  * @return array Merged array
@@ -303,24 +697,33 @@ class SLB_Utilities {
303
  function array_merge_recursive_distinct($arr1) {
304
  //Get all arrays passed to function
305
  $args = func_get_args();
306
- if (empty($args))
307
  return false;
 
 
 
308
  //Set first array as base array
309
  $merged = $args[0];
310
  //Iterate through arrays to merge
311
  $arg_length = count($args);
312
- for ($x = 1; $x < $arg_length; $x++) {
313
  //Skip if argument is not an array (only merge arrays)
314
- if (!is_array($args[$x]))
315
  continue;
316
  //Iterate through argument items
317
- foreach ($args[$x] as $key => $val) {
318
- if (!isset($merged[$key]) || !is_array($merged[$key]) || !is_array($val)) {
319
- $merged[$key] = $val;
320
- } elseif (is_array($merged[$key]) && is_array($val)) {
321
- $merged[$key] = $this->array_merge_recursive_distinct($merged[$key], $val);
322
- }
323
- //$merged[$key] = (is_array($val) && isset($merged[$key])) ? $this->array_merge_recursive_distinct($merged[$key], $val) : $val;
 
 
 
 
 
 
324
  }
325
  }
326
  return $merged;
@@ -375,6 +778,13 @@ class SLB_Utilities {
375
  return $item;
376
  }
377
 
 
 
 
 
 
 
 
378
  function get_array_path($attribute = '', $format = null) {
379
  //Formatted value
380
  $fmtd = '';
@@ -470,6 +880,15 @@ class SLB_Utilities {
470
  return $this->build_html_element(array('tag' => 'link', 'wrap' => false, 'attributes' => $attributes));
471
  }
472
 
 
 
 
 
 
 
 
 
 
473
  /**
474
  * Generate external script element
475
  * @param $url Script URL
@@ -542,23 +961,24 @@ class SLB_Utilities {
542
  //Get last submenu added
543
  $parent = $this->get_submenu_parent_file($parent);
544
  if ( isset($submenu[$parent]) ) {
545
- $subs =& $submenu[$parent];
546
- //Make sure menu isn't already in the desired position
547
- if ( $pos <= ( count($subs) - 1 ) ) {
548
- //Get submenu that was just added
549
- $sub = array_pop($subs);
550
- //Insert into desired position
551
- if ( 0 == $pos ) {
552
- array_unshift($subs, $sub);
553
- } else {
554
- $top = array_slice($subs, 0, $pos);
555
- $bottom = array_slice($subs, $pos);
556
- array_push($top, $sub);
557
- $subs = array_merge($top, $bottom);
558
- }
559
  }
560
  }
561
  }
 
562
 
563
  return $hookname;
564
  }
10
  */
11
  class SLB_Utilities {
12
 
13
+ /* Properties */
14
+
15
+ /**
16
+ * Instance parent
17
+ * @var object
18
+ */
19
+ var $parent = null;
20
+
21
+ /**
22
+ * Default plugin headers
23
+ * @var array
24
+ */
25
+ var $plugin_headers = array (
26
+ 'Name' => 'Plugin Name',
27
+ 'PluginURI' => 'Plugin URI',
28
+ 'Version' => 'Version',
29
+ 'Description' => 'Description',
30
+ 'Author' => 'Author',
31
+ 'AuthorURI' => 'Author URI',
32
+ 'TextDomain' => 'Text Domain',
33
+ 'DomainPath' => 'Domain Path',
34
+ 'Network' => 'Network',
35
+ );
36
+
37
+ /* Constructors */
38
+
39
+ function SLB_Utilities(&$obj) {
40
+ $this->__construct(&$obj);
41
  }
42
 
43
+ function __construct(&$obj) {
44
+ if ( is_object($obj) )
45
+ $this->parent =& $obj;
46
  }
47
 
48
  /**
60
 
61
  /* Helper Functions */
62
 
63
+ /*-** Prefix **-*/
64
+
65
+ /**
66
+ * Get valid separator
67
+ * @param string $sep (optional) Separator supplied
68
+ * @return string Separator
69
+ */
70
+ function get_sep($sep = false) {
71
+ if ( is_null($sep) )
72
+ $sep = '';
73
+ if ( !is_string($sep) )
74
+ $default = '_';
75
+ return ( is_string($sep) ) ? $sep : $default;
76
+ }
77
+
78
+ /**
79
+ * Retrieve class prefix (with separator if set)
80
+ * @param bool|string $sep Separator to append to class prefix (Default: no separator)
81
+ * @return string Class prefix
82
+ */
83
+ function get_prefix($sep = null) {
84
+ $sep = $this->get_sep($sep);
85
+ $prefix = ( !empty($this->parent->prefix) ) ? $this->parent->prefix . $sep : '';
86
+ return $prefix;
87
+ }
88
+
89
+ /**
90
+ * Check if a string is prefixed
91
+ * @param string $text Text to check for prefix
92
+ * @param string $sep (optional) Separator used
93
+ */
94
+ function has_prefix($text, $sep = null) {
95
+ return ( !empty($text) && strpos($text, $this->get_prefix($sep)) === 0 );
96
+ }
97
+
98
+ /**
99
+ * Prepend plugin prefix to some text
100
+ * @param string $text Text to add to prefix
101
+ * @param string $sep (optional) Text used to separate prefix and text
102
+ * @param bool $once (optional) Whether to add prefix to text that already contains a prefix or not
103
+ * @return string Text with prefix prepended
104
+ */
105
+ function add_prefix($text, $sep = '_', $once = true) {
106
+ if ( $this->has_prefix($text, $sep) )
107
+ return $text;
108
+ return $this->get_prefix($sep) . $text;
109
+ }
110
+
111
+ /**
112
+ * Add prefix to variable reference
113
+ * Updates actual variable rather than return value
114
+ * @uses add_prefix() to add prefix to variable
115
+ * @param string $var Variable to add prefix to
116
+ * @param string $sep (optional) Separator text
117
+ * @param bool $once (optional) Add prefix only once
118
+ * @return void
119
+ */
120
+ function add_prefix_ref(&$var, $sep = null, $once = true) {
121
+ $args = func_get_args();
122
+ $var = call_user_func_array($this->m($this, 'add_prefix'), $args);
123
+ }
124
+
125
+ /**
126
+ * Remove prefix from specified string
127
+ * @param string $text String to remove prefix from
128
+ * @param string $sep (optional) Separator used with prefix
129
+ */
130
+ function remove_prefix($text, $sep = '_') {
131
+ if ( $this->has_prefix($text,$sep) )
132
+ $text = substr($text, strlen($this->get_prefix($sep)));
133
+ return $text;
134
+ }
135
+
136
+ /**
137
+ * Returns Database prefix for Cornerstone-related DB Tables
138
+ * @return string Database prefix
139
+ */
140
+ function get_db_prefix() {
141
+ global $wpdb;
142
+ return $wpdb->prefix . $this->get_prefix('_');
143
+ }
144
+
145
  /*-** WP **-*/
146
 
147
  /**
168
  return true;
169
  }
170
 
171
+ /* Hooks */
172
+
173
+ function do_action($tag, $arg = '') {
174
+ do_action($this->add_prefix($tag), $arg);
175
+ }
176
+
177
+ function apply_filters($tag, $value) {
178
+ apply_filters($this->add_prefix($tag), $value);
179
+ }
180
+
181
+ function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
182
+ return add_action($this->add_prefix($tag), $function_to_add, $priority, $accepted_args);
183
+ }
184
+
185
+ function add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
186
+ return add_filter($this->add_prefix(tag), $function_to_add, $priority, $accepted_args);
187
+ }
188
+
189
+ /* Meta */
190
+
191
+ /**
192
+ * Retrieves post metadata for internal methods
193
+ * Metadata set internally is wrapped in an array so it is unwrapped before returned the retrieved value
194
+ * @see get_post_meta()
195
+ * @param int $post_id Post ID
196
+ * @param string $key Name of metadata to retrieve
197
+ * @param boolean $single Whether or not to retrieve single value or not
198
+ * @return mixed Retrieved post metadata
199
+ */
200
+ function post_meta_get($post_id, $key, $single = false) {
201
+ $meta_value = get_post_meta($post_id, $this->post_meta_get_key($key), $single);
202
+ if (is_array($meta_value) && count($meta_value) == 1)
203
+ $meta_value = $meta_value[0];
204
+ return $meta_value;
205
+ }
206
+
207
+ /**
208
+ * Wraps metadata in array for storage in database
209
+ * @param mixed $meta_value Value to be set as metadata
210
+ * @return array Wrapped metadata value
211
+ */
212
+ function post_meta_prepare_value($meta_value) {
213
+ return array($meta_value);
214
+ }
215
+
216
+ /**
217
+ * Adds Metadata for a post to database
218
+ * For internal methods
219
+ * @see add_post_meta
220
+ * @param $post_id
221
+ * @param $meta_key
222
+ * @param $meta_value
223
+ * @param $unique
224
+ * @return boolean Result of operation
225
+ */
226
+ function post_meta_add($post_id, $meta_key, $meta_value, $unique = false) {
227
+ $meta_value = $this->post_meta_value_prepare($meta_value);
228
+ return add_post_meta($post_id, $meta_key, $meta_value, $unique);
229
+ }
230
+
231
+ /**
232
+ * Updates post metadata for internal data/methods
233
+ * @see update_post_meta()
234
+ * @param $post_id
235
+ * @param $meta_key
236
+ * @param $meta_value
237
+ * @param $prev_value
238
+ * @return boolean Result of operation
239
+ */
240
+ function post_meta_update($post_id, $meta_key, $meta_value, $prev_value = '') {
241
+ $meta_value = $this->post_meta_prepare_value($meta_value);
242
+ return update_post_meta($post_id, $meta_key, $meta_value, $prev_value);
243
+ }
244
+
245
+ /**
246
+ * Builds postmeta key for custom data set by plugin
247
+ * @param string $key Base key name
248
+ * @return string Formatted postmeta key
249
+ */
250
+ function post_meta_get_key($key) {
251
+ $sep = '_';
252
+ if ( strpos($key, $sep . $this->prefix) !== 0 ) {
253
+ $key_base = func_get_args();
254
+ if ( !empty($key_base) ) {
255
+ $key = array_merge((array)$this->prefix, $key_base);
256
+ return $sep . implode($sep, $key);
257
+ }
258
+ }
259
+
260
+ return $key;
261
+ }
262
+
263
+ /**
264
+ * Creates a meta key for storing post meta data
265
+ * Prefixes standard prefixed text with underscore to hide meta data on post edit forms
266
+ * @param string $text Text to use as base of meta key
267
+ * @return string Formatted meta key
268
+ */
269
+ function make_meta_key($text = '') {
270
+ return '_' . $this->add_prefix($text);
271
+ }
272
+
273
  /*-** Request **-*/
274
 
275
  /**
276
+ * Checks if the currently executing file matches specified file name
277
  * @param string $filename Filename to check for
278
  * @return bool TRUE if current page matches specified filename, FALSE otherwise
279
  */
342
  $parts = str_replace($sl_b, $sl_f, $parts);
343
  //Add trailing slash (if necessary)
344
  if ( $trailing_slash )
345
+ $parts .= $sl_f;
346
  return $parts;
347
+ }
348
 
349
  /**
350
  * Returns URL of file (assumes that it is in plugin directory)
351
  * @param string $file name of file get URL
352
+ * @return string File URL
353
  */
354
  function get_file_url($file) {
355
+ if ( is_string($file) && '' != trim($file) ) {
356
+ $file = str_replace(' ', '%20', $this->normalize_path($this->get_url_base(), $file));
357
+ }
358
+ return $file;
359
+ }
360
+
361
+ /**
362
+ * Returns path to plugin file
363
+ * @param string $file file name
364
+ * @return string File path
365
+ */
366
+ function get_file_path($file) {
367
+ if ( is_string($file) && '' != trim($file) ) {
368
+ $file = $this->normalize_path($this->get_path_base(), $file);
369
  }
370
  return $file;
371
  }
378
  function get_file_extension($file) {
379
  $ret = '';
380
  $sep = '.';
381
+ if ( ( $rpos = strrpos($file, $sep) ) !== false )
382
  $ret = substr($file, $rpos + 1);
383
  return $ret;
384
  }
385
 
386
  /**
387
  * Checks if file has specified extension
388
+ * @uses get_file_extension()
389
  * @param string $file File name/path
390
+ * @param string|array $extension File ending(s) to check $file for
391
  * @return bool TRUE if file has extension
392
  */
393
  function has_file_extension($file, $extension) {
394
+ if ( !is_array($extension) )
395
+ $extension = array(strval($extension));
396
+ return ( in_array($this->get_file_extension($file), $extension) ) ? true : false;
397
  }
398
 
399
  /**
400
  * Retrieve base URL for plugin-specific files
401
+ * @uses get_plugin_base()
402
+ * @uses normalize_path()
403
  * @return string Base URL
404
  */
405
  function get_url_base() {
406
  static $url_base = '';
407
  if ( '' == $url_base ) {
408
+ $url_base = $this->normalize_path(plugins_url(), $this->get_plugin_base());
409
  }
410
  return $url_base;
411
  }
412
 
413
+ /**
414
+ * Retrieve plugin's base path
415
+ * @uses WP_PLUGIN_DIR
416
+ * @uses get_plugin_base()
417
+ * @uses normalize_path()
418
+ * @return string Base path
419
+ */
420
  function get_path_base() {
421
  static $path_base = '';
422
  if ( '' == $path_base ) {
424
  }
425
  return $path_base;
426
  }
427
+
428
+ /**
429
+ * Retrieve plugin's base directory
430
+ * @uses WP_PLUGIN_DIR
431
+ * @uses normalize_path()
432
+ * @return string Base directory
433
+ */
434
  function get_plugin_base() {
435
  static $plugin_dir = '';
436
  if ( '' == $plugin_dir ) {
439
  return $plugin_dir;
440
  }
441
 
442
+ /**
443
+ * Retrieve plugin's base file path
444
+ * @uses get_path_base()
445
+ * @uses get_file_path()
446
+ * @return string Base file path
447
+ */
448
  function get_plugin_base_file() {
449
+ static $file = '';
450
+ global $cnr;
451
+ if ( empty($file) ) {
452
+ $dir = @ opendir($this->get_path_base());
453
+ if ( $dir ) {
454
+ while ( ($ftemp = readdir($dir)) !== false ) {
455
+ //Only process PHP files
456
+ $ftemp = $this->get_file_path($ftemp);
457
+ if ( !$this->has_file_extension($ftemp, 'php') || !is_readable($ftemp) )
458
+ continue;
459
+ //Check for data
460
+ $data = get_file_data($ftemp, $this->plugin_headers);
461
+ if ( !empty($data['Name']) ) {
462
+ //Set base file
463
+ $file = $ftemp;
464
+ break;
465
+ }
466
+ }
467
+ }
468
+ @closedir($dir);
469
+ }
470
+ //Return
471
+ return $file;
472
  }
473
 
474
+ /**
475
+ * Retrieve plugin's internal name
476
+ * Internal name is used by WP core
477
+ * @uses get_plugin_base_file()
478
+ * @uses plugin_basename()
479
+ * @return string Internal plugin name
480
+ */
481
  function get_plugin_base_name() {
482
+ static $name = false;
483
+ if ( !$name ) {
484
+ $file = $this->get_plugin_base_file();
485
+ $name = plugin_basename($file);
486
+ }
487
+ return $name;
488
+ }
489
+
490
+ /**
491
+ * Retrieve plugin info
492
+ * Parses info comment in main plugin file
493
+ * @uses get_plugin_base_file()
494
+ */
495
+ function get_plugin_info($field = '') {
496
+ static $data = array();
497
+ $ret = '';
498
+ //Get plugin data
499
+ if ( empty($data) ) {
500
+ $file = $this->get_plugin_base_file();
501
+ $data = get_file_data($file, $this->plugin_headers);
502
+ }
503
+ //Return specified field
504
+ if ( !empty($field) ) {
505
+ if ( isset($data[$field]) )
506
+ $ret = $data[$field];
507
+ } else {
508
+ $ret = $data;
509
+ }
510
+ return $ret;
511
+ }
512
+
513
+ /**
514
+ * Retrieve plugin version
515
+ * @uses get_plugin_info()
516
+ * @param bool $strip_desc Strip any additional version text
517
+ * @return string Plugin version
518
+ */
519
+ function get_plugin_version($strip_desc = true) {
520
+ static $v = '';
521
+ //Retrieve version
522
+ if ( empty($v) ) {
523
+ $field = 'Version';
524
+ $v = $this->get_plugin_info($field);
525
+ }
526
+ //Format
527
+ $ret = $v;
528
+ if ( $strip_desc ) {
529
+ $ret = explode(' ', $ret, 2);
530
+ $ret = $ret[0];
531
+ }
532
+ //Return
533
+ return $ret;
534
  }
535
 
536
  /**
571
 
572
  /*-** General **-*/
573
 
574
+ /**
575
+ * Checks if last parameter sent to a function is an array of options and returns it
576
+ * Calling function should use `func_get_args()` and pass the value to this method
577
+ * @param array $args Parameters passed to calling function
578
+ * @return array Options array (Default: empty array)
579
+ */
580
+ function func_get_options($args) {
581
+ $r = array();
582
+ if ( is_array($args) && !empty($args) ) {
583
+ $last = count($args) - 1;
584
+ if ( is_array($args[$last]) )
585
+ $r = $args[$last];
586
+ }
587
+ return $r;
588
+ }
589
+
590
  /**
591
  * Checks if a property exists in a class or object
592
  * (Compatibility method for PHP 4
625
  }
626
  }
627
 
628
+ /**
629
+ * Remap array members based on a
630
+ * mapping of source/destination keys
631
+ * @param array $properties Associative array of properties
632
+ * @param array $map Source/Destination mapping
633
+ * > Key: Source member
634
+ * > Val: Destination member
635
+ * @param bool $overwrite If TRUE, source value will be set in destination regardless of whether member already exists or not
636
+ * @return array Remapped properties
637
+ */
638
+ function array_remap($arr, $map = array(), $overwrite = false) {
639
+ if ( !empty($map) && is_array($map) ) {
640
+ //Iterate through mappings
641
+ foreach ( $map as $from => $to ) {
642
+ if ( !array_key_exists($from, $arr) )
643
+ continue;
644
+ $move = $overwrite;
645
+ //Only remap if parent property doesn't already exist in array
646
+ if ( !array_key_exists($to, $arr) )
647
+ $move = true;
648
+ if ( $move ) {
649
+ //Move member value to new key
650
+ $arr[$to] = $arr[$from];
651
+ //Remove source member
652
+ unset($arr[$from]);
653
+ }
654
+ }
655
+ }
656
+ //Return remapped properties
657
+ return $arr;
658
+ }
659
+
660
+ function array_filter_keys($arr, $keys) {
661
+ if ( is_array($arr) && !empty($arr) && is_array($keys) && !empty($keys) ) {
662
+ foreach ( $keys as $rem ) {
663
+ if ( array_key_exists($rem, $arr) )
664
+ unset($arr[$rem]);
665
+ }
666
+ }
667
+
668
+ return $arr;
669
+ }
670
+
671
+ /**
672
+ * Insert an item into an array at the specified position
673
+ * @param mixed $item Item to insert into array
674
+ * @param int $pos Index position to insert item into array
675
+ * @return array Modified array
676
+ */
677
+ function array_insert($array, $item, $pos = null) {
678
+ array_splice($array, $pos, 0, $item);
679
+ return $array;
680
+ }
681
+
682
  /**
683
  * Merges 1 or more arrays together
684
  * Methodology
690
  * - Merge item in base array with current item based on key name
691
  * - If the current item's value AND the corresponding item in the base array are BOTH arrays, recursively merge the the arrays
692
  * - If the current item's value OR the corresponding item in the base array is NOT an array, current item overwrites base item
 
693
  * @param array Variable number of arrays
694
  * @param array $arr1 Default array
695
  * @return array Merged array
697
  function array_merge_recursive_distinct($arr1) {
698
  //Get all arrays passed to function
699
  $args = func_get_args();
700
+ if ( empty($args) )
701
  return false;
702
+ //Return empty array if first parameter is not an array
703
+ if ( !is_array($args[0]) )
704
+ return array();
705
  //Set first array as base array
706
  $merged = $args[0];
707
  //Iterate through arrays to merge
708
  $arg_length = count($args);
709
+ for ( $x = 1; $x < $arg_length; $x++ ) {
710
  //Skip if argument is not an array (only merge arrays)
711
+ if ( !is_array($args[$x]) )
712
  continue;
713
  //Iterate through argument items
714
+ foreach ( $args[$x] as $key => $val ) {
715
+ //Generate key for numeric indexes
716
+ if ( is_int($key) ) {
717
+ //Add new item to merged array
718
+ $merged[] = null;
719
+ //Get key of new item
720
+ $key = array_pop(array_keys($merged));
721
+ }
722
+ if ( !isset($merged[$key]) || !is_array($merged[$key]) || !is_array($val) ) {
723
+ $merged[$key] = $val;
724
+ } elseif ( is_array($merged[$key]) && is_array($val) ) {
725
+ $merged[$key] = $this->array_merge_recursive_distinct($merged[$key], $val);
726
+ }
727
  }
728
  }
729
  return $merged;
778
  return $item;
779
  }
780
 
781
+ /**
782
+ * Build formatted string based on array values
783
+ * Array values in formatted string will be ordered by index order
784
+ * @param array $attribute Values to build string with
785
+ * @param string $format (optional) Format name (Default: Multidimensional array representation > ['value1']['value2']['value3'], etc.)
786
+ * @return string Formatted string based on array values
787
+ */
788
  function get_array_path($attribute = '', $format = null) {
789
  //Formatted value
790
  $fmtd = '';
880
  return $this->build_html_element(array('tag' => 'link', 'wrap' => false, 'attributes' => $attributes));
881
  }
882
 
883
+ function build_script_element($content = '', $id = '') {
884
+ $attributes = array('type' => 'text/javascript');
885
+ $content = '/* <![CDATA[ */' . $content . '/* ]]> */';
886
+ if ( is_string($id) && !empty($id) ) {
887
+ $attributes['id'] = $id;
888
+ }
889
+ return $this->build_html_element(array('tag' => 'script', 'content' => $content, 'attributes' => $attributes));
890
+ }
891
+
892
  /**
893
  * Generate external script element
894
  * @param $url Script URL
961
  //Get last submenu added
962
  $parent = $this->get_submenu_parent_file($parent);
963
  if ( isset($submenu[$parent]) ) {
964
+ $subs =& $submenu[$parent];
965
+
966
+ //Make sure menu isn't already in the desired position
967
+ if ( $pos <= ( count($subs) - 1 ) ) {
968
+ //Get submenu that was just added
969
+ $sub = array_pop($subs);
970
+ //Insert into desired position
971
+ if ( 0 == $pos ) {
972
+ array_unshift($subs, $sub);
973
+ } else {
974
+ $top = array_slice($subs, 0, $pos);
975
+ $bottom = array_slice($subs, $pos);
976
+ array_push($top, $sub);
977
+ $subs = array_merge($top, $bottom);
978
  }
979
  }
980
  }
981
+ }
982
 
983
  return $hookname;
984
  }
js/dev/effects.js DELETED
@@ -1,1094 +0,0 @@
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();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
js/dev/lib.dev.js ADDED
@@ -0,0 +1,1018 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // -----------------------------------------------------------------------------------
2
+ //
3
+ // Simple Lightbox
4
+ // by Archetyped - http://archetyped.com/tools/simple-lightbox/
5
+ // Updated: 2011-01-27
6
+ //
7
+ // Originally based on Lightbox Slideshow v1.1
8
+ // by Justin Barkhuff - http://www.justinbarkhuff.com/lab/lightbox_slideshow/
9
+ // 2007/08/15
10
+ //
11
+ // Largely based on Lightbox v2.02
12
+ // by Lokesh Dhakar - http://huddletogether.com/projects/lightbox2/
13
+ // 2006/03/31
14
+ //
15
+ // Licensed under the Creative Commons Attribution 2.5 License - http://creativecommons.org/licenses/by/2.5/
16
+ //
17
+ // -----------------------------------------------------------------------------------
18
+ /**
19
+ * Lightbox object
20
+ */
21
+ //var SLB = null;
22
+ (function($) {
23
+ SLB = {
24
+ activeImage : null,
25
+ badObjects : ['select','object','embed'],
26
+ container : null,
27
+ enableSlideshow : null,
28
+ groupName : null,
29
+ imageArray : [],
30
+ options : null,
31
+ overlayDuration : null,
32
+ overlayOpacity : null,
33
+ playSlides : null,
34
+ refTags : ['a'],
35
+ relAttribute : null,
36
+ resizeDuration : null,
37
+ slideShowTimer : null,
38
+ startImage : null,
39
+ prefix : '',
40
+ checkedUrls : {},
41
+ media : {},
42
+
43
+ /**
44
+ * Initialize lightbox instance
45
+ * @param object options Instance options
46
+ */
47
+ initialize: function(options) {
48
+ this.options = $.extend(true, {
49
+ animate : true, // resizing animations
50
+ validateLinks : false, //Validate links before adding them to lightbox
51
+ captionEnabled: true, //Display caption
52
+ captionSrc : true, //Use image source URI if title not set
53
+ descEnabled: true, //Display description
54
+ autoPlay : true, // should slideshow start automatically
55
+ borderSize : 10, // if you adjust the padding in the CSS, you will need to update this variable
56
+ containerID : document, // lightbox container object
57
+ enableSlideshow : true, // enable slideshow feature
58
+ googleAnalytics : false, // track individual image views using Google Analytics
59
+ imageDataLocation : 'south', // location of image caption information
60
+ initImage : '', // ID of image link to automatically launch when upon script initialization
61
+ loop : true, // whether to continuously loop slideshow images
62
+ overlayDuration : .2, // time to fade in shadow overlay
63
+ overlayOpacity : .8, // transparency of shadow overlay
64
+ relAttribute : null, // specifies the rel attribute value that triggers lightbox
65
+ resizeSpeed : 400, // controls the speed of the image resizing (milliseconds)
66
+ showGroupName : false, // show group name of images in image details
67
+ slideTime : 4, // time to display images during slideshow
68
+ altsrc : 'src',
69
+ mId : 'id',
70
+ strings : { // allows for localization
71
+ closeLink : 'close',
72
+ loadingMsg : 'loading',
73
+ nextLink : 'next &raquo;',
74
+ prevLink : '&laquo; prev',
75
+ startSlideshow : 'start slideshow',
76
+ stopSlideshow : 'stop slideshow',
77
+ numDisplayPrefix : 'Image',
78
+ numDisplaySeparator : 'of'
79
+ },
80
+ placeholders : {
81
+ slbContent: '<img id="slb_slbContent" />',
82
+ slbLoading: '<span id="slb_slbLoading">loading</span>',
83
+ slbClose: '<a class="slb_slbClose" href="#">close</a>',
84
+ navPrev: '<a class="slb_navPrev slb_nav" href="#">&laquo; prev</a>',
85
+ navNext: '<a class="slb_navNext slb_nav" href="#">&raquo; next</a>',
86
+ navSlideControl: '<a class="slb_navSlideControl" href="#">Stop</a>',
87
+ dataCaption: '<span class="slb_dataCaption"></span>',
88
+ dataDescription: '<span class="slb_dataDescription"></span>',
89
+ dataNumber: '<span class="slb_dataNumber"></span>'
90
+ },
91
+ layout : null
92
+ }, options);
93
+
94
+ //Stop if no layout is defined
95
+ if (!this.options.layout || this.options.layout.toString().length == 0)
96
+ this.end();
97
+
98
+
99
+ //Validate options
100
+ if ( 'prefix' in this.options )
101
+ this.prefix = this.options.prefix;
102
+ //Activation Attribute
103
+ if ( null == this.options.relAttribute )
104
+ this.options.relAttribute = [this.prefix];
105
+ else if ( !$.isArray(this.options.relAttribute) )
106
+ this.options.relAttribute = [this.options.relAttribute.toString()];
107
+ this.relAttribute = this.options.relAttribute;
108
+
109
+ if ( this.options.animate ) {
110
+ this.overlayDuration = Math.max(this.options.overlayDuration,0);
111
+ this.resizeDuration = this.options.resizeSpeed;
112
+ } else {
113
+ this.overlayDuration = 0;
114
+ this.resizeDuration = 0;
115
+ }
116
+ this.enableSlideshow = this.options.enableSlideshow;
117
+ this.overlayOpacity = Math.max(Math.min(this.options.overlayOpacity,1),0);
118
+ this.playSlides = this.options.autoPlay;
119
+ this.container = $(this.options.containerID);
120
+ this.updateImageList();
121
+ var t = this;
122
+ var objBody = $(this.container).get(0) != document ? this.container : $('body');
123
+
124
+ var objOverlay = $('<div/>', {
125
+ 'id': this.getID('overlay'),
126
+ 'css': {'display': 'none'}
127
+ }).appendTo(objBody)
128
+ .click(function() {t.end()});
129
+
130
+ var objLightbox = $('<div/>', {
131
+ 'id': this.getID('lightbox'),
132
+ 'css': {'display': 'none'}
133
+ }).appendTo(objBody)
134
+ .click(function() {t.end()});
135
+
136
+ //Build layout from template
137
+ var layout = this.getLayout();
138
+
139
+ //Append to container
140
+ $(layout).appendTo(objLightbox);
141
+
142
+ //Set UI
143
+ this.setUI();
144
+
145
+ //Add events
146
+ this.setEvents();
147
+
148
+ if (this.options.initImage != '') {
149
+ this.start($(this.options.initImage));
150
+ }
151
+ },
152
+
153
+ /**
154
+ * Build layout from template
155
+ * @uses options.layout
156
+ * @return string Layout markup (HTML)
157
+ */
158
+ getLayout: function() {
159
+ var l = this.options.layout;
160
+
161
+ //Expand placeholders
162
+ var ph, phs, phr;
163
+ for (ph in this.options.placeholders) {
164
+ phs = '{' + ph + '}';
165
+ //Continue to next placeholder if current one is not in layout
166
+ if (l.indexOf(phs) == -1)
167
+ continue;
168
+ phr = new RegExp(phs, "g");
169
+ l = l.replace(phr, this.options.placeholders[ph]);
170
+ }
171
+
172
+ //Return final layout
173
+ return l;
174
+
175
+ },
176
+
177
+ /**
178
+ * Set localized values for UI elements
179
+ */
180
+ setUI: function() {
181
+ var s = this.options.strings;
182
+ this.get('slbClose').html(s.closeLink);
183
+ this.get('navNext').html(s.nextLink);
184
+ this.get('navPrev').html(s.prevLink);
185
+ this.get('navSlideControl').html(((this.playSlides) ? s.stopSlideshow : s.startSlideshow));
186
+ },
187
+
188
+ /**
189
+ * Add events to various UI elements
190
+ */
191
+ setEvents: function() {
192
+ var t = this, delay = 500;
193
+ this.get('container,details').click(function(ev) {
194
+ ev.stopPropagation();
195
+ });
196
+
197
+ var clickP = function() {
198
+ t.get('navPrev').unbind('click').click(false);
199
+ setTimeout(function() {t.get('navPrev').click(clickP)}, delay);
200
+ t.showPrev();
201
+ return false;
202
+ };
203
+ this.get('navPrev').click(function(){
204
+ return clickP();
205
+ });
206
+
207
+ var clickN = function() {
208
+ t.get('navNext').unbind('click').click(false);
209
+ setTimeout(function() {t.get('navNext').click(clickN)}, delay);
210
+ t.showNext();
211
+ return false;
212
+ };
213
+ this.get('navNext').click(function() {
214
+ return clickN();
215
+ });
216
+
217
+ this.get('navSlideControl').click(function() {
218
+ t.toggleSlideShow();
219
+ return false;
220
+ });
221
+ this.get('slbClose').click(function() {
222
+ t.end();
223
+ return false;
224
+ });
225
+ },
226
+
227
+ /**
228
+ * Finds all compatible image links on page
229
+ */
230
+ updateImageList: function() {
231
+ var el, els, rel, ph = '{relattr}', t = this;
232
+ var sel = [], selBase = '[href][rel*="' + ph + '"]:not([rel~="' + this.addPrefix('off') + '"])';
233
+
234
+ //Define event handler
235
+ var handler = function() {
236
+ //Check if element is valid for lightbox
237
+ t.start(this);
238
+ return false;
239
+ };
240
+
241
+ //Build selector
242
+ for (var i = 0; i < this.refTags.length; i++) {
243
+ for (var x = 0; x < this.relAttribute.length; x++) {
244
+ sel.push(this.refTags[i] + selBase.replace(ph, this.relAttribute[x]));
245
+ }
246
+ }
247
+ sel = sel.join(',');
248
+ //Add event handler to links
249
+ $(sel, $(this.container)).live('click', handler);
250
+ },
251
+
252
+ /**
253
+ * Display overlay and lightbox. If image is part of a set, add siblings to imageArray.
254
+ * @param node imageLink Link element containing image URL
255
+ */
256
+ start: function(imageLink) {
257
+ imageLink = $(imageLink);
258
+ this.hideBadObjects();
259
+
260
+ this.imageArray = [];
261
+ this.groupName = this.getGroup(imageLink);
262
+
263
+ var rel = $(imageLink).attr('rel') || '';
264
+ var imageTitle = '';
265
+ var t = this;
266
+ var groupTemp = {};
267
+ this.fileExists(this.getSourceFile(imageLink),
268
+ function() { //File exists
269
+ // Stretch overlay to fill page and fade in
270
+ t.get('overlay')
271
+ .height($(document).height())
272
+ .fadeTo(t.overlayDuration, t.overlayOpacity);
273
+
274
+ // Add image to array closure
275
+ var addLink = function(el, idx) {
276
+ groupTemp[idx] = el;
277
+ return groupTemp.length;
278
+ };
279
+
280
+ //Build final image array & launch lightbox
281
+ var proceed = function() {
282
+ t.startImage = 0;
283
+ //Sort links by document order
284
+ var order = [], el;
285
+ for (var x in groupTemp) {
286
+ order.push(x);
287
+ }
288
+ order.sort(function(a, b) { return (a - b); });
289
+ for (x = 0; x < order.length; x++) {
290
+ el = groupTemp[order[x]];
291
+ //Check if link being evaluated is the same as the clicked link
292
+ if ($(el).get(0) == $(imageLink).get(0)) {
293
+ t.startImage = x;
294
+ }
295
+ t.imageArray.push({'link':t.getSourceFile($(el)), 'title':t.getCaption(el), 'desc': t.getDescription(el)});
296
+ }
297
+ // Calculate top offset for the lightbox and display
298
+ var lightboxTop = $(document).scrollTop() + ($(window).height() / 15);
299
+
300
+ t.get('lightbox').css('top', lightboxTop + 'px').show();
301
+ t.changeImage(t.startImage);
302
+ }
303
+
304
+ // If image is NOT part of a group..
305
+ if (null == t.groupName) {
306
+ // Add single image to imageArray
307
+ addLink(imageLink, 0);
308
+ t.startImage = 0;
309
+ proceed();
310
+ } else {
311
+ // If image is part of a group
312
+ var els = $(t.container).find($(imageLink).get(0).tagName.toLowerCase());
313
+ // Loop through links on page & find other images in group
314
+ var grpLinks = [];
315
+ var i, el;
316
+ for (i = 0; i < els.length; i++) {
317
+ el = $(els[i]);
318
+ if (t.getSourceFile(el) && (t.getGroup(el) == t.groupName)) {
319
+ //Add links in same group to temp array
320
+ grpLinks.push(el);
321
+ }
322
+ }
323
+
324
+ //Loop through group links, validate, and add to imageArray
325
+ var processed = 0;
326
+ for (i = 0; i < grpLinks.length; i++) {
327
+ el = grpLinks[i];
328
+ t.fileExists(t.getSourceFile($(el)),
329
+ function(args) { //File exists
330
+ var el = args.els[args.idx];
331
+ var il = addLink(el, args.idx);
332
+ processed++;
333
+ if (processed == args.els.length)
334
+ proceed();
335
+ },
336
+ function(args) { //File does not exist
337
+ processed++;
338
+ if (args.idx == args.els.length)
339
+ proceed();
340
+ },
341
+ {'idx': i, 'els': grpLinks});
342
+ }
343
+ }
344
+ },
345
+ function() { //File does not exist
346
+ t.end();
347
+ });
348
+ },
349
+
350
+ /**
351
+ * Retrieve ID of media item
352
+ * @param {Object} el Link element
353
+ * @return int Media ID (Default: 0 - No ID)
354
+ */
355
+ getMediaId: function(el) {
356
+ var rel = $(el).attr('rel') || '',
357
+ mId = 0;
358
+ if (rel.length) {
359
+ var reId = new RegExp('\\b' + this.addPrefix(this.options.mId) + '\\[(.+?)\\](?:\\b|$)');
360
+ if (reId.test(rel)) {
361
+ mId = reId.exec(rel)[1];
362
+ }
363
+ }
364
+ return mId;
365
+ },
366
+
367
+ /**
368
+ * Retrieve Media properties
369
+ * @param {Object} el Link element
370
+ * @return Object (Default: Empty)
371
+ */
372
+ getMediaProperties: function(el) {
373
+ var props = {},
374
+ mId = this.getMediaId(el);
375
+ if (mId in this.media) {
376
+ props = this.media[mId];
377
+ }
378
+ return props;
379
+ },
380
+
381
+ /**
382
+ * Retrieve single property for media item
383
+ * @param {Object} el Link element
384
+ * @param string prop Property to retrieve
385
+ * @return mixed Item property (Default: false)
386
+ */
387
+ getMediaProperty: function(el, prop) {
388
+ var props = this.getMediaProperties(el);
389
+ return (prop in props) ? props[prop] : false;
390
+ },
391
+
392
+ /**
393
+ * Build caption for displayed caption
394
+ * @param {Object} imageLink
395
+ */
396
+ getCaption: function(imageLink) {
397
+ imageLink = $(imageLink);
398
+ var caption = '';
399
+ if (this.options.captionEnabled) {
400
+ var sels = {
401
+ 'capt': '.wp-caption-text',
402
+ 'gIcon': '.gallery-icon'
403
+ };
404
+ var els = {
405
+ 'link': imageLink,
406
+ 'origin': imageLink,
407
+ 'sibs': null,
408
+ 'img': null
409
+ }
410
+ //WP Caption
411
+ if ( $(els.link).parent(sels.gIcon).length > 0 ) {
412
+ els.origin = $(els.link).parent();
413
+ }
414
+ if ( (els.sibs = $(els.origin).siblings(sels.capt)) && $(els.sibs).length > 0 ) {
415
+ caption = $(els.sibs).first().text();
416
+ }
417
+ caption = $.trim(caption);
418
+ //Fall back to image properties
419
+ if ( '' == caption ) {
420
+ els.img = $(els.link).find('img').first();
421
+ if ( $(els.img).length ) {
422
+ //Image title / alt
423
+ caption = $(els.img).attr('title') || $(els.img).attr('alt');
424
+ }
425
+ }
426
+ caption = $.trim(caption);
427
+ //Fall back Link Text
428
+ if ('' == caption) {
429
+ if ($.trim($(sels.link).text()).length) {
430
+ caption = $.trim($(sels.link).text());
431
+ } else if (this.options.captionSrc) {
432
+ //Fall back to Link href
433
+ caption = $(sels.link).attr('href');
434
+ }
435
+ }
436
+ caption = $.trim(caption);
437
+ }
438
+ return caption;
439
+ },
440
+
441
+ /**
442
+ * Retrieve item description
443
+ * @param {Object} imageLink
444
+ * @return string Item description (Default: empty string)
445
+ */
446
+ getDescription: function(imageLink) {
447
+ var desc = '';
448
+ if (this.options.descEnabled) {
449
+ //Retrieve description
450
+ if (this.inGallery(imageLink, 'ng')) {
451
+ desc = $(imageLink).attr('title');
452
+ }
453
+ else
454
+ desc = this.getMediaProperty(imageLink, 'desc');
455
+
456
+ if (!desc)
457
+ desc = '';
458
+ }
459
+ return desc;
460
+ },
461
+
462
+ /**
463
+ * Check if current link is part of a gallery
464
+ * @param {Object} imageLink
465
+ * @param string gType Gallery type to check for
466
+ * @return bool Whether link is part of a gallery
467
+ */
468
+ inGallery: function(imageLink, gType) {
469
+ var ret = false;
470
+ var galls = {
471
+ 'wp': '.gallery-icon',
472
+ 'ng': '.ngg-gallery-thumbnail'
473
+ };
474
+
475
+
476
+ if ( typeof gType == 'undefined' || !(gType in galls) ) {
477
+ gType = 'wp';
478
+ }
479
+ return ( ( $(imageLink).parent(galls[gType]).length > 0 ) ? true : false );
480
+ },
481
+
482
+ /**
483
+ * Retrieve source URI in link
484
+ * @param {Object} el
485
+ * @return string Source file URI
486
+ */
487
+ getSourceFile: function(el) {
488
+ var src = $(el).attr('href');
489
+ var rel = $(el).attr('rel') || '';
490
+ if (rel.length) {
491
+ //Attachment source
492
+ relSrc = this.getMediaProperty(el, 'source');
493
+ //Explicit source
494
+ if (!relSrc || !relSrc.length) {
495
+ var reSrc = new RegExp('\\b' + this.addPrefix(this.options.altsrc) + '\\[(.+?)\\](?:\\b|$)');
496
+ if (reSrc.test(rel)) {
497
+ relSrc = reSrc.exec(rel)[1];
498
+ }
499
+ }
500
+ //Set source using rel-derived value
501
+ if ( relSrc.length )
502
+ src = relSrc;
503
+ }
504
+ return src;
505
+ },
506
+
507
+ /**
508
+ * Extract group name from
509
+ * @param obj el Element to extract group name from
510
+ * @return string Group name
511
+ */
512
+ getGroup: function(el) {
513
+ //Get full attribute value
514
+ var g = null;
515
+ var rel = $(el).attr('rel') || '';
516
+ if (rel != '') {
517
+ var gTmp = '',
518
+ gSt = '[',
519
+ gEnd = ']',
520
+ search = '',
521
+ idx,
522
+ prefix = ' ';
523
+ //Iterate through attributes to find group
524
+ for (var i = 0; i < this.relAttribute.length; i++) {
525
+ search = this.relAttribute[i];
526
+ idx = rel.indexOf(search);
527
+ //Prefix with space to find whole word
528
+ if (prefix != search.charAt(0) && idx > 0) {
529
+ search = prefix + search;
530
+ idx = rel.indexOf(search);
531
+ }
532
+ //Stop processing if value is not found
533
+ if (idx == -1)
534
+ continue;
535
+ gTmp = $.trim(rel.substring(idx).replace(search, ''));
536
+ //Check if group defined
537
+ if (gTmp.length && gSt == gTmp.charAt(0) && gTmp.indexOf(gEnd) != -1) {
538
+ //Extract group name
539
+ g = gTmp.substring(1, gTmp.indexOf(gEnd));
540
+ continue;
541
+ }
542
+ }
543
+ }
544
+ return g;
545
+ },
546
+
547
+ /**
548
+ * Preload requested image prior to displaying it in lightbox
549
+ * @param int imageNum Index of image in imageArray property
550
+ * @uses imageArray to retrieve index at specified image
551
+ * @uses resizeImageContainer() to resize lightbox after image has loaded
552
+ */
553
+ changeImage: function(imageNum) {
554
+ this.activeImage = imageNum;
555
+
556
+ this.disableKeyboardNav();
557
+ this.pauseSlideShow();
558
+
559
+ // hide elements during transition
560
+ this.get('slbLoading').show();
561
+ this.get('slbContent').hide();
562
+ this.get('details').hide();
563
+ var imgPreloader = new Image();
564
+ var t = this;
565
+ // once image is preloaded, resize image container
566
+ $(imgPreloader).bind('load', function() {
567
+ t.get('slbContent').attr('src', imgPreloader.src);
568
+ t.resizeImageContainer(imgPreloader.width, imgPreloader.height);
569
+ //Restart slideshow if active
570
+ if ( t.isSlideShowActive() )
571
+ t.startSlideShow();
572
+ });
573
+
574
+ //Load image
575
+ imgPreloader.src = this.imageArray[this.activeImage].link;
576
+ },
577
+
578
+ /**
579
+ * Resizes lightbox to fit image
580
+ * @param int imgWidth Image width in pixels
581
+ * @param int imgHeight Image height in pixels
582
+ */
583
+ resizeImageContainer: function(imgWidth, imgHeight) {
584
+ // get current height and width
585
+ var el = this.get('container');
586
+ var borderSize = this.options.borderSize * 2;
587
+
588
+ this.get('container').animate({width: imgWidth + borderSize, height: imgHeight + borderSize}, this.resizeDuration)
589
+
590
+ this.showImage();
591
+ },
592
+
593
+ /**
594
+ * Display image and begin preloading neighbors.
595
+ */
596
+ showImage: function() {
597
+ this.get('slbLoading').hide();
598
+ var t = this;
599
+ this.get('slbContent').fadeIn(500, function() { t.updateDetails(); });
600
+ this.preloadNeighborImages();
601
+ },
602
+
603
+ /**
604
+ * Display caption, image number, and bottom nav
605
+ */
606
+ updateDetails: function() {
607
+ //Caption
608
+ if (this.options.captionEnabled) {
609
+ this.get('dataCaption').text(this.imageArray[this.activeImage].title);
610
+ this.get('dataCaption').show();
611
+ } else {
612
+ this.get('dataCaption').hide();
613
+ }
614
+
615
+ //Description
616
+ this.get('dataDescription').html(this.imageArray[this.activeImage].desc);
617
+
618
+ // if image is part of set display 'Image x of y'
619
+ if (this.hasImages()) {
620
+ var num_display = this.options.strings.numDisplayPrefix + ' ' + (this.activeImage + 1) + ' ' + this.options.strings.numDisplaySeparator + ' ' + this.imageArray.length;
621
+ if (this.options.showGroupName && this.groupName != '') {
622
+ num_display += ' ' + this.options.strings.numDisplaySeparator + ' ' + this.groupName;
623
+ }
624
+ this.get('dataNumber')
625
+ .text(num_display)
626
+ .show();
627
+ }
628
+
629
+ this.get('details').width(this.get('slbContent').width() + (this.options.borderSize * 2));
630
+ this.updateNav();
631
+ var t = this;
632
+ this.get('details').animate({height: 'show', opacity: 'show'}, 650);
633
+ },
634
+
635
+ /**
636
+ * Display appropriate previous and next hover navigation.
637
+ */
638
+ updateNav: function() {
639
+ if (this.hasImages()) {
640
+ this.get('navPrev').show();
641
+ this.get('navNext').show();
642
+ if (this.enableSlideshow) {
643
+ this.get('navSlideControl').show();
644
+ if (this.playSlides) {
645
+ this.startSlideShow();
646
+ } else {
647
+ this.stopSlideShow();
648
+ }
649
+ } else {
650
+ this.get('navSlideControl').hide();
651
+ }
652
+ } else {
653
+ // Hide navigation controls when only one image exists
654
+ this.get('dataNumber').hide();
655
+ this.get('navPrev').hide();
656
+ this.get('navNext').hide();
657
+ this.get('navSlideControl').hide();
658
+ }
659
+ this.enableKeyboardNav();
660
+ },
661
+
662
+ /**
663
+ * Checks if slideshow is currently activated
664
+ * @return bool TRUE if slideshow is active, FALSE otherwise
665
+ * @uses playSlides to check slideshow activation status
666
+ */
667
+ isSlideShowActive: function() {
668
+ return this.playSlides;
669
+ },
670
+
671
+ /**
672
+ * Start the slideshow
673
+ */
674
+ startSlideShow: function() {
675
+ this.playSlides = true;
676
+ var t = this;
677
+ clearInterval(this.slideShowTimer);
678
+ this.slideShowTimer = setInterval(function() { t.showNext(); t.pauseSlideShow(); }, this.options.slideTime * 1000);
679
+ this.get('navSlideControl').text(this.options.strings.stopSlideshow);
680
+ },
681
+
682
+ /**
683
+ * Stop the slideshow
684
+ */
685
+ stopSlideShow: function() {
686
+ this.playSlides = false;
687
+ if (this.slideShowTimer) {
688
+ clearInterval(this.slideShowTimer);
689
+ }
690
+ this.get('navSlideControl').text(this.options.strings.startSlideshow);
691
+ },
692
+
693
+ /**
694
+ * Toggles the slideshow status
695
+ */
696
+ toggleSlideShow: function() {
697
+ if (this.playSlides) {
698
+ this.stopSlideShow();
699
+ }else{
700
+ this.startSlideShow();
701
+ }
702
+ },
703
+
704
+ /**
705
+ * Pauses the slideshow
706
+ * Stops the slideshow but does not change the slideshow's activation status
707
+ */
708
+ pauseSlideShow: function() {
709
+ if (this.slideShowTimer) {
710
+ clearInterval(this.slideShowTimer);
711
+ }
712
+ },
713
+
714
+ /**
715
+ * Check if there is at least one image to display in the lightbox
716
+ * @return bool TRUE if at least one image is found
717
+ * @uses imageArray to check for images
718
+ */
719
+ hasImage: function() {
720
+ return ( this.imageArray.length > 0 );
721
+ },
722
+
723
+ /**
724
+ * Check if there are multiple images to display in the lightbox
725
+ * @return bool TRUE if there are multiple images
726
+ * @uses imageArray to determine the number of images
727
+ */
728
+ hasImages: function() {
729
+ return ( this.imageArray.length > 1 );
730
+ },
731
+
732
+ /**
733
+ * Check if the current image is the first image in the list
734
+ * @return bool TRUE if image is first
735
+ * @uses activeImage to check index of current image
736
+ */
737
+ isFirstImage: function() {
738
+ return ( this.activeImage == 0 );
739
+ },
740
+
741
+ /**
742
+ * Check if the current image is the last image in the list
743
+ * @return bool TRUE if image is last
744
+ * @uses activeImage to check index of current image
745
+ * @uses imageArray to compare current image to total number of images
746
+ */
747
+ isLastImage: function() {
748
+ return ( this.activeImage == this.imageArray.length - 1 );
749
+ },
750
+
751
+ /**
752
+ * Show the next image in the list
753
+ */
754
+ showNext : function() {
755
+ if (this.hasImages()) {
756
+ if ( !this.options.loop && this.isLastImage() ) {
757
+ return this.end();
758
+ }
759
+ if ( this.isLastImage() ) {
760
+ this.showFirst();
761
+ } else {
762
+ this.changeImage(this.activeImage + 1);
763
+ }
764
+ }
765
+ },
766
+
767
+ /**
768
+ * Show the previous image in the list
769
+ */
770
+ showPrev : function() {
771
+ if (this.hasImages()) {
772
+ if ( !this.options.loop && this.isFirstImage() )
773
+ return this.end();
774
+ if (this.activeImage == 0) {
775
+ this.showLast();
776
+ } else {
777
+ this.changeImage(this.activeImage - 1);
778
+ }
779
+ }
780
+ },
781
+
782
+ /**
783
+ * Show the first image in the list
784
+ */
785
+ showFirst : function() {
786
+ if (this.hasImages()) {
787
+ this.changeImage(0);
788
+ }
789
+ },
790
+
791
+ /**
792
+ * Show the last image in the list
793
+ */
794
+ showLast : function() {
795
+ if (this.hasImages()) {
796
+ this.changeImage(this.imageArray.length - 1);
797
+ }
798
+ },
799
+
800
+ /**
801
+ * Enable image navigation via the keyboard
802
+ */
803
+ enableKeyboardNav: function() {
804
+ var t = this;
805
+ $(document).keydown(function(e) {
806
+ t.keyboardAction(e);
807
+ });
808
+ },
809
+
810
+ /**
811
+ * Disable image navigation via the keyboard
812
+ */
813
+ disableKeyboardNav: function() {
814
+ $(document).unbind('keydown');
815
+ },
816
+
817
+ /**
818
+ * Handler for keyboard events
819
+ * @param event e Keyboard event data
820
+ */
821
+ keyboardAction: function(e) {
822
+ if (e == null) { // ie
823
+ keycode = event.keyCode;
824
+ } else { // mozilla
825
+ keycode = e.which;
826
+ }
827
+
828
+ key = String.fromCharCode(keycode).toLowerCase();
829
+
830
+ if (keycode == 27 || key == 'x' || key == 'o' || key == 'c') { // close lightbox
831
+ this.end();
832
+ } else if (key == 'p' || key == '%') { // display previous image
833
+ this.showPrev();
834
+ } else if (key == 'n' || key =='\'') { // display next image
835
+ this.showNext();
836
+ } else if (key == 'f') { // display first image
837
+ this.showFirst();
838
+ } else if (key == 'l') { // display last image
839
+ this.showLast();
840
+ } else if (key == 's') { // toggle slideshow
841
+ if (this.hasImage() && this.options.enableSlideshow) {
842
+ this.toggleSlideShow();
843
+ }
844
+ }
845
+ },
846
+
847
+ /**
848
+ * Preloads images before/after current image
849
+ */
850
+ preloadNeighborImages: function() {
851
+ var nextImageID = this.imageArray.length - 1 == this.activeImage ? 0 : this.activeImage + 1;
852
+ nextImage = new Image();
853
+ nextImage.src = this.imageArray[nextImageID].link;
854
+
855
+ var prevImageID = this.activeImage == 0 ? this.imageArray.length - 1 : this.activeImage - 1;
856
+ prevImage = new Image();
857
+ prevImage.src = this.imageArray[prevImageID].link;
858
+ },
859
+
860
+ /**
861
+ * Close the lightbox
862
+ */
863
+ end: function() {
864
+ this.disableKeyboardNav();
865
+ this.pauseSlideShow();
866
+ this.get('lightbox').hide();
867
+ this.get('overlay').fadeOut(this.overlayDuration);
868
+ this.showBadObjects();
869
+ },
870
+
871
+ /**
872
+ * Displays objects that may conflict with the lightbox
873
+ * @param bool show (optional) Whether or not to show objects (Default: TRUE)
874
+ */
875
+ showBadObjects: function (show) {
876
+ show = ( typeof(show) == 'undefined' ) ? true : !!show;
877
+ var vis = (show) ? 'visible' : 'hidden';
878
+ $(this.badObjects.join(',')).css('visibility', vis);
879
+ },
880
+
881
+ /**
882
+ * Hides objects that may conflict with the lightbox
883
+ * @uses showBadObjects() to hide objects
884
+ */
885
+ hideBadObjects: function () {
886
+ this.showBadObjects(false);
887
+ },
888
+
889
+ /**
890
+ * Generate separator text
891
+ * @param string sep Separator text
892
+ * @return string Separator text
893
+ */
894
+ getSep: function(sep) {
895
+ return ( typeof sep == 'undefined' ) ? '_' : sep;
896
+ },
897
+
898
+ /**
899
+ * Retrieve prefix
900
+ * @return string Object prefix
901
+ */
902
+ getPrefix: function() {
903
+ return this.prefix;
904
+ },
905
+
906
+ /**
907
+ * Add prefix to text
908
+ * @param string txt Text to add prefix to
909
+ * @param string sep (optional) Separator text
910
+ * @return string Prefixed text
911
+ */
912
+ addPrefix: function(txt, sep) {
913
+ return this.getPrefix() + this.getSep(sep) + txt;
914
+ },
915
+
916
+ hasPrefix: function(txt) {
917
+ return ( txt.indexOf(this.addPrefix('')) == 0 ) ? true : false;
918
+ },
919
+
920
+ /**
921
+ * Generate formatted ID for lightbox-specific elements
922
+ * @param string id Base ID of element
923
+ * @return string Formatted ID
924
+ */
925
+ getID: function(id) {
926
+ return this.addPrefix(id);
927
+ },
928
+
929
+ /**
930
+ * Generate formatted selector for lightbox-specific elements
931
+ * Compares specified ID to placeholders first, then named elements
932
+ * Multiple selectors can be included and separated by commas (',')
933
+ * @param string id Base ID of element
934
+ * @uses options.placeholders to compare id to placeholder names
935
+ * @return string Formatted selector
936
+ */
937
+ getSel: function(id) {
938
+ //Process multiple selectors
939
+ var delim = ',', prefix = '#', sel;
940
+ if (id.toString().indexOf(delim) != -1) {
941
+ //Split
942
+ var sels = id.toString().split(delim);
943
+ //Build selector
944
+ for (var x = 0; x < sels.length; x++) {
945
+ sels[x] = this.getSel($.trim(sels[x]));
946
+ }
947
+ //Join
948
+ sel = sels.join(delim);
949
+ } else {
950
+ //Single selector
951
+ if (id in this.options.placeholders) {
952
+ var ph = $(this.options.placeholders[id]);
953
+ if (!ph.attr('id')) {
954
+ //Class selector
955
+ prefix = '.';
956
+ }
957
+ }
958
+ sel = prefix + this.getID(id);
959
+ }
960
+
961
+ return sel;
962
+ },
963
+
964
+ /**
965
+ * Retrieve lightbox-specific element
966
+ * @param string id Base ID of element
967
+ * @uses getSel() to generate formatted selector for element
968
+ * @return object jQuery object of selected element(s)
969
+ */
970
+ get: function(id) {
971
+ return $(this.getSel(id));
972
+ },
973
+
974
+ /**
975
+ * Checks if file exists using AJAX request
976
+ * @param string url File URL
977
+ * @param callback success Callback to run if file exists
978
+ * @param callback failure Callback to run if file does not exist
979
+ * @param obj args Arguments for callback
980
+ */
981
+ fileExists: function(url, success, failure, args) {
982
+ if (!this.options.validateLinks)
983
+ return success(args);
984
+ var statusFail = 400;
985
+ var stateCheck = 4;
986
+ var t = this;
987
+ var proceed = function(res) {
988
+ if (res.status < statusFail) {
989
+ if ($.isFunction(success))
990
+ success(args);
991
+ } else {
992
+ if ($.isFunction(failure))
993
+ failure(args);
994
+ }
995
+ };
996
+
997
+ //Check if URL already processed
998
+ if (url in this.checkedUrls) {
999
+ proceed(this.checkedUrls[url]);
1000
+ } else {
1001
+ var req = new XMLHttpRequest();
1002
+ req.open('HEAD', url, true);
1003
+ req.onreadystatechange = function() {
1004
+ if (stateCheck == this.readyState) {
1005
+ t.addUrl(url, this);
1006
+ proceed(this);
1007
+ }
1008
+ };
1009
+ req.send();
1010
+ }
1011
+ },
1012
+
1013
+ addUrl: function(url, res) {
1014
+ if (!(url in this.checkedUrls))
1015
+ this.checkedUrls[url] = res;
1016
+ }
1017
+ }
1018
+ })(jQuery);
js/dev/lightbox.js DELETED
@@ -1,719 +0,0 @@
1
- // -----------------------------------------------------------------------------------
2
- //
3
- // Simple Lightbox
4
- // by Archetyped - http://archetyped.com/tools/simple-lightbox/
5
- // Updated: 2010-06-11
6
- //
7
- // Largely based on Lightbox Slideshow v1.1
8
- // by Justin Barkhuff - http://www.justinbarkhuff.com/lab/lightbox_slideshow/
9
- // 2007/08/15
10
- //
11
- // Largely based on Lightbox v2.02
12
- // by Lokesh Dhakar - http://huddletogether.com/projects/lightbox2/
13
- // 2006/03/31
14
- //
15
- // Licensed under the Creative Commons Attribution 2.5 License - http://creativecommons.org/licenses/by/2.5/
16
- //
17
- // The code inserts html at the bottom of the page that looks similar to this:
18
- //
19
- // <div id="overlay"></div>
20
- // <div id="lightbox">
21
- // <div id="outerImageContainer">
22
- // <div id="imageContainer">
23
- // <img id="lightboxImage" />
24
- // <div id="hoverNav">
25
- // <a href="javascript:void(0);" id="prevLinkImg">&laquo; prev</a>
26
- // <a href="javascript:void(0);" id="nextLinkImg">next &raquo;</a>
27
- // </div>
28
- // <div id="loading">
29
- // <a href="javascript:void(0);" id="loadingLink">loading</a>
30
- // </div>
31
- // </div>
32
- // </div>
33
- // <div id="imageDataContainer">
34
- // <div id="imageData">
35
- // <div id="imageDetails">
36
- // <span id="caption"></span>
37
- // <span id="numberDisplay"></span>
38
- // <span id="detailsNav">
39
- // <a id="prevLinkDetails" href="javascript:void(0);">&laquo; prev</a>
40
- // <a id="nextLinkDetails" href="javascript:void(0);">next &raquo;</a>
41
- // <a id="slideShowControl" href="javascript:void(0);">stop slideshow</a>
42
- // </span>
43
- // </div>
44
- // <div id="close">
45
- // <a id="closeLink" href="javascript:void(0);">close</a>
46
- // </div>
47
- // </div>
48
- // </div>
49
- // </div>
50
- //
51
- // -----------------------------------------------------------------------------------
52
-
53
- //
54
- // Lightbox Object
55
- //
56
-
57
- var Lightbox = {
58
- activeImage : null,
59
- badObjects : ['select','object','embed'],
60
- container : null,
61
- enableSlideshow : null,
62
- groupName : null,
63
- imageArray : [],
64
- options : null,
65
- overlayDuration : null,
66
- overlayOpacity : null,
67
- playSlides : null,
68
- refTags : ['a','area'],
69
- relAttribute : null,
70
- resizeDuration : null,
71
- slideShowTimer : null,
72
- startImage : null,
73
-
74
- //
75
- // initialize()
76
- // Constructor sets class properties and configuration options and
77
- // inserts html at the bottom of the page which is used to display the shadow
78
- // overlay and the image container.
79
- //
80
- initialize: function(options) {
81
- if (!document.getElementsByTagName){ return; }
82
-
83
- this.options = $H({
84
- animate : true, // resizing animations
85
- autoPlay : true, // should slideshow start automatically
86
- borderSize : 10, // if you adjust the padding in the CSS, you will need to update this variable
87
- containerID : document, // lightbox container object
88
- enableSlideshow : true, // enable slideshow feature
89
- googleAnalytics : false, // track individual image views using Google Analytics
90
- imageDataLocation : 'south', // location of image caption information
91
- initImage : '', // ID of image link to automatically launch when upon script initialization
92
- loop : true, // whether to continuously loop slideshow images
93
- overlayDuration : .2, // time to fade in shadow overlay
94
- overlayOpacity : .8, // transparency of shadow overlay
95
- prefix : '', // ID prefix for all dynamically created html elements
96
- relAttribute : 'lightbox', // specifies the rel attribute value that triggers lightbox
97
- resizeSpeed : 7, // controls the speed of the image resizing (1=slowest and 10=fastest)
98
- showGroupName : false, // show group name of images in image details
99
- slideTime : 4, // time to display images during slideshow
100
- strings : { // allows for localization
101
- closeLink : 'close',
102
- loadingMsg : 'loading',
103
- nextLink : 'next &raquo;',
104
- prevLink : '&laquo; prev',
105
- startSlideshow : 'start slideshow',
106
- stopSlideshow : 'stop slideshow',
107
- numDisplayPrefix : 'Image',
108
- numDisplaySeparator : 'of'
109
- }
110
- }).merge(options);
111
-
112
- if(this.options.animate){
113
- this.overlayDuration = Math.max(this.options.overlayDuration,0);
114
- this.options.resizeSpeed = Math.max(Math.min(this.options.resizeSpeed,10),1);
115
- this.resizeDuration = (11 - this.options.resizeSpeed) * 0.15;
116
- }else{
117
- this.overlayDuration = 0;
118
- this.resizeDuration = 0;
119
- }
120
-
121
- this.enableSlideshow = this.options.enableSlideshow;
122
- this.overlayOpacity = Math.max(Math.min(this.options.overlayOpacity,1),0);
123
- this.playSlides = this.options.autoPlay;
124
- this.container = $(this.options.containerID);
125
- this.relAttribute = this.options.relAttribute;
126
- this.updateImageList();
127
-
128
- var objBody = this.container != document ? this.container : document.getElementsByTagName('body').item(0);
129
-
130
- var objOverlay = document.createElement('div');
131
- objOverlay.setAttribute('id',this.getID('overlay'));
132
- objOverlay.style.display = 'none';
133
- objBody.appendChild(objOverlay);
134
- Event.observe(objOverlay,'click',this.end.bindAsEventListener(this));
135
-
136
- var objLightbox = document.createElement('div');
137
- objLightbox.setAttribute('id',this.getID('lightbox'));
138
- objLightbox.style.display = 'none';
139
- objBody.appendChild(objLightbox);
140
-
141
- var objImageDataContainer = document.createElement('div');
142
- objImageDataContainer.setAttribute('id',this.getID('imageDataContainer'));
143
- objImageDataContainer.className = this.getID('clearfix');
144
-
145
- var objImageData = document.createElement('div');
146
- objImageData.setAttribute('id',this.getID('imageData'));
147
- objImageDataContainer.appendChild(objImageData);
148
-
149
- var objImageDetails = document.createElement('div');
150
- objImageDetails.setAttribute('id',this.getID('imageDetails'));
151
- objImageData.appendChild(objImageDetails);
152
-
153
- var objCaption = document.createElement('span');
154
- objCaption.setAttribute('id',this.getID('caption'));
155
- objImageDetails.appendChild(objCaption);
156
-
157
- var objNumberDisplay = document.createElement('span');
158
- objNumberDisplay.setAttribute('id',this.getID('numberDisplay'));
159
- objImageDetails.appendChild(objNumberDisplay);
160
-
161
- var objDetailsNav = document.createElement('span');
162
- objDetailsNav.setAttribute('id',this.getID('detailsNav'));
163
- objImageDetails.appendChild(objDetailsNav);
164
-
165
- var objPrevLink = document.createElement('a');
166
- objPrevLink.setAttribute('id',this.getID('prevLinkDetails'));
167
- objPrevLink.setAttribute('href','javascript:void(0);');
168
- objPrevLink.innerHTML = this.options.strings.prevLink;
169
- objDetailsNav.appendChild(objPrevLink);
170
- Event.observe(objPrevLink,'click',this.showPrev.bindAsEventListener(this));
171
-
172
- var objNextLink = document.createElement('a');
173
- objNextLink.setAttribute('id',this.getID('nextLinkDetails'));
174
- objNextLink.setAttribute('href','javascript:void(0);');
175
- objNextLink.innerHTML = this.options.strings.nextLink;
176
- objDetailsNav.appendChild(objNextLink);
177
- Event.observe(objNextLink,'click',this.showNext.bindAsEventListener(this));
178
-
179
- var objSlideShowControl = document.createElement('a');
180
- objSlideShowControl.setAttribute('id',this.getID('slideShowControl'));
181
- objSlideShowControl.setAttribute('href','javascript:void(0);');
182
- objDetailsNav.appendChild(objSlideShowControl);
183
- Event.observe(objSlideShowControl,'click',this.toggleSlideShow.bindAsEventListener(this));
184
-
185
- var objClose = document.createElement('div');
186
- objClose.setAttribute('id',this.getID('close'));
187
- objImageData.appendChild(objClose);
188
-
189
- var objCloseLink = document.createElement('a');
190
- objCloseLink.setAttribute('id',this.getID('closeLink'));
191
- objCloseLink.setAttribute('href','javascript:void(0);');
192
- objCloseLink.innerHTML = this.options.strings.closeLink;
193
- objClose.appendChild(objCloseLink);
194
- Event.observe(objCloseLink,'click',this.end.bindAsEventListener(this));
195
-
196
- if(this.options.imageDataLocation == 'north'){
197
- objLightbox.appendChild(objImageDataContainer);
198
- }
199
-
200
- var objOuterImageContainer = document.createElement('div');
201
- objOuterImageContainer.setAttribute('id',this.getID('outerImageContainer'));
202
- objLightbox.appendChild(objOuterImageContainer);
203
-
204
- var objImageContainer = document.createElement('div');
205
- objImageContainer.setAttribute('id',this.getID('imageContainer'));
206
- objOuterImageContainer.appendChild(objImageContainer);
207
-
208
- var objLightboxImage = document.createElement('img');
209
- objLightboxImage.setAttribute('id',this.getID('lightboxImage'));
210
- objImageContainer.appendChild(objLightboxImage);
211
-
212
- var objHoverNav = document.createElement('div');
213
- objHoverNav.setAttribute('id',this.getID('hoverNav'));
214
- objImageContainer.appendChild(objHoverNav);
215
-
216
- var objPrevLinkImg = document.createElement('a');
217
- objPrevLinkImg.setAttribute('id',this.getID('prevLinkImg'));
218
- objPrevLinkImg.setAttribute('href','javascript:void(0);');
219
- objHoverNav.appendChild(objPrevLinkImg);
220
- Event.observe(objPrevLinkImg,'click',this.showPrev.bindAsEventListener(this));
221
-
222
- var objNextLinkImg = document.createElement('a');
223
- objNextLinkImg.setAttribute('id',this.getID('nextLinkImg'));
224
- objNextLinkImg.setAttribute('href','javascript:void(0);');
225
- objHoverNav.appendChild(objNextLinkImg);
226
- Event.observe(objNextLinkImg,'click',this.showNext.bindAsEventListener(this));
227
-
228
- var objLoading = document.createElement('div');
229
- objLoading.setAttribute('id',this.getID('loading'));
230
- objImageContainer.appendChild(objLoading);
231
-
232
- var objLoadingLink = document.createElement('a');
233
- objLoadingLink.setAttribute('id',this.getID('loadingLink'));
234
- objLoadingLink.setAttribute('href','javascript:void(0);');
235
- objLoadingLink.innerHTML = this.options.strings.loadingMsg;
236
- objLoading.appendChild(objLoadingLink);
237
- Event.observe(objLoadingLink,'click',this.end.bindAsEventListener(this));
238
-
239
- if(this.options.imageDataLocation != 'north'){
240
- objLightbox.appendChild(objImageDataContainer);
241
- }
242
-
243
- if(this.options.initImage != ''){
244
- this.start($(this.options.initImage));
245
- }
246
- },
247
-
248
- //
249
- // updateImageList()
250
- // Loops through specific tags within 'container' looking for
251
- // 'lightbox' references and applies onclick events to them.
252
- //
253
- updateImageList: function(){
254
- var el, els, rel;
255
- for(var i=0; i < this.refTags.length; i++){
256
- els = this.container.getElementsByTagName(this.refTags[i]);
257
- for(var j=0; j < els.length; j++){
258
- el = els[j];
259
- rel = String(el.getAttribute('rel'));
260
- if (el.getAttribute('href') && (rel.toLowerCase().match(this.relAttribute))){
261
- el.onclick = function(){Lightbox.start(this); return false;}
262
- }
263
- }
264
- }
265
- },
266
-
267
- getCaption: function(imageLink) {
268
- var caption = imageLink.title || '';
269
- if ( caption == '' ) {
270
- var inner = $(imageLink).getElementsBySelector('img').first();
271
- if ( inner )
272
- caption = inner.getAttribute('title') || inner.getAttribute('alt');
273
- if ( !caption )
274
- caption = imageLink.innerHTML.stripTags() || imageLink.href || '';
275
- }
276
- return caption;
277
- },
278
-
279
- //
280
- // start()
281
- // Display overlay and lightbox. If image is part of a set, add siblings to imageArray.
282
- //
283
- start: function(imageLink) {
284
-
285
- this.hideBadObjects();
286
-
287
- // stretch overlay to fill page and fade in
288
- var pageSize = this.getPageSize();
289
- $(this.getID('overlay')).setStyle({height:pageSize.pageHeight+'px'});
290
- new Effect.Appear(this.getID('overlay'), { duration: this.overlayDuration, from: 0, to: this.overlayOpacity });
291
-
292
- this.imageArray = [];
293
- this.groupName = null;
294
-
295
- var rel = imageLink.getAttribute('rel');
296
- var imageTitle = '';
297
-
298
- // if image is NOT part of a group..
299
- if(rel == this.relAttribute){
300
- // add single image to imageArray
301
- imageTitle = this.getCaption(imageLink);
302
- this.imageArray.push({'link':imageLink.getAttribute('href'), 'title':imageTitle});
303
- this.startImage = 0;
304
- } else {
305
- // if image is part of a group..
306
- var els = this.container.getElementsByTagName(imageLink.tagName);
307
- // loop through anchors, find other images in group, and add them to imageArray
308
- for (var i=0; i<els.length; i++){
309
- var el = els[i];
310
- if (el.getAttribute('href') && (el.getAttribute('rel') == rel)){
311
- imageTitle = this.getCaption(el);
312
- this.imageArray.push({'link':el.getAttribute('href'),'title':imageTitle});
313
- if(el == imageLink){
314
- this.startImage = this.imageArray.length-1;
315
- }
316
- }
317
- }
318
- // get group name
319
- this.groupName = rel.substring(this.relAttribute.length+1,rel.length-1);
320
- }
321
-
322
- // calculate top offset for the lightbox and display
323
- var pageScroll = this.getPageScroll();
324
- var lightboxTop = pageScroll.y + (pageSize.winHeight / 15);
325
-
326
- $(this.getID('lightbox')).setStyle({top:lightboxTop+'px'}).show();
327
- this.changeImage(this.startImage);
328
- },
329
-
330
- //
331
- // changeImage()
332
- // Hide most elements and preload image in preparation for resizing image container.
333
- //
334
- changeImage: function(imageNum){
335
- this.activeImage = imageNum;
336
-
337
- this.disableKeyboardNav();
338
- this.pauseSlideShow();
339
-
340
- // hide elements during transition
341
- $(this.getID('loading')).show();
342
- $(this.getID('lightboxImage')).hide();
343
- $(this.getID('hoverNav')).hide();
344
- $(this.getID('imageDataContainer')).hide();
345
- $(this.getID('numberDisplay')).hide();
346
- $(this.getID('detailsNav')).hide();
347
-
348
- var imgPreloader = new Image();
349
-
350
- // once image is preloaded, resize image container
351
- imgPreloader.onload=function(){
352
- $(Lightbox.getID('lightboxImage')).src = imgPreloader.src;
353
- Lightbox.resizeImageContainer(imgPreloader.width,imgPreloader.height);
354
- }
355
- imgPreloader.src = this.imageArray[this.activeImage].link;
356
-
357
- if(this.options.googleAnalytics){
358
- urchinTracker(this.imageArray[this.activeImage].link);
359
- }
360
- },
361
-
362
- //
363
- // resizeImageContainer()
364
- //
365
- resizeImageContainer: function(imgWidth,imgHeight) {
366
- // get current height and width
367
- var cDims = $(this.getID('outerImageContainer')).getDimensions();
368
-
369
- // scalars based on change from old to new
370
- var xScale = ((imgWidth + (this.options.borderSize * 2)) / cDims.width) * 100;
371
- var yScale = ((imgHeight + (this.options.borderSize * 2)) / cDims.height) * 100;
372
-
373
- // calculate size difference between new and old image, and resize if necessary
374
- var wDiff = (cDims.width - this.options.borderSize * 2) - imgWidth;
375
- var hDiff = (cDims.height - this.options.borderSize * 2) - imgHeight;
376
-
377
- if(!( hDiff == 0)){ new Effect.Scale(this.getID('outerImageContainer'), yScale, {scaleX: false, duration: this.resizeDuration, queue: 'front'}); }
378
- if(!( wDiff == 0)){ new Effect.Scale(this.getID('outerImageContainer'), xScale, {scaleY: false, delay: this.resizeDuration, duration: this.resizeDuration}); }
379
-
380
- // if new and old image are same size and no scaling transition is necessary,
381
- // do a quick pause to prevent image flicker.
382
- if((hDiff == 0) && (wDiff == 0)){
383
- if(navigator.appVersion.indexOf('MSIE')!=-1){ this.pause(250); } else { this.pause(100);}
384
- }
385
-
386
- $(this.getID('prevLinkImg')).setStyle({height:imgHeight+'px'});
387
- $(this.getID('nextLinkImg')).setStyle({height:imgHeight+'px'});
388
- $(this.getID('imageDataContainer')).setStyle({width:(imgWidth+(this.options.borderSize * 2))+'px'});
389
-
390
- this.showImage();
391
- },
392
-
393
- //
394
- // showImage()
395
- // Display image and begin preloading neighbors.
396
- //
397
- showImage: function(){
398
- $(this.getID('loading')).hide();
399
- new Effect.Appear(this.getID('lightboxImage'), { duration: 0.5, queue: 'end', afterFinish: function(){ Lightbox.updateDetails(); } });
400
- this.preloadNeighborImages();
401
- },
402
-
403
- //
404
- // updateDetails()
405
- // Display caption, image number, and bottom nav.
406
- //
407
- updateDetails: function() {
408
- $(this.getID('caption')).show();
409
- $(this.getID('caption')).update(this.imageArray[this.activeImage].title);
410
-
411
- // if image is part of set display 'Image x of y'
412
- if(this.imageArray.length > 1){
413
- var num_display = this.options.strings.numDisplayPrefix + ' ' + eval(this.activeImage + 1) + ' ' + this.options.strings.numDisplaySeparator + ' ' + this.imageArray.length;
414
- if(this.options.showGroupName && this.groupName != ''){
415
- num_display += ' '+this.options.strings.numDisplaySeparator+' '+this.groupName;
416
- }
417
- $(this.getID('numberDisplay')).update(num_display).show();
418
- if(!this.enableSlideshow){
419
- $(this.getID('slideShowControl')).hide();
420
- }
421
- $(this.getID('detailsNav')).show();
422
- }
423
-
424
- new Effect.Parallel(
425
- [ new Effect.SlideDown( this.getID('imageDataContainer'), { sync: true }),
426
- new Effect.Appear(this.getID('imageDataContainer'), { sync: true }) ],
427
- { duration:.65, afterFinish: function() { Lightbox.updateNav();} }
428
- );
429
- },
430
-
431
- //
432
- // updateNav()
433
- // Display appropriate previous and next hover navigation.
434
- //
435
- updateNav: function() {
436
- if(this.imageArray.length > 1){
437
- $(this.getID('hoverNav')).show();
438
- if(this.enableSlideshow){
439
- if(this.playSlides){
440
- this.startSlideShow();
441
- } else {
442
- this.stopSlideShow();
443
- }
444
- }
445
- }
446
- this.enableKeyboardNav();
447
- },
448
- //
449
- // startSlideShow()
450
- // Starts the slide show
451
- //
452
- startSlideShow: function(){
453
- this.playSlides = true;
454
- this.slideShowTimer = new PeriodicalExecuter(function(pe){ Lightbox.showNext(); pe.stop(); },this.options.slideTime);
455
- $(this.getID('slideShowControl')).update(this.options.strings.stopSlideshow);
456
- },
457
-
458
- //
459
- // stopSlideShow()
460
- // Stops the slide show
461
- //
462
- stopSlideShow: function(){
463
- this.playSlides = false;
464
- if(this.slideShowTimer){
465
- this.slideShowTimer.stop();
466
- }
467
- $(this.getID('slideShowControl')).update(this.options.strings.startSlideshow);
468
- },
469
-
470
- //
471
- // stopSlideShow()
472
- // Stops the slide show
473
- //
474
- toggleSlideShow: function(){
475
- if(this.playSlides){
476
- this.stopSlideShow();
477
- }else{
478
- this.startSlideShow();
479
- }
480
- },
481
-
482
- //
483
- // pauseSlideShow()
484
- // Pauses the slide show (doesn't change the value of this.playSlides)
485
- //
486
- pauseSlideShow: function(){
487
- if(this.slideShowTimer){
488
- this.slideShowTimer.stop();
489
- }
490
- },
491
-
492
- //
493
- // showNext()
494
- // Display the next image in a group
495
- //
496
- showNext : function(){
497
- if(this.imageArray.length > 1){
498
- if(!this.options.loop && ((this.activeImage == this.imageArray.length - 1 && this.startImage == 0) || (this.activeImage+1 == this.startImage))){
499
- return this.end();
500
- }
501
- if(this.activeImage == this.imageArray.length - 1){
502
- this.changeImage(0);
503
- }else{
504
- this.changeImage(this.activeImage+1);
505
- }
506
- }
507
- },
508
-
509
- //
510
- // showPrev()
511
- // Display the next image in a group
512
- //
513
- showPrev : function(){
514
- if(this.imageArray.length > 1){
515
- if(this.activeImage == 0){
516
- this.changeImage(this.imageArray.length - 1);
517
- }else{
518
- this.changeImage(this.activeImage-1);
519
- }
520
- }
521
- },
522
-
523
- //
524
- // showFirst()
525
- // Display the first image in a group
526
- //
527
- showFirst : function(){
528
- if(this.imageArray.length > 1){
529
- this.changeImage(0);
530
- }
531
- },
532
-
533
- //
534
- // showFirst()
535
- // Display the first image in a group
536
- //
537
- showLast : function(){
538
- if(this.imageArray.length > 1){
539
- this.changeImage(this.imageArray.length - 1);
540
- }
541
- },
542
-
543
- //
544
- // enableKeyboardNav()
545
- //
546
- enableKeyboardNav: function() {
547
- document.onkeydown = this.keyboardAction;
548
- },
549
-
550
- //
551
- // disableKeyboardNav()
552
- //
553
- disableKeyboardNav: function() {
554
- document.onkeydown = '';
555
- },
556
-
557
- //
558
- // keyboardAction()
559
- //
560
- keyboardAction: function(e) {
561
- if (e == null) { // ie
562
- keycode = event.keyCode;
563
- } else { // mozilla
564
- keycode = e.which;
565
- }
566
-
567
- key = String.fromCharCode(keycode).toLowerCase();
568
-
569
- if(key == 'x' || key == 'o' || key == 'c'){ // close lightbox
570
- Lightbox.end();
571
- } else if(key == 'p' || key == '%'){ // display previous image
572
- Lightbox.showPrev();
573
- } else if(key == 'n' || key =='\''){ // display next image
574
- Lightbox.showNext();
575
- } else if(key == 'f'){ // display first image
576
- Lightbox.showFirst();
577
- } else if(key == 'l'){ // display last image
578
- Lightbox.showLast();
579
- } else if(key == 's'){ // toggle slideshow
580
- if(Lightbox.imageArray.length > 0 && Lightbox.options.enableSlideshow){
581
- Lightbox.toggleSlideShow();
582
- }
583
- }
584
- },
585
-
586
- //
587
- // preloadNeighborImages()
588
- // Preload previous and next images.
589
- //
590
- preloadNeighborImages: function(){
591
- var nextImageID = this.imageArray.length - 1 == this.activeImage ? 0 : this.activeImage + 1;
592
- nextImage = new Image();
593
- nextImage.src = this.imageArray[nextImageID].link
594
-
595
- var prevImageID = this.activeImage == 0 ? this.imageArray.length - 1 : this.activeImage - 1;
596
- prevImage = new Image();
597
- prevImage.src = this.imageArray[prevImageID].link;
598
- },
599
-
600
- //
601
- // end()
602
- //
603
- end: function() {
604
- this.disableKeyboardNav();
605
- this.pauseSlideShow();
606
- $(this.getID('lightbox')).hide();
607
- new Effect.Fade(this.getID('overlay'), { duration:this.overlayDuration });
608
- this.showBadObjects();
609
- },
610
-
611
- //
612
- // showBadObjects()
613
- //
614
- showBadObjects: function (){
615
- var els;
616
- var tags = Lightbox.badObjects;
617
- for(var i=0; i<tags.length; i++){
618
- els = document.getElementsByTagName(tags[i]);
619
- for(var j=0; j<els.length; j++){
620
- $(els[j]).setStyle({visibility:'visible'});
621
- }
622
- }
623
- },
624
-
625
- //
626
- // hideBadObjects()
627
- //
628
- hideBadObjects: function (){
629
- var els;
630
- var tags = Lightbox.badObjects;
631
- for(var i=0; i<tags.length; i++){
632
- els = document.getElementsByTagName(tags[i]);
633
- for(var j=0; j<els.length; j++){
634
- $(els[j]).setStyle({visibility:'hidden'});
635
- }
636
- }
637
- },
638
-
639
- //
640
- // pause(numberMillis)
641
- // Pauses code execution for specified time. Uses busy code, not good.
642
- // Code from http://www.faqts.com/knowledge_base/view.phtml/aid/1602
643
- //
644
- pause: function(numberMillis) {
645
- var now = new Date();
646
- var exitTime = now.getTime() + numberMillis;
647
- while(true){
648
- now = new Date();
649
- if (now.getTime() > exitTime)
650
- return;
651
- }
652
- },
653
-
654
- //
655
- // getPageScroll()
656
- // Returns array with x,y page scroll values.
657
- // Core code from - quirksmode.org
658
- //
659
- getPageScroll: function(){
660
- var x,y;
661
- if (self.pageYOffset) {
662
- x = self.pageXOffset;
663
- y = self.pageYOffset;
664
- } else if (document.documentElement && document.documentElement.scrollTop){ // Explorer 6 Strict
665
- x = document.documentElement.scrollLeft;
666
- y = document.documentElement.scrollTop;
667
- } else if (document.body) {// all other Explorers
668
- x = document.body.scrollLeft;
669
- y = document.body.scrollTop;
670
- }
671
- return {x:x,y:y};
672
- },
673
-
674
- //
675
- // getPageSize()
676
- // Returns array with page width, height and window width, height
677
- // Core code from - quirksmode.org
678
- // Edit for Firefox by pHaez
679
- //
680
- getPageSize: function(){
681
- var scrollX,scrollY,windowX,windowY,pageX,pageY;
682
- if (window.innerHeight && window.scrollMaxY) {
683
- scrollX = document.body.scrollWidth;
684
- scrollY = window.innerHeight + window.scrollMaxY;
685
- } else if (document.body.scrollHeight > document.body.offsetHeight){ // all but Explorer Mac
686
- scrollX = document.body.scrollWidth;
687
- scrollY = document.body.scrollHeight;
688
- } else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
689
- scrollX = document.body.offsetWidth;
690
- scrollY = document.body.offsetHeight;
691
- }
692
-
693
- if (self.innerHeight) { // all except Explorer
694
- windowX = self.innerWidth;
695
- windowY = self.innerHeight;
696
- } else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
697
- windowX = document.documentElement.clientWidth;
698
- windowY = document.documentElement.clientHeight;
699
- } else if (document.body) { // other Explorers
700
- windowX = document.body.clientWidth;
701
- windowY = document.body.clientHeight;
702
- }
703
-
704
- pageY = (scrollY < windowY) ? windowY : scrollY; // for small pages with total height less then height of the viewport
705
- pageX = (scrollX < windowX) ? windowX : scrollX; // for small pages with total width less then width of the viewport
706
-
707
- return {pageWidth:pageX,pageHeight:pageY,winWidth:windowX,winHeight:windowY};
708
- },
709
-
710
- //
711
- // getID()
712
- // Returns formatted Lightbox element ID
713
- //
714
- getID: function(id){
715
- return this.options.prefix+id;
716
- }
717
- }
718
-
719
- // -----------------------------------------------------------------------------------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
js/dev/prototype.js DELETED
@@ -1,3271 +0,0 @@
1
- /* Prototype JavaScript framework, version 1.5.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',
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[^>]*>([\u0001-\uFFFF]*?)</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
- evalJSON: function(sanitize) {
368
- var json = this.unfilterJSON();
369
- try {
370
- if (!sanitize || (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(json)))
371
- return eval('(' + json + ')');
372
- } catch (e) { }
373
- throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
374
- },
375
-
376
- include: function(pattern) {
377
- return this.indexOf(pattern) > -1;
378
- },
379
-
380
- startsWith: function(pattern) {
381
- return this.indexOf(pattern) === 0;
382
- },
383
-
384
- endsWith: function(pattern) {
385
- var d = this.length - pattern.length;
386
- return d >= 0 && this.lastIndexOf(pattern) === d;
387
- },
388
-
389
- empty: function() {
390
- return this == '';
391
- },
392
-
393
- blank: function() {
394
- return /^\s*$/.test(this);
395
- }
396
- });
397
-
398
- if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
399
- escapeHTML: function() {
400
- return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
401
- },
402
- unescapeHTML: function() {
403
- return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
404
- }
405
- });
406
-
407
- String.prototype.gsub.prepareReplacement = function(replacement) {
408
- if (typeof replacement == 'function') return replacement;
409
- var template = new Template(replacement);
410
- return function(match) { return template.evaluate(match) };
411
- }
412
-
413
- String.prototype.parseQuery = String.prototype.toQueryParams;
414
-
415
- Object.extend(String.prototype.escapeHTML, {
416
- div: document.createElement('div'),
417
- text: document.createTextNode('')
418
- });
419
-
420
- with (String.prototype.escapeHTML) div.appendChild(text);
421
-
422
- var Template = Class.create();
423
- Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
424
- Template.prototype = {
425
- initialize: function(template, pattern) {
426
- this.template = template.toString();
427
- this.pattern = pattern || Template.Pattern;
428
- },
429
-
430
- evaluate: function(object) {
431
- return this.template.gsub(this.pattern, function(match) {
432
- var before = match[1];
433
- if (before == '\\') return match[2];
434
- return before + String.interpret(object[match[3]]);
435
- });
436
- }
437
- }
438
-
439
- var $break = {}, $continue = new Error('"throw $continue" is deprecated, use "return" instead');
440
-
441
- var Enumerable = {
442
- each: function(iterator) {
443
- var index = 0;
444
- try {
445
- this._each(function(value) {
446
- iterator(value, index++);
447
- });
448
- } catch (e) {
449
- if (e != $break) throw e;
450
- }
451
- return this;
452
- },
453
-
454
- eachSlice: function(number, iterator) {
455
- var index = -number, slices = [], array = this.toArray();
456
- while ((index += number) < array.length)
457
- slices.push(array.slice(index, index+number));
458
- return slices.map(iterator);
459
- },
460
-
461
- all: function(iterator) {
462
- var result = true;
463
- this.each(function(value, index) {
464
- result = result && !!(iterator || Prototype.K)(value, index);
465
- if (!result) throw $break;
466
- });
467
- return result;
468
- },
469
-
470
- any: function(iterator) {
471
- var result = false;
472
- this.each(function(value, index) {
473
- if (result = !!(iterator || Prototype.K)(value, index))
474
- throw $break;
475
- });
476
- return result;
477
- },
478
-
479
- collect: function(iterator) {
480
- var results = [];
481
- this.each(function(value, index) {
482
- results.push((iterator || Prototype.K)(value, index));
483
- });
484
- return results;
485
- },
486
-
487
- detect: function(iterator) {
488
- var result;
489
- this.each(function(value, index) {
490
- if (iterator(value, index)) {
491
- result = value;
492
- throw $break;
493
- }
494
- });
495
- return result;
496
- },
497
-
498
- findAll: function(iterator) {
499
- var results = [];
500
- this.each(function(value, index) {
501
- if (iterator(value, index))
502
- results.push(value);
503
- });
504
- return results;
505
- },
506
-
507
- grep: function(pattern, iterator) {
508
- var results = [];
509
- this.each(function(value, index) {
510
- var stringValue = value.toString();
511
- if (stringValue.match(pattern))
512
- results.push((iterator || Prototype.K)(value, index));
513
- })
514
- return results;
515
- },
516
-
517
- include: function(object) {
518
- var found = false;
519
- this.each(function(value) {
520
- if (value == object) {
521
- found = true;
522
- throw $break;
523
- }
524
- });
525
- return found;
526
- },
527
-
528
- inGroupsOf: function(number, fillWith) {
529
- fillWith = fillWith === undefined ? null : fillWith;
530
- return this.eachSlice(number, function(slice) {
531
- while(slice.length < number) slice.push(fillWith);
532
- return slice;
533
- });
534
- },
535
-
536
- inject: function(memo, iterator) {
537
- this.each(function(value, index) {
538
- memo = iterator(memo, value, index);
539
- });
540
- return memo;
541
- },
542
-
543
- invoke: function(method) {
544
- var args = $A(arguments).slice(1);
545
- return this.map(function(value) {
546
- return value[method].apply(value, args);
547
- });
548
- },
549
-
550
- max: function(iterator) {
551
- var result;
552
- this.each(function(value, index) {
553
- value = (iterator || Prototype.K)(value, index);
554
- if (result == undefined || value >= result)
555
- result = value;
556
- });
557
- return result;
558
- },
559
-
560
- min: function(iterator) {
561
- var result;
562
- this.each(function(value, index) {
563
- value = (iterator || Prototype.K)(value, index);
564
- if (result == undefined || value < result)
565
- result = value;
566
- });
567
- return result;
568
- },
569
-
570
- partition: function(iterator) {
571
- var trues = [], falses = [];
572
- this.each(function(value, index) {
573
- ((iterator || Prototype.K)(value, index) ?
574
- trues : falses).push(value);
575
- });
576
- return [trues, falses];
577
- },
578
-
579
- pluck: function(property) {
580
- var results = [];
581
- this.each(function(value, index) {
582
- results.push(value[property]);
583
- });
584
- return results;
585
- },
586
-
587
- reject: function(iterator) {
588
- var results = [];
589
- this.each(function(value, index) {
590
- if (!iterator(value, index))
591
- results.push(value);
592
- });
593
- return results;
594
- },
595
-
596
- sortBy: function(iterator) {
597
- return this.map(function(value, index) {
598
- return {value: value, criteria: iterator(value, index)};
599
- }).sort(function(left, right) {
600
- var a = left.criteria, b = right.criteria;
601
- return a < b ? -1 : a > b ? 1 : 0;
602
- }).pluck('value');
603
- },
604
-
605
- toArray: function() {
606
- return this.map();
607
- },
608
-
609
- zip: function() {
610
- var iterator = Prototype.K, args = $A(arguments);
611
- if (typeof args.last() == 'function')
612
- iterator = args.pop();
613
-
614
- var collections = [this].concat(args).map($A);
615
- return this.map(function(value, index) {
616
- return iterator(collections.pluck(index));
617
- });
618
- },
619
-
620
- size: function() {
621
- return this.toArray().length;
622
- },
623
-
624
- inspect: function() {
625
- return '#<Enumerable:' + this.toArray().inspect() + '>';
626
- }
627
- }
628
-
629
- Object.extend(Enumerable, {
630
- map: Enumerable.collect,
631
- find: Enumerable.detect,
632
- select: Enumerable.findAll,
633
- member: Enumerable.include,
634
- entries: Enumerable.toArray
635
- });
636
- var $A = Array.from = function(iterable) {
637
- if (!iterable) return [];
638
- if (iterable.toArray) {
639
- return iterable.toArray();
640
- } else {
641
- var results = [];
642
- for (var i = 0, length = iterable.length; i < length; i++)
643
- results.push(iterable[i]);
644
- return results;
645
- }
646
- }
647
-
648
- if (Prototype.Browser.WebKit) {
649
- $A = Array.from = function(iterable) {
650
- if (!iterable) return [];
651
- if (!(typeof iterable == 'function' && iterable == '[object NodeList]') &&
652
- iterable.toArray) {
653
- return iterable.toArray();
654
- } else {
655
- var results = [];
656
- for (var i = 0, length = iterable.length; i < length; i++)
657
- results.push(iterable[i]);
658
- return results;
659
- }
660
- }
661
- }
662
-
663
- Object.extend(Array.prototype, Enumerable);
664
-
665
- if (!Array.prototype._reverse)
666
- Array.prototype._reverse = Array.prototype.reverse;
667
-
668
- Object.extend(Array.prototype, {
669
- _each: function(iterator) {
670
- for (var i = 0, length = this.length; i < length; i++)
671
- iterator(this[i]);
672
- },
673
-
674
- clear: function() {
675
- this.length = 0;
676
- return this;
677
- },
678
-
679
- first: function() {
680
- return this[0];
681
- },
682
-
683
- last: function() {
684
- return this[this.length - 1];
685
- },
686
-
687
- compact: function() {
688
- return this.select(function(value) {
689
- return value != null;
690
- });
691
- },
692
-
693
- flatten: function() {
694
- return this.inject([], function(array, value) {
695
- return array.concat(value && value.constructor == Array ?
696
- value.flatten() : [value]);
697
- });
698
- },
699
-
700
- without: function() {
701
- var values = $A(arguments);
702
- return this.select(function(value) {
703
- return !values.include(value);
704
- });
705
- },
706
-
707
- indexOf: function(object) {
708
- for (var i = 0, length = this.length; i < length; i++)
709
- if (this[i] == object) return i;
710
- return -1;
711
- },
712
-
713
- reverse: function(inline) {
714
- return (inline !== false ? this : this.toArray())._reverse();
715
- },
716
-
717
- reduce: function() {
718
- return this.length > 1 ? this : this[0];
719
- },
720
-
721
- uniq: function(sorted) {
722
- return this.inject([], function(array, value, index) {
723
- if (0 == index || (sorted ? array.last() != value : !array.include(value)))
724
- array.push(value);
725
- return array;
726
- });
727
- },
728
-
729
- clone: function() {
730
- return [].concat(this);
731
- },
732
-
733
- size: function() {
734
- return this.length;
735
- },
736
-
737
- inspect: function() {
738
- return '[' + this.map(Object.inspect).join(', ') + ']';
739
- },
740
-
741
- toJSON: function() {
742
- var results = [];
743
- this.each(function(object) {
744
- var value = Object.toJSON(object);
745
- if (value !== undefined) results.push(value);
746
- });
747
- return '[' + results.join(', ') + ']';
748
- }
749
- });
750
-
751
- Array.prototype.toArray = Array.prototype.clone;
752
-
753
- function $w(string) {
754
- string = string.strip();
755
- return string ? string.split(/\s+/) : [];
756
- }
757
-
758
- if (Prototype.Browser.Opera){
759
- Array.prototype.concat = function() {
760
- var array = [];
761
- for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
762
- for (var i = 0, length = arguments.length; i < length; i++) {
763
- if (arguments[i].constructor == Array) {
764
- for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
765
- array.push(arguments[i][j]);
766
- } else {
767
- array.push(arguments[i]);
768
- }
769
- }
770
- return array;
771
- }
772
- }
773
- var Hash = function(object) {
774
- if (object instanceof Hash) this.merge(object);
775
- else Object.extend(this, object || {});
776
- };
777
-
778
- Object.extend(Hash, {
779
- toQueryString: function(obj) {
780
- var parts = [];
781
- parts.add = arguments.callee.addPair;
782
-
783
- this.prototype._each.call(obj, function(pair) {
784
- if (!pair.key) return;
785
- var value = pair.value;
786
-
787
- if (value && typeof value == 'object') {
788
- if (value.constructor == Array) value.each(function(value) {
789
- parts.add(pair.key, value);
790
- });
791
- return;
792
- }
793
- parts.add(pair.key, value);
794
- });
795
-
796
- return parts.join('&');
797
- },
798
-
799
- toJSON: function(object) {
800
- var results = [];
801
- this.prototype._each.call(object, function(pair) {
802
- var value = Object.toJSON(pair.value);
803
- if (value !== undefined) results.push(pair.key.toJSON() + ': ' + value);
804
- });
805
- return '{' + results.join(', ') + '}';
806
- }
807
- });
808
-
809
- Hash.toQueryString.addPair = function(key, value, prefix) {
810
- key = encodeURIComponent(key);
811
- if (value === undefined) this.push(key);
812
- else this.push(key + '=' + (value == null ? '' : encodeURIComponent(value)));
813
- }
814
-
815
- Object.extend(Hash.prototype, Enumerable);
816
- Object.extend(Hash.prototype, {
817
- _each: function(iterator) {
818
- for (var key in this) {
819
- var value = this[key];
820
- if (value && value == Hash.prototype[key]) continue;
821
-
822
- var pair = [key, value];
823
- pair.key = key;
824
- pair.value = value;
825
- iterator(pair);
826
- }
827
- },
828
-
829
- keys: function() {
830
- return this.pluck('key');
831
- },
832
-
833
- values: function() {
834
- return this.pluck('value');
835
- },
836
-
837
- merge: function(hash) {
838
- return $H(hash).inject(this, function(mergedHash, pair) {
839
- mergedHash[pair.key] = pair.value;
840
- return mergedHash;
841
- });
842
- },
843
-
844
- remove: function() {
845
- var result;
846
- for(var i = 0, length = arguments.length; i < length; i++) {
847
- var value = this[arguments[i]];
848
- if (value !== undefined){
849
- if (result === undefined) result = value;
850
- else {
851
- if (result.constructor != Array) result = [result];
852
- result.push(value)
853
- }
854
- }
855
- delete this[arguments[i]];
856
- }
857
- return result;
858
- },
859
-
860
- toQueryString: function() {
861
- return Hash.toQueryString(this);
862
- },
863
-
864
- inspect: function() {
865
- return '#<Hash:{' + this.map(function(pair) {
866
- return pair.map(Object.inspect).join(': ');
867
- }).join(', ') + '}>';
868
- },
869
-
870
- toJSON: function() {
871
- return Hash.toJSON(this);
872
- }
873
- });
874
-
875
- function $H(object) {
876
- if (object instanceof Hash) return object;
877
- return new Hash(object);
878
- };
879
-
880
- // Safari iterates over shadowed properties
881
- if (function() {
882
- var i = 0, Test = function(value) { this.key = value };
883
- Test.prototype.key = 'foo';
884
- for (var property in new Test('bar')) i++;
885
- return i > 1;
886
- }()) Hash.prototype._each = function(iterator) {
887
- var cache = [];
888
- for (var key in this) {
889
- var value = this[key];
890
- if ((value && value == Hash.prototype[key]) || cache.include(key)) continue;
891
- cache.push(key);
892
- var pair = [key, value];
893
- pair.key = key;
894
- pair.value = value;
895
- iterator(pair);
896
- }
897
- };
898
- ObjectRange = Class.create();
899
- Object.extend(ObjectRange.prototype, Enumerable);
900
- Object.extend(ObjectRange.prototype, {
901
- initialize: function(start, end, exclusive) {
902
- this.start = start;
903
- this.end = end;
904
- this.exclusive = exclusive;
905
- },
906
-
907
- _each: function(iterator) {
908
- var value = this.start;
909
- while (this.include(value)) {
910
- iterator(value);
911
- value = value.succ();
912
- }
913
- },
914
-
915
- include: function(value) {
916
- if (value < this.start)
917
- return false;
918
- if (this.exclusive)
919
- return value < this.end;
920
- return value <= this.end;
921
- }
922
- });
923
-
924
- var $R = function(start, end, exclusive) {
925
- return new ObjectRange(start, end, exclusive);
926
- }
927
-
928
- var Ajax = {
929
- getTransport: function() {
930
- return Try.these(
931
- function() {return new XMLHttpRequest()},
932
- function() {return new ActiveXObject('Msxml2.XMLHTTP')},
933
- function() {return new ActiveXObject('Microsoft.XMLHTTP')}
934
- ) || false;
935
- },
936
-
937
- activeRequestCount: 0
938
- }
939
-
940
- Ajax.Responders = {
941
- responders: [],
942
-
943
- _each: function(iterator) {
944
- this.responders._each(iterator);
945
- },
946
-
947
- register: function(responder) {
948
- if (!this.include(responder))
949
- this.responders.push(responder);
950
- },
951
-
952
- unregister: function(responder) {
953
- this.responders = this.responders.without(responder);
954
- },
955
-
956
- dispatch: function(callback, request, transport, json) {
957
- this.each(function(responder) {
958
- if (typeof responder[callback] == 'function') {
959
- try {
960
- responder[callback].apply(responder, [request, transport, json]);
961
- } catch (e) {}
962
- }
963
- });
964
- }
965
- };
966
-
967
- Object.extend(Ajax.Responders, Enumerable);
968
-
969
- Ajax.Responders.register({
970
- onCreate: function() {
971
- Ajax.activeRequestCount++;
972
- },
973
- onComplete: function() {
974
- Ajax.activeRequestCount--;
975
- }
976
- });
977
-
978
- Ajax.Base = function() {};
979
- Ajax.Base.prototype = {
980
- setOptions: function(options) {
981
- this.options = {
982
- method: 'post',
983
- asynchronous: true,
984
- contentType: 'application/x-www-form-urlencoded',
985
- encoding: 'UTF-8',
986
- parameters: ''
987
- }
988
- Object.extend(this.options, options || {});
989
-
990
- this.options.method = this.options.method.toLowerCase();
991
- if (typeof this.options.parameters == 'string')
992
- this.options.parameters = this.options.parameters.toQueryParams();
993
- }
994
- }
995
-
996
- Ajax.Request = Class.create();
997
- Ajax.Request.Events =
998
- ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
999
-
1000
- Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
1001
- _complete: false,
1002
-
1003
- initialize: function(url, options) {
1004
- this.transport = Ajax.getTransport();
1005
- this.setOptions(options);
1006
- this.request(url);
1007
- },
1008
-
1009
- request: function(url) {
1010
- this.url = url;
1011
- this.method = this.options.method;
1012
- var params = Object.clone(this.options.parameters);
1013
-
1014
- if (!['get', 'post'].include(this.method)) {
1015
- // simulate other verbs over post
1016
- params['_method'] = this.method;
1017
- this.method = 'post';
1018
- }
1019
-
1020
- this.parameters = params;
1021
-
1022
- if (params = Hash.toQueryString(params)) {
1023
- // when GET, append parameters to URL
1024
- if (this.method == 'get')
1025
- this.url += (this.url.include('?') ? '&' : '?') + params;
1026
- else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
1027
- params += '&_=';
1028
- }
1029
-
1030
- try {
1031
- if (this.options.onCreate) this.options.onCreate(this.transport);
1032
- Ajax.Responders.dispatch('onCreate', this, this.transport);
1033
-
1034
- this.transport.open(this.method.toUpperCase(), this.url,
1035
- this.options.asynchronous);
1036
-
1037
- if (this.options.asynchronous)
1038
- setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
1039
-
1040
- this.transport.onreadystatechange = this.onStateChange.bind(this);
1041
- this.setRequestHeaders();
1042
-
1043
- this.body = this.method == 'post' ? (this.options.postBody || params) : null;
1044
- this.transport.send(this.body);
1045
-
1046
- /* Force Firefox to handle ready state 4 for synchronous requests */
1047
- if (!this.options.asynchronous && this.transport.overrideMimeType)
1048
- this.onStateChange();
1049
-
1050
- }
1051
- catch (e) {
1052
- this.dispatchException(e);
1053
- }
1054
- },
1055
-
1056
- onStateChange: function() {
1057
- var readyState = this.transport.readyState;
1058
- if (readyState > 1 && !((readyState == 4) && this._complete))
1059
- this.respondToReadyState(this.transport.readyState);
1060
- },
1061
-
1062
- setRequestHeaders: function() {
1063
- var headers = {
1064
- 'X-Requested-With': 'XMLHttpRequest',
1065
- 'X-Prototype-Version': Prototype.Version,
1066
- 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
1067
- };
1068
-
1069
- if (this.method == 'post') {
1070
- headers['Content-type'] = this.options.contentType +
1071
- (this.options.encoding ? '; charset=' + this.options.encoding : '');
1072
-
1073
- /* Force "Connection: close" for older Mozilla browsers to work
1074
- * around a bug where XMLHttpRequest sends an incorrect
1075
- * Content-length header. See Mozilla Bugzilla #246651.
1076
- */
1077
- if (this.transport.overrideMimeType &&
1078
- (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
1079
- headers['Connection'] = 'close';
1080
- }
1081
-
1082
- // user-defined headers
1083
- if (typeof this.options.requestHeaders == 'object') {
1084
- var extras = this.options.requestHeaders;
1085
-
1086
- if (typeof extras.push == 'function')
1087
- for (var i = 0, length = extras.length; i < length; i += 2)
1088
- headers[extras[i]] = extras[i+1];
1089
- else
1090
- $H(extras).each(function(pair) { headers[pair.key] = pair.value });
1091
- }
1092
-
1093
- for (var name in headers)
1094
- this.transport.setRequestHeader(name, headers[name]);
1095
- },
1096
-
1097
- success: function() {
1098
- return !this.transport.status
1099
- || (this.transport.status >= 200 && this.transport.status < 300);
1100
- },
1101
-
1102
- respondToReadyState: function(readyState) {
1103
- var state = Ajax.Request.Events[readyState];
1104
- var transport = this.transport, json = this.evalJSON();
1105
-
1106
- if (state == 'Complete') {
1107
- try {
1108
- this._complete = true;
1109
- (this.options['on' + this.transport.status]
1110
- || this.options['on' + (this.success() ? 'Success' : 'Failure')]
1111
- || Prototype.emptyFunction)(transport, json);
1112
- } catch (e) {
1113
- this.dispatchException(e);
1114
- }
1115
-
1116
- var contentType = this.getHeader('Content-type');
1117
- if (contentType && contentType.strip().
1118
- match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
1119
- this.evalResponse();
1120
- }
1121
-
1122
- try {
1123
- (this.options['on' + state] || Prototype.emptyFunction)(transport, json);
1124
- Ajax.Responders.dispatch('on' + state, this, transport, json);
1125
- } catch (e) {
1126
- this.dispatchException(e);
1127
- }
1128
-
1129
- if (state == 'Complete') {
1130
- // avoid memory leak in MSIE: clean up
1131
- this.transport.onreadystatechange = Prototype.emptyFunction;
1132
- }
1133
- },
1134
-
1135
- getHeader: function(name) {
1136
- try {
1137
- return this.transport.getResponseHeader(name);
1138
- } catch (e) { return null }
1139
- },
1140
-
1141
- evalJSON: function() {
1142
- try {
1143
- var json = this.getHeader('X-JSON');
1144
- return json ? json.evalJSON() : null;
1145
- } catch (e) { return null }
1146
- },
1147
-
1148
- evalResponse: function() {
1149
- try {
1150
- return eval((this.transport.responseText || '').unfilterJSON());
1151
- } catch (e) {
1152
- this.dispatchException(e);
1153
- }
1154
- },
1155
-
1156
- dispatchException: function(exception) {
1157
- (this.options.onException || Prototype.emptyFunction)(this, exception);
1158
- Ajax.Responders.dispatch('onException', this, exception);
1159
- }
1160
- });
1161
-
1162
- Ajax.Updater = Class.create();
1163
-
1164
- Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
1165
- initialize: function(container, url, options) {
1166
- this.container = {
1167
- success: (container.success || container),
1168
- failure: (container.failure || (container.success ? null : container))
1169
- }
1170
-
1171
- this.transport = Ajax.getTransport();
1172
- this.setOptions(options);
1173
-
1174
- var onComplete = this.options.onComplete || Prototype.emptyFunction;
1175
- this.options.onComplete = (function(transport, param) {
1176
- this.updateContent();
1177
- onComplete(transport, param);
1178
- }).bind(this);
1179
-
1180
- this.request(url);
1181
- },
1182
-
1183
- updateContent: function() {
1184
- var receiver = this.container[this.success() ? 'success' : 'failure'];
1185
- var response = this.transport.responseText;
1186
-
1187
- if (!this.options.evalScripts) response = response.stripScripts();
1188
-
1189
- if (receiver = $(receiver)) {
1190
- if (this.options.insertion)
1191
- new this.options.insertion(receiver, response);
1192
- else
1193
- receiver.update(response);
1194
- }
1195
-
1196
- if (this.success()) {
1197
- if (this.onComplete)
1198
- setTimeout(this.onComplete.bind(this), 10);
1199
- }
1200
- }
1201
- });
1202
-
1203
- Ajax.PeriodicalUpdater = Class.create();
1204
- Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
1205
- initialize: function(container, url, options) {
1206
- this.setOptions(options);
1207
- this.onComplete = this.options.onComplete;
1208
-
1209
- this.frequency = (this.options.frequency || 2);
1210
- this.decay = (this.options.decay || 1);
1211
-
1212
- this.updater = {};
1213
- this.container = container;
1214
- this.url = url;
1215
-
1216
- this.start();
1217
- },
1218
-
1219
- start: function() {
1220
- this.options.onComplete = this.updateComplete.bind(this);
1221
- this.onTimerEvent();
1222
- },
1223
-
1224
- stop: function() {
1225
- this.updater.options.onComplete = undefined;
1226
- clearTimeout(this.timer);
1227
- (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
1228
- },
1229
-
1230
- updateComplete: function(request) {
1231
- if (this.options.decay) {
1232
- this.decay = (request.responseText == this.lastText ?
1233
- this.decay * this.options.decay : 1);
1234
-
1235
- this.lastText = request.responseText;
1236
- }
1237
- this.timer = setTimeout(this.onTimerEvent.bind(this),
1238
- this.decay * this.frequency * 1000);
1239
- },
1240
-
1241
- onTimerEvent: function() {
1242
- this.updater = new Ajax.Updater(this.container, this.url, this.options);
1243
- }
1244
- });
1245
- function $(element) {
1246
- if (arguments.length > 1) {
1247
- for (var i = 0, elements = [], length = arguments.length; i < length; i++)
1248
- elements.push($(arguments[i]));
1249
- return elements;
1250
- }
1251
- if (typeof element == 'string')
1252
- element = document.getElementById(element);
1253
- return Element.extend(element);
1254
- }
1255
-
1256
- if (Prototype.BrowserFeatures.XPath) {
1257
- document._getElementsByXPath = function(expression, parentElement) {
1258
- var results = [];
1259
- var query = document.evaluate(expression, $(parentElement) || document,
1260
- null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
1261
- for (var i = 0, length = query.snapshotLength; i < length; i++)
1262
- results.push(query.snapshotItem(i));
1263
- return results;
1264
- };
1265
-
1266
- document.getElementsByClassName = function(className, parentElement) {
1267
- var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
1268
- return document._getElementsByXPath(q, parentElement);
1269
- }
1270
-
1271
- } else document.getElementsByClassName = function(className, parentElement) {
1272
- var children = ($(parentElement) || document.body).getElementsByTagName('*');
1273
- var elements = [], child;
1274
- for (var i = 0, length = children.length; i < length; i++) {
1275
- child = children[i];
1276
- if (Element.hasClassName(child, className))
1277
- elements.push(Element.extend(child));
1278
- }
1279
- return elements;
1280
- };
1281
-
1282
- /*--------------------------------------------------------------------------*/
1283
-
1284
- if (!window.Element) var Element = {};
1285
-
1286
- Element.extend = function(element) {
1287
- var F = Prototype.BrowserFeatures;
1288
- if (!element || !element.tagName || element.nodeType == 3 ||
1289
- element._extended || F.SpecificElementExtensions || element == window)
1290
- return element;
1291
-
1292
- var methods = {}, tagName = element.tagName, cache = Element.extend.cache,
1293
- T = Element.Methods.ByTag;
1294
-
1295
- // extend methods for all tags (Safari doesn't need this)
1296
- if (!F.ElementExtensions) {
1297
- Object.extend(methods, Element.Methods),
1298
- Object.extend(methods, Element.Methods.Simulated);
1299
- }
1300
-
1301
- // extend methods for specific tags
1302
- if (T[tagName]) Object.extend(methods, T[tagName]);
1303
-
1304
- for (var property in methods) {
1305
- var value = methods[property];
1306
- if (typeof value == 'function' && !(property in element))
1307
- element[property] = cache.findOrStore(value);
1308
- }
1309
-
1310
- element._extended = Prototype.emptyFunction;
1311
- return element;
1312
- };
1313
-
1314
- Element.extend.cache = {
1315
- findOrStore: function(value) {
1316
- return this[value] = this[value] || function() {
1317
- return value.apply(null, [this].concat($A(arguments)));
1318
- }
1319
- }
1320
- };
1321
-
1322
- Element.Methods = {
1323
- visible: function(element) {
1324
- return $(element).style.display != 'none';
1325
- },
1326
-
1327
- toggle: function(element) {
1328
- element = $(element);
1329
- Element[Element.visible(element) ? 'hide' : 'show'](element);
1330
- return element;
1331
- },
1332
-
1333
- hide: function(element) {
1334
- $(element).style.display = 'none';
1335
- return element;
1336
- },
1337
-
1338
- show: function(element) {
1339
- $(element).style.display = '';
1340
- return element;
1341
- },
1342
-
1343
- remove: function(element) {
1344
- element = $(element);
1345
- element.parentNode.removeChild(element);
1346
- return element;
1347
- },
1348
-
1349
- update: function(element, html) {
1350
- html = typeof html == 'undefined' ? '' : html.toString();
1351
- $(element).innerHTML = html.stripScripts();
1352
- setTimeout(function() {html.evalScripts()}, 10);
1353
- return element;
1354
- },
1355
-
1356
- replace: function(element, html) {
1357
- element = $(element);
1358
- html = typeof html == 'undefined' ? '' : html.toString();
1359
- if (element.outerHTML) {
1360
- element.outerHTML = html.stripScripts();
1361
- } else {
1362
- var range = element.ownerDocument.createRange();
1363
- range.selectNodeContents(element);
1364
- element.parentNode.replaceChild(
1365
- range.createContextualFragment(html.stripScripts()), element);
1366
- }
1367
- setTimeout(function() {html.evalScripts()}, 10);
1368
- return element;
1369
- },
1370
-
1371
- inspect: function(element) {
1372
- element = $(element);
1373
- var result = '<' + element.tagName.toLowerCase();
1374
- $H({'id': 'id', 'className': 'class'}).each(function(pair) {
1375
- var property = pair.first(), attribute = pair.last();
1376
- var value = (element[property] || '').toString();
1377
- if (value) result += ' ' + attribute + '=' + value.inspect(true);
1378
- });
1379
- return result + '>';
1380
- },
1381
-
1382
- recursivelyCollect: function(element, property) {
1383
- element = $(element);
1384
- var elements = [];
1385
- while (element = element[property])
1386
- if (element.nodeType == 1)
1387
- elements.push(Element.extend(element));
1388
- return elements;
1389
- },
1390
-
1391
- ancestors: function(element) {
1392
- return $(element).recursivelyCollect('parentNode');
1393
- },
1394
-
1395
- descendants: function(element) {
1396
- return $A($(element).getElementsByTagName('*')).each(Element.extend);
1397
- },
1398
-
1399
- firstDescendant: function(element) {
1400
- element = $(element).firstChild;
1401
- while (element && element.nodeType != 1) element = element.nextSibling;
1402
- return $(element);
1403
- },
1404
-
1405
- immediateDescendants: function(element) {
1406
- if (!(element = $(element).firstChild)) return [];
1407
- while (element && element.nodeType != 1) element = element.nextSibling;
1408
- if (element) return [element].concat($(element).nextSiblings());
1409
- return [];
1410
- },
1411
-
1412
- previousSiblings: function(element) {
1413
- return $(element).recursivelyCollect('previousSibling');
1414
- },
1415
-
1416
- nextSiblings: function(element) {
1417
- return $(element).recursivelyCollect('nextSibling');
1418
- },
1419
-
1420
- siblings: function(element) {
1421
- element = $(element);
1422
- return element.previousSiblings().reverse().concat(element.nextSiblings());
1423
- },
1424
-
1425
- match: function(element, selector) {
1426
- if (typeof selector == 'string')
1427
- selector = new Selector(selector);
1428
- return selector.match($(element));
1429
- },
1430
-
1431
- up: function(element, expression, index) {
1432
- element = $(element);
1433
- if (arguments.length == 1) return $(element.parentNode);
1434
- var ancestors = element.ancestors();
1435
- return expression ? Selector.findElement(ancestors, expression, index) :
1436
- ancestors[index || 0];
1437
- },
1438
-
1439
- down: function(element, expression, index) {
1440
- element = $(element);
1441
- if (arguments.length == 1) return element.firstDescendant();
1442
- var descendants = element.descendants();
1443
- return expression ? Selector.findElement(descendants, expression, index) :
1444
- descendants[index || 0];
1445
- },
1446
-
1447
- previous: function(element, expression, index) {
1448
- element = $(element);
1449
- if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
1450
- var previousSiblings = element.previousSiblings();
1451
- return expression ? Selector.findElement(previousSiblings, expression, index) :
1452
- previousSiblings[index || 0];
1453
- },
1454
-
1455
- next: function(element, expression, index) {
1456
- element = $(element);
1457
- if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
1458
- var nextSiblings = element.nextSiblings();
1459
- return expression ? Selector.findElement(nextSiblings, expression, index) :
1460
- nextSiblings[index || 0];
1461
- },
1462
-
1463
- getElementsBySelector: function() {
1464
- var args = $A(arguments), element = $(args.shift());
1465
- return Selector.findChildElements(element, args);
1466
- },
1467
-
1468
- getElementsByClassName: function(element, className) {
1469
- return document.getElementsByClassName(className, element);
1470
- },
1471
-
1472
- readAttribute: function(element, name) {
1473
- element = $(element);
1474
- if (Prototype.Browser.IE) {
1475
- if (!element.attributes) return null;
1476
- var t = Element._attributeTranslations;
1477
- if (t.values[name]) return t.values[name](element, name);
1478
- if (t.names[name]) name = t.names[name];
1479
- var attribute = element.attributes[name];
1480
- return attribute ? attribute.nodeValue : null;
1481
- }
1482
- return element.getAttribute(name);
1483
- },
1484
-
1485
- getHeight: function(element) {
1486
- return $(element).getDimensions().height;
1487
- },
1488
-
1489
- getWidth: function(element) {
1490
- return $(element).getDimensions().width;
1491
- },
1492
-
1493
- classNames: function(element) {
1494
- return new Element.ClassNames(element);
1495
- },
1496
-
1497
- hasClassName: function(element, className) {
1498
- if (!(element = $(element))) return;
1499
- var elementClassName = element.className;
1500
- if (elementClassName.length == 0) return false;
1501
- if (elementClassName == className ||
1502
- elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
1503
- return true;
1504
- return false;
1505
- },
1506
-
1507
- addClassName: function(element, className) {
1508
- if (!(element = $(element))) return;
1509
- Element.classNames(element).add(className);
1510
- return element;
1511
- },
1512
-
1513
- removeClassName: function(element, className) {
1514
- if (!(element = $(element))) return;
1515
- Element.classNames(element).remove(className);
1516
- return element;
1517
- },
1518
-
1519
- toggleClassName: function(element, className) {
1520
- if (!(element = $(element))) return;
1521
- Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
1522
- return element;
1523
- },
1524
-
1525
- observe: function() {
1526
- Event.observe.apply(Event, arguments);
1527
- return $A(arguments).first();
1528
- },
1529
-
1530
- stopObserving: function() {
1531
- Event.stopObserving.apply(Event, arguments);
1532
- return $A(arguments).first();
1533
- },
1534
-
1535
- // removes whitespace-only text node children
1536
- cleanWhitespace: function(element) {
1537
- element = $(element);
1538
- var node = element.firstChild;
1539
- while (node) {
1540
- var nextNode = node.nextSibling;
1541
- if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
1542
- element.removeChild(node);
1543
- node = nextNode;
1544
- }
1545
- return element;
1546
- },
1547
-
1548
- empty: function(element) {
1549
- return $(element).innerHTML.blank();
1550
- },
1551
-
1552
- descendantOf: function(element, ancestor) {
1553
- element = $(element), ancestor = $(ancestor);
1554
- while (element = element.parentNode)
1555
- if (element == ancestor) return true;
1556
- return false;
1557
- },
1558
-
1559
- scrollTo: function(element) {
1560
- element = $(element);
1561
- var pos = Position.cumulativeOffset(element);
1562
- window.scrollTo(pos[0], pos[1]);
1563
- return element;
1564
- },
1565
-
1566
- getStyle: function(element, style) {
1567
- element = $(element);
1568
- style = style == 'float' ? 'cssFloat' : style.camelize();
1569
- var value = element.style[style];
1570
- if (!value) {
1571
- var css = document.defaultView.getComputedStyle(element, null);
1572
- value = css ? css[style] : null;
1573
- }
1574
- if (style == 'opacity') return value ? parseFloat(value) : 1.0;
1575
- return value == 'auto' ? null : value;
1576
- },
1577
-
1578
- getOpacity: function(element) {
1579
- return $(element).getStyle('opacity');
1580
- },
1581
-
1582
- setStyle: function(element, styles, camelized) {
1583
- element = $(element);
1584
- var elementStyle = element.style;
1585
-
1586
- for (var property in styles)
1587
- if (property == 'opacity') element.setOpacity(styles[property])
1588
- else
1589
- elementStyle[(property == 'float' || property == 'cssFloat') ?
1590
- (elementStyle.styleFloat === undefined ? 'cssFloat' : 'styleFloat') :
1591
- (camelized ? property : property.camelize())] = styles[property];
1592
-
1593
- return element;
1594
- },
1595
-
1596
- setOpacity: function(element, value) {
1597
- element = $(element);
1598
- element.style.opacity = (value == 1 || value === '') ? '' :
1599
- (value < 0.00001) ? 0 : value;
1600
- return element;
1601
- },
1602
-
1603
- getDimensions: function(element) {
1604
- element = $(element);
1605
- var display = $(element).getStyle('display');
1606
- if (display != 'none' && display != null) // Safari bug
1607
- return {width: element.offsetWidth, height: element.offsetHeight};
1608
-
1609
- // All *Width and *Height properties give 0 on elements with display none,
1610
- // so enable the element temporarily
1611
- var els = element.style;
1612
- var originalVisibility = els.visibility;
1613
- var originalPosition = els.position;
1614
- var originalDisplay = els.display;
1615
- els.visibility = 'hidden';
1616
- els.position = 'absolute';
1617
- els.display = 'block';
1618
- var originalWidth = element.clientWidth;
1619
- var originalHeight = element.clientHeight;
1620
- els.display = originalDisplay;
1621
- els.position = originalPosition;
1622
- els.visibility = originalVisibility;
1623
- return {width: originalWidth, height: originalHeight};
1624
- },
1625
-
1626
- makePositioned: function(element) {
1627
- element = $(element);
1628
- var pos = Element.getStyle(element, 'position');
1629
- if (pos == 'static' || !pos) {
1630
- element._madePositioned = true;
1631
- element.style.position = 'relative';
1632
- // Opera returns the offset relative to the positioning context, when an
1633
- // element is position relative but top and left have not been defined
1634
- if (window.opera) {
1635
- element.style.top = 0;
1636
- element.style.left = 0;
1637
- }
1638
- }
1639
- return element;
1640
- },
1641
-
1642
- undoPositioned: function(element) {
1643
- element = $(element);
1644
- if (element._madePositioned) {
1645
- element._madePositioned = undefined;
1646
- element.style.position =
1647
- element.style.top =
1648
- element.style.left =
1649
- element.style.bottom =
1650
- element.style.right = '';
1651
- }
1652
- return element;
1653
- },
1654
-
1655
- makeClipping: function(element) {
1656
- element = $(element);
1657
- if (element._overflow) return element;
1658
- element._overflow = element.style.overflow || 'auto';
1659
- if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
1660
- element.style.overflow = 'hidden';
1661
- return element;
1662
- },
1663
-
1664
- undoClipping: function(element) {
1665
- element = $(element);
1666
- if (!element._overflow) return element;
1667
- element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
1668
- element._overflow = null;
1669
- return element;
1670
- }
1671
- };
1672
-
1673
- Object.extend(Element.Methods, {
1674
- childOf: Element.Methods.descendantOf,
1675
- childElements: Element.Methods.immediateDescendants
1676
- });
1677
-
1678
- if (Prototype.Browser.Opera) {
1679
- Element.Methods._getStyle = Element.Methods.getStyle;
1680
- Element.Methods.getStyle = function(element, style) {
1681
- switch(style) {
1682
- case 'left':
1683
- case 'top':
1684
- case 'right':
1685
- case 'bottom':
1686
- if (Element._getStyle(element, 'position') == 'static') return null;
1687
- default: return Element._getStyle(element, style);
1688
- }
1689
- };
1690
- }
1691
- else if (Prototype.Browser.IE) {
1692
- Element.Methods.getStyle = function(element, style) {
1693
- element = $(element);
1694
- style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
1695
- var value = element.style[style];
1696
- if (!value && element.currentStyle) value = element.currentStyle[style];
1697
-
1698
- if (style == 'opacity') {
1699
- if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
1700
- if (value[1]) return parseFloat(value[1]) / 100;
1701
- return 1.0;
1702
- }
1703
-
1704
- if (value == 'auto') {
1705
- if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
1706
- return element['offset'+style.capitalize()] + 'px';
1707
- return null;
1708
- }
1709
- return value;
1710
- };
1711
-
1712
- Element.Methods.setOpacity = function(element, value) {
1713
- element = $(element);
1714
- var filter = element.getStyle('filter'), style = element.style;
1715
- if (value == 1 || value === '') {
1716
- style.filter = filter.replace(/alpha\([^\)]*\)/gi,'');
1717
- return element;
1718
- } else if (value < 0.00001) value = 0;
1719
- style.filter = filter.replace(/alpha\([^\)]*\)/gi, '') +
1720
- 'alpha(opacity=' + (value * 100) + ')';
1721
- return element;
1722
- };
1723
-
1724
- // IE is missing .innerHTML support for TABLE-related elements
1725
- Element.Methods.update = function(element, html) {
1726
- element = $(element);
1727
- html = typeof html == 'undefined' ? '' : html.toString();
1728
- var tagName = element.tagName.toUpperCase();
1729
- if (['THEAD','TBODY','TR','TD'].include(tagName)) {
1730
- var div = document.createElement('div');
1731
- switch (tagName) {
1732
- case 'THEAD':
1733
- case 'TBODY':
1734
- div.innerHTML = '<table><tbody>' + html.stripScripts() + '</tbody></table>';
1735
- depth = 2;
1736
- break;
1737
- case 'TR':
1738
- div.innerHTML = '<table><tbody><tr>' + html.stripScripts() + '</tr></tbody></table>';
1739
- depth = 3;
1740
- break;
1741
- case 'TD':
1742
- div.innerHTML = '<table><tbody><tr><td>' + html.stripScripts() + '</td></tr></tbody></table>';
1743
- depth = 4;
1744
- }
1745
- $A(element.childNodes).each(function(node) { element.removeChild(node) });
1746
- depth.times(function() { div = div.firstChild });
1747
- $A(div.childNodes).each(function(node) { element.appendChild(node) });
1748
- } else {
1749
- element.innerHTML = html.stripScripts();
1750
- }
1751
- setTimeout(function() { html.evalScripts() }, 10);
1752
- return element;
1753
- }
1754
- }
1755
- else if (Prototype.Browser.Gecko) {
1756
- Element.Methods.setOpacity = function(element, value) {
1757
- element = $(element);
1758
- element.style.opacity = (value == 1) ? 0.999999 :
1759
- (value === '') ? '' : (value < 0.00001) ? 0 : value;
1760
- return element;
1761
- };
1762
- }
1763
-
1764
- Element._attributeTranslations = {
1765
- names: {
1766
- colspan: "colSpan",
1767
- rowspan: "rowSpan",
1768
- valign: "vAlign",
1769
- datetime: "dateTime",
1770
- accesskey: "accessKey",
1771
- tabindex: "tabIndex",
1772
- enctype: "encType",
1773
- maxlength: "maxLength",
1774
- readonly: "readOnly",
1775
- longdesc: "longDesc"
1776
- },
1777
- values: {
1778
- _getAttr: function(element, attribute) {
1779
- return element.getAttribute(attribute, 2);
1780
- },
1781
- _flag: function(element, attribute) {
1782
- return $(element).hasAttribute(attribute) ? attribute : null;
1783
- },
1784
- style: function(element) {
1785
- return element.style.cssText.toLowerCase();
1786
- },
1787
- title: function(element) {
1788
- var node = element.getAttributeNode('title');
1789
- return node.specified ? node.nodeValue : null;
1790
- }
1791
- }
1792
- };
1793
-
1794
- (function() {
1795
- Object.extend(this, {
1796
- href: this._getAttr,
1797
- src: this._getAttr,
1798
- type: this._getAttr,
1799
- disabled: this._flag,
1800
- checked: this._flag,
1801
- readonly: this._flag,
1802
- multiple: this._flag
1803
- });
1804
- }).call(Element._attributeTranslations.values);
1805
-
1806
- Element.Methods.Simulated = {
1807
- hasAttribute: function(element, attribute) {
1808
- var t = Element._attributeTranslations, node;
1809
- attribute = t.names[attribute] || attribute;
1810
- node = $(element).getAttributeNode(attribute);
1811
- return node && node.specified;
1812
- }
1813
- };
1814
-
1815
- Element.Methods.ByTag = {};
1816
-
1817
- Object.extend(Element, Element.Methods);
1818
-
1819
- if (!Prototype.BrowserFeatures.ElementExtensions &&
1820
- document.createElement('div').__proto__) {
1821
- window.HTMLElement = {};
1822
- window.HTMLElement.prototype = document.createElement('div').__proto__;
1823
- Prototype.BrowserFeatures.ElementExtensions = true;
1824
- }
1825
-
1826
- Element.hasAttribute = function(element, attribute) {
1827
- if (element.hasAttribute) return element.hasAttribute(attribute);
1828
- return Element.Methods.Simulated.hasAttribute(element, attribute);
1829
- };
1830
-
1831
- Element.addMethods = function(methods) {
1832
- var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
1833
-
1834
- if (!methods) {
1835
- Object.extend(Form, Form.Methods);
1836
- Object.extend(Form.Element, Form.Element.Methods);
1837
- Object.extend(Element.Methods.ByTag, {
1838
- "FORM": Object.clone(Form.Methods),
1839
- "INPUT": Object.clone(Form.Element.Methods),
1840
- "SELECT": Object.clone(Form.Element.Methods),
1841
- "TEXTAREA": Object.clone(Form.Element.Methods)
1842
- });
1843
- }
1844
-
1845
- if (arguments.length == 2) {
1846
- var tagName = methods;
1847
- methods = arguments[1];
1848
- }
1849
-
1850
- if (!tagName) Object.extend(Element.Methods, methods || {});
1851
- else {
1852
- if (tagName.constructor == Array) tagName.each(extend);
1853
- else extend(tagName);
1854
- }
1855
-
1856
- function extend(tagName) {
1857
- tagName = tagName.toUpperCase();
1858
- if (!Element.Methods.ByTag[tagName])
1859
- Element.Methods.ByTag[tagName] = {};
1860
- Object.extend(Element.Methods.ByTag[tagName], methods);
1861
- }
1862
-
1863
- function copy(methods, destination, onlyIfAbsent) {
1864
- onlyIfAbsent = onlyIfAbsent || false;
1865
- var cache = Element.extend.cache;
1866
- for (var property in methods) {
1867
- var value = methods[property];
1868
- if (!onlyIfAbsent || !(property in destination))
1869
- destination[property] = cache.findOrStore(value);
1870
- }
1871
- }
1872
-
1873
- function findDOMClass(tagName) {
1874
- var klass;
1875
- var trans = {
1876
- "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
1877
- "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
1878
- "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
1879
- "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
1880
- "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
1881
- "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
1882
- "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
1883
- "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
1884
- "FrameSet", "IFRAME": "IFrame"
1885
- };
1886
- if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
1887
- if (window[klass]) return window[klass];
1888
- klass = 'HTML' + tagName + 'Element';
1889
- if (window[klass]) return window[klass];
1890
- klass = 'HTML' + tagName.capitalize() + 'Element';
1891
- if (window[klass]) return window[klass];
1892
-
1893
- window[klass] = {};
1894
- window[klass].prototype = document.createElement(tagName).__proto__;
1895
- return window[klass];
1896
- }
1897
-
1898
- if (F.ElementExtensions) {
1899
- copy(Element.Methods, HTMLElement.prototype);
1900
- copy(Element.Methods.Simulated, HTMLElement.prototype, true);
1901
- }
1902
-
1903
- if (F.SpecificElementExtensions) {
1904
- for (var tag in Element.Methods.ByTag) {
1905
- var klass = findDOMClass(tag);
1906
- if (typeof klass == "undefined") continue;
1907
- copy(T[tag], klass.prototype);
1908
- }
1909
- }
1910
-
1911
- Object.extend(Element, Element.Methods);
1912
- delete Element.ByTag;
1913
- };
1914
-
1915
- var Toggle = { display: Element.toggle };
1916
-
1917
- /*--------------------------------------------------------------------------*/
1918
-
1919
- Abstract.Insertion = function(adjacency) {
1920
- this.adjacency = adjacency;
1921
- }
1922
-
1923
- Abstract.Insertion.prototype = {
1924
- initialize: function(element, content) {
1925
- this.element = $(element);
1926
- this.content = content.stripScripts();
1927
-
1928
- if (this.adjacency && this.element.insertAdjacentHTML) {
1929
- try {
1930
- this.element.insertAdjacentHTML(this.adjacency, this.content);
1931
- } catch (e) {
1932
- var tagName = this.element.tagName.toUpperCase();
1933
- if (['TBODY', 'TR'].include(tagName)) {
1934
- this.insertContent(this.contentFromAnonymousTable());
1935
- } else {
1936
- throw e;
1937
- }
1938
- }
1939
- } else {
1940
- this.range = this.element.ownerDocument.createRange();
1941
- if (this.initializeRange) this.initializeRange();
1942
- this.insertContent([this.range.createContextualFragment(this.content)]);
1943
- }
1944
-
1945
- setTimeout(function() {content.evalScripts()}, 10);
1946
- },
1947
-
1948
- contentFromAnonymousTable: function() {
1949
- var div = document.createElement('div');
1950
- div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
1951
- return $A(div.childNodes[0].childNodes[0].childNodes);
1952
- }
1953
- }
1954
-
1955
- var Insertion = new Object();
1956
-
1957
- Insertion.Before = Class.create();
1958
- Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
1959
- initializeRange: function() {
1960
- this.range.setStartBefore(this.element);
1961
- },
1962
-
1963
- insertContent: function(fragments) {
1964
- fragments.each((function(fragment) {
1965
- this.element.parentNode.insertBefore(fragment, this.element);
1966
- }).bind(this));
1967
- }
1968
- });
1969
-
1970
- Insertion.Top = Class.create();
1971
- Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
1972
- initializeRange: function() {
1973
- this.range.selectNodeContents(this.element);
1974
- this.range.collapse(true);
1975
- },
1976
-
1977
- insertContent: function(fragments) {
1978
- fragments.reverse(false).each((function(fragment) {
1979
- this.element.insertBefore(fragment, this.element.firstChild);
1980
- }).bind(this));
1981
- }
1982
- });
1983
-
1984
- Insertion.Bottom = Class.create();
1985
- Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
1986
- initializeRange: function() {
1987
- this.range.selectNodeContents(this.element);
1988
- this.range.collapse(this.element);
1989
- },
1990
-
1991
- insertContent: function(fragments) {
1992
- fragments.each((function(fragment) {
1993
- this.element.appendChild(fragment);
1994
- }).bind(this));
1995
- }
1996
- });
1997
-
1998
- Insertion.After = Class.create();
1999
- Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
2000
- initializeRange: function() {
2001
- this.range.setStartAfter(this.element);
2002
- },
2003
-
2004
- insertContent: function(fragments) {
2005
- fragments.each((function(fragment) {
2006
- this.element.parentNode.insertBefore(fragment,
2007
- this.element.nextSibling);
2008
- }).bind(this));
2009
- }
2010
- });
2011
-
2012
- /*--------------------------------------------------------------------------*/
2013
-
2014
- Element.ClassNames = Class.create();
2015
- Element.ClassNames.prototype = {
2016
- initialize: function(element) {
2017
- this.element = $(element);
2018
- },
2019
-
2020
- _each: function(iterator) {
2021
- this.element.className.split(/\s+/).select(function(name) {
2022
- return name.length > 0;
2023
- })._each(iterator);
2024
- },
2025
-
2026
- set: function(className) {
2027
- this.element.className = className;
2028
- },
2029
-
2030
- add: function(classNameToAdd) {
2031
- if (this.include(classNameToAdd)) return;
2032
- this.set($A(this).concat(classNameToAdd).join(' '));
2033
- },
2034
-
2035
- remove: function(classNameToRemove) {
2036
- if (!this.include(classNameToRemove)) return;
2037
- this.set($A(this).without(classNameToRemove).join(' '));
2038
- },
2039
-
2040
- toString: function() {
2041
- return $A(this).join(' ');
2042
- }
2043
- };
2044
-
2045
- Object.extend(Element.ClassNames.prototype, Enumerable);
2046
- /* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
2047
- * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
2048
- * license. Please see http://www.yui-ext.com/ for more information. */
2049
-
2050
- var Selector = Class.create();
2051
-
2052
- Selector.prototype = {
2053
- initialize: function(expression) {
2054
- this.expression = expression.strip();
2055
- this.compileMatcher();
2056
- },
2057
-
2058
- compileMatcher: function() {
2059
- // Selectors with namespaced attributes can't use the XPath version
2060
- if (Prototype.BrowserFeatures.XPath && !(/\[[\w-]*?:/).test(this.expression))
2061
- return this.compileXPathMatcher();
2062
-
2063
- var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
2064
- c = Selector.criteria, le, p, m;
2065
-
2066
- if (Selector._cache[e]) {
2067
- this.matcher = Selector._cache[e]; return;
2068
- }
2069
- this.matcher = ["this.matcher = function(root) {",
2070
- "var r = root, h = Selector.handlers, c = false, n;"];
2071
-
2072
- while (e && le != e && (/\S/).test(e)) {
2073
- le = e;
2074
- for (var i in ps) {
2075
- p = ps[i];
2076
- if (m = e.match(p)) {
2077
- this.matcher.push(typeof c[i] == 'function' ? c[i](m) :
2078
- new Template(c[i]).evaluate(m));
2079
- e = e.replace(m[0], '');
2080
- break;
2081
- }
2082
- }
2083
- }
2084
-
2085
- this.matcher.push("return h.unique(n);\n}");
2086
- eval(this.matcher.join('\n'));
2087
- Selector._cache[this.expression] = this.matcher;
2088
- },
2089
-
2090
- compileXPathMatcher: function() {
2091
- var e = this.expression, ps = Selector.patterns,
2092
- x = Selector.xpath, le, m;
2093
-
2094
- if (Selector._cache[e]) {
2095
- this.xpath = Selector._cache[e]; return;
2096
- }
2097
-
2098
- this.matcher = ['.//*'];
2099
- while (e && le != e && (/\S/).test(e)) {
2100
- le = e;
2101
- for (var i in ps) {
2102
- if (m = e.match(ps[i])) {
2103
- this.matcher.push(typeof x[i] == 'function' ? x[i](m) :
2104
- new Template(x[i]).evaluate(m));
2105
- e = e.replace(m[0], '');
2106
- break;
2107
- }
2108
- }
2109
- }
2110
-
2111
- this.xpath = this.matcher.join('');
2112
- Selector._cache[this.expression] = this.xpath;
2113
- },
2114
-
2115
- findElements: function(root) {
2116
- root = root || document;
2117
- if (this.xpath) return document._getElementsByXPath(this.xpath, root);
2118
- return this.matcher(root);
2119
- },
2120
-
2121
- match: function(element) {
2122
- return this.findElements(document).include(element);
2123
- },
2124
-
2125
- toString: function() {
2126
- return this.expression;
2127
- },
2128
-
2129
- inspect: function() {
2130
- return "#<Selector:" + this.expression.inspect() + ">";
2131
- }
2132
- };
2133
-
2134
- Object.extend(Selector, {
2135
- _cache: {},
2136
-
2137
- xpath: {
2138
- descendant: "//*",
2139
- child: "/*",
2140
- adjacent: "/following-sibling::*[1]",
2141
- laterSibling: '/following-sibling::*',
2142
- tagName: function(m) {
2143
- if (m[1] == '*') return '';
2144
- return "[local-name()='" + m[1].toLowerCase() +
2145
- "' or local-name()='" + m[1].toUpperCase() + "']";
2146
- },
2147
- className: "[contains(concat(' ', @class, ' '), ' #{1} ')]",
2148
- id: "[@id='#{1}']",
2149
- attrPresence: "[@#{1}]",
2150
- attr: function(m) {
2151
- m[3] = m[5] || m[6];
2152
- return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
2153
- },
2154
- pseudo: function(m) {
2155
- var h = Selector.xpath.pseudos[m[1]];
2156
- if (!h) return '';
2157
- if (typeof h === 'function') return h(m);
2158
- return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
2159
- },
2160
- operators: {
2161
- '=': "[@#{1}='#{3}']",
2162
- '!=': "[@#{1}!='#{3}']",
2163
- '^=': "[starts-with(@#{1}, '#{3}')]",
2164
- '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
2165
- '*=': "[contains(@#{1}, '#{3}')]",
2166
- '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
2167
- '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
2168
- },
2169
- pseudos: {
2170
- 'first-child': '[not(preceding-sibling::*)]',
2171
- 'last-child': '[not(following-sibling::*)]',
2172
- 'only-child': '[not(preceding-sibling::* or following-sibling::*)]',
2173
- 'empty': "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
2174
- 'checked': "[@checked]",
2175
- 'disabled': "[@disabled]",
2176
- 'enabled': "[not(@disabled)]",
2177
- 'not': function(m) {
2178
- var e = m[6], p = Selector.patterns,
2179
- x = Selector.xpath, le, m, v;
2180
-
2181
- var exclusion = [];
2182
- while (e && le != e && (/\S/).test(e)) {
2183
- le = e;
2184
- for (var i in p) {
2185
- if (m = e.match(p[i])) {
2186
- v = typeof x[i] == 'function' ? x[i](m) : new Template(x[i]).evaluate(m);
2187
- exclusion.push("(" + v.substring(1, v.length - 1) + ")");
2188
- e = e.replace(m[0], '');
2189
- break;
2190
- }
2191
- }
2192
- }
2193
- return "[not(" + exclusion.join(" and ") + ")]";
2194
- },
2195
- 'nth-child': function(m) {
2196
- return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
2197
- },
2198
- 'nth-last-child': function(m) {
2199
- return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
2200
- },
2201
- 'nth-of-type': function(m) {
2202
- return Selector.xpath.pseudos.nth("position() ", m);
2203
- },
2204
- 'nth-last-of-type': function(m) {
2205
- return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
2206
- },
2207
- 'first-of-type': function(m) {
2208
- m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
2209
- },
2210
- 'last-of-type': function(m) {
2211
- m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
2212
- },
2213
- 'only-of-type': function(m) {
2214
- var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
2215
- },
2216
- nth: function(fragment, m) {
2217
- var mm, formula = m[6], predicate;
2218
- if (formula == 'even') formula = '2n+0';
2219
- if (formula == 'odd') formula = '2n+1';
2220
- if (mm = formula.match(/^(\d+)$/)) // digit only
2221
- return '[' + fragment + "= " + mm[1] + ']';
2222
- if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
2223
- if (mm[1] == "-") mm[1] = -1;
2224
- var a = mm[1] ? Number(mm[1]) : 1;
2225
- var b = mm[2] ? Number(mm[2]) : 0;
2226
- predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
2227
- "((#{fragment} - #{b}) div #{a} >= 0)]";
2228
- return new Template(predicate).evaluate({
2229
- fragment: fragment, a: a, b: b });
2230
- }
2231
- }
2232
- }
2233
- },
2234
-
2235
- criteria: {
2236
- tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;',
2237
- className: 'n = h.className(n, r, "#{1}", c); c = false;',
2238
- id: 'n = h.id(n, r, "#{1}", c); c = false;',
2239
- attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;',
2240
- attr: function(m) {
2241
- m[3] = (m[5] || m[6]);
2242
- return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m);
2243
- },
2244
- pseudo: function(m) {
2245
- if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
2246
- return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
2247
- },
2248
- descendant: 'c = "descendant";',
2249
- child: 'c = "child";',
2250
- adjacent: 'c = "adjacent";',
2251
- laterSibling: 'c = "laterSibling";'
2252
- },
2253
-
2254
- patterns: {
2255
- // combinators must be listed first
2256
- // (and descendant needs to be last combinator)
2257
- laterSibling: /^\s*~\s*/,
2258
- child: /^\s*>\s*/,
2259
- adjacent: /^\s*\+\s*/,
2260
- descendant: /^\s/,
2261
-
2262
- // selectors follow
2263
- tagName: /^\s*(\*|[\w\-]+)(\b|$)?/,
2264
- id: /^#([\w\-\*]+)(\b|$)/,
2265
- className: /^\.([\w\-\*]+)(\b|$)/,
2266
- pseudo: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|\s|(?=:))/,
2267
- attrPresence: /^\[([\w]+)\]/,
2268
- attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\]]*?)\4|([^'"][^\]]*?)))?\]/
2269
- },
2270
-
2271
- handlers: {
2272
- // UTILITY FUNCTIONS
2273
- // joins two collections
2274
- concat: function(a, b) {
2275
- for (var i = 0, node; node = b[i]; i++)
2276
- a.push(node);
2277
- return a;
2278
- },
2279
-
2280
- // marks an array of nodes for counting
2281
- mark: function(nodes) {
2282
- for (var i = 0, node; node = nodes[i]; i++)
2283
- node._counted = true;
2284
- return nodes;
2285
- },
2286
-
2287
- unmark: function(nodes) {
2288
- for (var i = 0, node; node = nodes[i]; i++)
2289
- node._counted = undefined;
2290
- return nodes;
2291
- },
2292
-
2293
- // mark each child node with its position (for nth calls)
2294
- // "ofType" flag indicates whether we're indexing for nth-of-type
2295
- // rather than nth-child
2296
- index: function(parentNode, reverse, ofType) {
2297
- parentNode._counted = true;
2298
- if (reverse) {
2299
- for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
2300
- node = nodes[i];
2301
- if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
2302
- }
2303
- } else {
2304
- for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
2305
- if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
2306
- }
2307
- },
2308
-
2309
- // filters out duplicates and extends all nodes
2310
- unique: function(nodes) {
2311
- if (nodes.length == 0) return nodes;
2312
- var results = [], n;
2313
- for (var i = 0, l = nodes.length; i < l; i++)
2314
- if (!(n = nodes[i])._counted) {
2315
- n._counted = true;
2316
- results.push(Element.extend(n));
2317
- }
2318
- return Selector.handlers.unmark(results);
2319
- },
2320
-
2321
- // COMBINATOR FUNCTIONS
2322
- descendant: function(nodes) {
2323
- var h = Selector.handlers;
2324
- for (var i = 0, results = [], node; node = nodes[i]; i++)
2325
- h.concat(results, node.getElementsByTagName('*'));
2326
- return results;
2327
- },
2328
-
2329
- child: function(nodes) {
2330
- var h = Selector.handlers;
2331
- for (var i = 0, results = [], node; node = nodes[i]; i++) {
2332
- for (var j = 0, children = [], child; child = node.childNodes[j]; j++)
2333
- if (child.nodeType == 1 && child.tagName != '!') results.push(child);
2334
- }
2335
- return results;
2336
- },
2337
-
2338
- adjacent: function(nodes) {
2339
- for (var i = 0, results = [], node; node = nodes[i]; i++) {
2340
- var next = this.nextElementSibling(node);
2341
- if (next) results.push(next);
2342
- }
2343
- return results;
2344
- },
2345
-
2346
- laterSibling: function(nodes) {
2347
- var h = Selector.handlers;
2348
- for (var i = 0, results = [], node; node = nodes[i]; i++)
2349
- h.concat(results, Element.nextSiblings(node));
2350
- return results;
2351
- },
2352
-
2353
- nextElementSibling: function(node) {
2354
- while (node = node.nextSibling)
2355
- if (node.nodeType == 1) return node;
2356
- return null;
2357
- },
2358
-
2359
- previousElementSibling: function(node) {
2360
- while (node = node.previousSibling)
2361
- if (node.nodeType == 1) return node;
2362
- return null;
2363
- },
2364
-
2365
- // TOKEN FUNCTIONS
2366
- tagName: function(nodes, root, tagName, combinator) {
2367
- tagName = tagName.toUpperCase();
2368
- var results = [], h = Selector.handlers;
2369
- if (nodes) {
2370
- if (combinator) {
2371
- // fastlane for ordinary descendant combinators
2372
- if (combinator == "descendant") {
2373
- for (var i = 0, node; node = nodes[i]; i++)
2374
- h.concat(results, node.getElementsByTagName(tagName));
2375
- return results;
2376
- } else nodes = this[combinator](nodes);
2377
- if (tagName == "*") return nodes;
2378
- }
2379
- for (var i = 0, node; node = nodes[i]; i++)
2380
- if (node.tagName.toUpperCase() == tagName) results.push(node);
2381
- return results;
2382
- } else return root.getElementsByTagName(tagName);
2383
- },
2384
-
2385
- id: function(nodes, root, id, combinator) {
2386
- var targetNode = $(id), h = Selector.handlers;
2387
- if (!nodes && root == document) return targetNode ? [targetNode] : [];
2388
- if (nodes) {
2389
- if (combinator) {
2390
- if (combinator == 'child') {
2391
- for (var i = 0, node; node = nodes[i]; i++)
2392
- if (targetNode.parentNode == node) return [targetNode];
2393
- } else if (combinator == 'descendant') {
2394
- for (var i = 0, node; node = nodes[i]; i++)
2395
- if (Element.descendantOf(targetNode, node)) return [targetNode];
2396
- } else if (combinator == 'adjacent') {
2397
- for (var i = 0, node; node = nodes[i]; i++)
2398
- if (Selector.handlers.previousElementSibling(targetNode) == node)
2399
- return [targetNode];
2400
- } else nodes = h[combinator](nodes);
2401
- }
2402
- for (var i = 0, node; node = nodes[i]; i++)
2403
- if (node == targetNode) return [targetNode];
2404
- return [];
2405
- }
2406
- return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
2407
- },
2408
-
2409
- className: function(nodes, root, className, combinator) {
2410
- if (nodes && combinator) nodes = this[combinator](nodes);
2411
- return Selector.handlers.byClassName(nodes, root, className);
2412
- },
2413
-
2414
- byClassName: function(nodes, root, className) {
2415
- if (!nodes) nodes = Selector.handlers.descendant([root]);
2416
- var needle = ' ' + className + ' ';
2417
- for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
2418
- nodeClassName = node.className;
2419
- if (nodeClassName.length == 0) continue;
2420
- if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
2421
- results.push(node);
2422
- }
2423
- return results;
2424
- },
2425
-
2426
- attrPresence: function(nodes, root, attr) {
2427
- var results = [];
2428
- for (var i = 0, node; node = nodes[i]; i++)
2429
- if (Element.hasAttribute(node, attr)) results.push(node);
2430
- return results;
2431
- },
2432
-
2433
- attr: function(nodes, root, attr, value, operator) {
2434
- if (!nodes) nodes = root.getElementsByTagName("*");
2435
- var handler = Selector.operators[operator], results = [];
2436
- for (var i = 0, node; node = nodes[i]; i++) {
2437
- var nodeValue = Element.readAttribute(node, attr);
2438
- if (nodeValue === null) continue;
2439
- if (handler(nodeValue, value)) results.push(node);
2440
- }
2441
- return results;
2442
- },
2443
-
2444
- pseudo: function(nodes, name, value, root, combinator) {
2445
- if (nodes && combinator) nodes = this[combinator](nodes);
2446
- if (!nodes) nodes = root.getElementsByTagName("*");
2447
- return Selector.pseudos[name](nodes, value, root);
2448
- }
2449
- },
2450
-
2451
- pseudos: {
2452
- 'first-child': function(nodes, value, root) {
2453
- for (var i = 0, results = [], node; node = nodes[i]; i++) {
2454
- if (Selector.handlers.previousElementSibling(node)) continue;
2455
- results.push(node);
2456
- }
2457
- return results;
2458
- },
2459
- 'last-child': function(nodes, value, root) {
2460
- for (var i = 0, results = [], node; node = nodes[i]; i++) {
2461
- if (Selector.handlers.nextElementSibling(node)) continue;
2462
- results.push(node);
2463
- }
2464
- return results;
2465
- },
2466
- 'only-child': function(nodes, value, root) {
2467
- var h = Selector.handlers;
2468
- for (var i = 0, results = [], node; node = nodes[i]; i++)
2469
- if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
2470
- results.push(node);
2471
- return results;
2472
- },
2473
- 'nth-child': function(nodes, formula, root) {
2474
- return Selector.pseudos.nth(nodes, formula, root);
2475
- },
2476
- 'nth-last-child': function(nodes, formula, root) {
2477
- return Selector.pseudos.nth(nodes, formula, root, true);
2478
- },
2479
- 'nth-of-type': function(nodes, formula, root) {
2480
- return Selector.pseudos.nth(nodes, formula, root, false, true);
2481
- },
2482
- 'nth-last-of-type': function(nodes, formula, root) {
2483
- return Selector.pseudos.nth(nodes, formula, root, true, true);
2484
- },
2485
- 'first-of-type': function(nodes, formula, root) {
2486
- return Selector.pseudos.nth(nodes, "1", root, false, true);
2487
- },
2488
- 'last-of-type': function(nodes, formula, root) {
2489
- return Selector.pseudos.nth(nodes, "1", root, true, true);
2490
- },
2491
- 'only-of-type': function(nodes, formula, root) {
2492
- var p = Selector.pseudos;
2493
- return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
2494
- },
2495
-
2496
- // handles the an+b logic
2497
- getIndices: function(a, b, total) {
2498
- if (a == 0) return b > 0 ? [b] : [];
2499
- return $R(1, total).inject([], function(memo, i) {
2500
- if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
2501
- return memo;
2502
- });
2503
- },
2504
-
2505
- // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
2506
- nth: function(nodes, formula, root, reverse, ofType) {
2507
- if (nodes.length == 0) return [];
2508
- if (formula == 'even') formula = '2n+0';
2509
- if (formula == 'odd') formula = '2n+1';
2510
- var h = Selector.handlers, results = [], indexed = [], m;
2511
- h.mark(nodes);
2512
- for (var i = 0, node; node = nodes[i]; i++) {
2513
- if (!node.parentNode._counted) {
2514
- h.index(node.parentNode, reverse, ofType);
2515
- indexed.push(node.parentNode);
2516
- }
2517
- }
2518
- if (formula.match(/^\d+$/)) { // just a number
2519
- formula = Number(formula);
2520
- for (var i = 0, node; node = nodes[i]; i++)
2521
- if (node.nodeIndex == formula) results.push(node);
2522
- } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
2523
- if (m[1] == "-") m[1] = -1;
2524
- var a = m[1] ? Number(m[1]) : 1;
2525
- var b = m[2] ? Number(m[2]) : 0;
2526
- var indices = Selector.pseudos.getIndices(a, b, nodes.length);
2527
- for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
2528
- for (var j = 0; j < l; j++)
2529
- if (node.nodeIndex == indices[j]) results.push(node);
2530
- }
2531
- }
2532
- h.unmark(nodes);
2533
- h.unmark(indexed);
2534
- return results;
2535
- },
2536
-
2537
- 'empty': function(nodes, value, root) {
2538
- for (var i = 0, results = [], node; node = nodes[i]; i++) {
2539
- // IE treats comments as element nodes
2540
- if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
2541
- results.push(node);
2542
- }
2543
- return results;
2544
- },
2545
-
2546
- 'not': function(nodes, selector, root) {
2547
- var h = Selector.handlers, selectorType, m;
2548
- var exclusions = new Selector(selector).findElements(root);
2549
- h.mark(exclusions);
2550
- for (var i = 0, results = [], node; node = nodes[i]; i++)
2551
- if (!node._counted) results.push(node);
2552
- h.unmark(exclusions);
2553
- return results;
2554
- },
2555
-
2556
- 'enabled': function(nodes, value, root) {
2557
- for (var i = 0, results = [], node; node = nodes[i]; i++)
2558
- if (!node.disabled) results.push(node);
2559
- return results;
2560
- },
2561
-
2562
- 'disabled': 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
- 'checked': function(nodes, value, root) {
2569
- for (var i = 0, results = [], node; node = nodes[i]; i++)
2570
- if (node.checked) results.push(node);
2571
- return results;
2572
- }
2573
- },
2574
-
2575
- operators: {
2576
- '=': function(nv, v) { return nv == v; },
2577
- '!=': function(nv, v) { return nv != v; },
2578
- '^=': function(nv, v) { return nv.startsWith(v); },
2579
- '$=': function(nv, v) { return nv.endsWith(v); },
2580
- '*=': function(nv, v) { return nv.include(v); },
2581
- '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
2582
- '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
2583
- },
2584
-
2585
- matchElements: function(elements, expression) {
2586
- var matches = new Selector(expression).findElements(), h = Selector.handlers;
2587
- h.mark(matches);
2588
- for (var i = 0, results = [], element; element = elements[i]; i++)
2589
- if (element._counted) results.push(element);
2590
- h.unmark(matches);
2591
- return results;
2592
- },
2593
-
2594
- findElement: function(elements, expression, index) {
2595
- if (typeof expression == 'number') {
2596
- index = expression; expression = false;
2597
- }
2598
- return Selector.matchElements(elements, expression || '*')[index || 0];
2599
- },
2600
-
2601
- findChildElements: function(element, expressions) {
2602
- var exprs = expressions.join(','), expressions = [];
2603
- exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
2604
- expressions.push(m[1].strip());
2605
- });
2606
- var results = [], h = Selector.handlers;
2607
- for (var i = 0, l = expressions.length, selector; i < l; i++) {
2608
- selector = new Selector(expressions[i].strip());
2609
- h.concat(results, selector.findElements(element));
2610
- }
2611
- return (l > 1) ? h.unique(results) : results;
2612
- }
2613
- });
2614
-
2615
- function $$() {
2616
- return Selector.findChildElements(document, $A(arguments));
2617
- }
2618
- var Form = {
2619
- reset: function(form) {
2620
- $(form).reset();
2621
- return form;
2622
- },
2623
-
2624
- serializeElements: function(elements, getHash) {
2625
- var data = elements.inject({}, function(result, element) {
2626
- if (!element.disabled && element.name) {
2627
- var key = element.name, value = $(element).getValue();
2628
- if (value != null) {
2629
- if (key in result) {
2630
- if (result[key].constructor != Array) result[key] = [result[key]];
2631
- result[key].push(value);
2632
- }
2633
- else result[key] = value;
2634
- }
2635
- }
2636
- return result;
2637
- });
2638
-
2639
- return getHash ? data : Hash.toQueryString(data);
2640
- }
2641
- };
2642
-
2643
- Form.Methods = {
2644
- serialize: function(form, getHash) {
2645
- return Form.serializeElements(Form.getElements(form), getHash);
2646
- },
2647
-
2648
- getElements: function(form) {
2649
- return $A($(form).getElementsByTagName('*')).inject([],
2650
- function(elements, child) {
2651
- if (Form.Element.Serializers[child.tagName.toLowerCase()])
2652
- elements.push(Element.extend(child));
2653
- return elements;
2654
- }
2655
- );
2656
- },
2657
-
2658
- getInputs: function(form, typeName, name) {
2659
- form = $(form);
2660
- var inputs = form.getElementsByTagName('input');
2661
-
2662
- if (!typeName && !name) return $A(inputs).map(Element.extend);
2663
-
2664
- for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
2665
- var input = inputs[i];
2666
- if ((typeName && input.type != typeName) || (name && input.name != name))
2667
- continue;
2668
- matchingInputs.push(Element.extend(input));
2669
- }
2670
-
2671
- return matchingInputs;
2672
- },
2673
-
2674
- disable: function(form) {
2675
- form = $(form);
2676
- Form.getElements(form).invoke('disable');
2677
- return form;
2678
- },
2679
-
2680
- enable: function(form) {
2681
- form = $(form);
2682
- Form.getElements(form).invoke('enable');
2683
- return form;
2684
- },
2685
-
2686
- findFirstElement: function(form) {
2687
- return $(form).getElements().find(function(element) {
2688
- return element.type != 'hidden' && !element.disabled &&
2689
- ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
2690
- });
2691
- },
2692
-
2693
- focusFirstElement: function(form) {
2694
- form = $(form);
2695
- form.findFirstElement().activate();
2696
- return form;
2697
- },
2698
-
2699
- request: function(form, options) {
2700
- form = $(form), options = Object.clone(options || {});
2701
-
2702
- var params = options.parameters;
2703
- options.parameters = form.serialize(true);
2704
-
2705
- if (params) {
2706
- if (typeof params == 'string') params = params.toQueryParams();
2707
- Object.extend(options.parameters, params);
2708
- }
2709
-
2710
- if (form.hasAttribute('method') && !options.method)
2711
- options.method = form.method;
2712
-
2713
- return new Ajax.Request(form.readAttribute('action'), options);
2714
- }
2715
- }
2716
-
2717
- /*--------------------------------------------------------------------------*/
2718
-
2719
- Form.Element = {
2720
- focus: function(element) {
2721
- $(element).focus();
2722
- return element;
2723
- },
2724
-
2725
- select: function(element) {
2726
- $(element).select();
2727
- return element;
2728
- }
2729
- }
2730
-
2731
- Form.Element.Methods = {
2732
- serialize: function(element) {
2733
- element = $(element);
2734
- if (!element.disabled && element.name) {
2735
- var value = element.getValue();
2736
- if (value != undefined) {
2737
- var pair = {};
2738
- pair[element.name] = value;
2739
- return Hash.toQueryString(pair);
2740
- }
2741
- }
2742
- return '';
2743
- },
2744
-
2745
- getValue: function(element) {
2746
- element = $(element);
2747
- var method = element.tagName.toLowerCase();
2748
- return Form.Element.Serializers[method](element);
2749
- },
2750
-
2751
- clear: function(element) {
2752
- $(element).value = '';
2753
- return element;
2754
- },
2755
-
2756
- present: function(element) {
2757
- return $(element).value != '';
2758
- },
2759
-
2760
- activate: function(element) {
2761
- element = $(element);
2762
- try {
2763
- element.focus();
2764
- if (element.select && (element.tagName.toLowerCase() != 'input' ||
2765
- !['button', 'reset', 'submit'].include(element.type)))
2766
- element.select();
2767
- } catch (e) {}
2768
- return element;
2769
- },
2770
-
2771
- disable: function(element) {
2772
- element = $(element);
2773
- element.blur();
2774
- element.disabled = true;
2775
- return element;
2776
- },
2777
-
2778
- enable: function(element) {
2779
- element = $(element);
2780
- element.disabled = false;
2781
- return element;
2782
- }
2783
- }
2784
-
2785
- /*--------------------------------------------------------------------------*/
2786
-
2787
- var Field = Form.Element;
2788
- var $F = Form.Element.Methods.getValue;
2789
-
2790
- /*--------------------------------------------------------------------------*/
2791
-
2792
- Form.Element.Serializers = {
2793
- input: function(element) {
2794
- switch (element.type.toLowerCase()) {
2795
- case 'checkbox':
2796
- case 'radio':
2797
- return Form.Element.Serializers.inputSelector(element);
2798
- default:
2799
- return Form.Element.Serializers.textarea(element);
2800
- }
2801
- },
2802
-
2803
- inputSelector: function(element) {
2804
- return element.checked ? element.value : null;
2805
- },
2806
-
2807
- textarea: function(element) {
2808
- return element.value;
2809
- },
2810
-
2811
- select: function(element) {
2812
- return this[element.type == 'select-one' ?
2813
- 'selectOne' : 'selectMany'](element);
2814
- },
2815
-
2816
- selectOne: function(element) {
2817
- var index = element.selectedIndex;
2818
- return index >= 0 ? this.optionValue(element.options[index]) : null;
2819
- },
2820
-
2821
- selectMany: function(element) {
2822
- var values, length = element.length;
2823
- if (!length) return null;
2824
-
2825
- for (var i = 0, values = []; i < length; i++) {
2826
- var opt = element.options[i];
2827
- if (opt.selected) values.push(this.optionValue(opt));
2828
- }
2829
- return values;
2830
- },
2831
-
2832
- optionValue: function(opt) {
2833
- // extend element because hasAttribute may not be native
2834
- return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
2835
- }
2836
- }
2837
-
2838
- /*--------------------------------------------------------------------------*/
2839
-
2840
- Abstract.TimedObserver = function() {}
2841
- Abstract.TimedObserver.prototype = {
2842
- initialize: function(element, frequency, callback) {
2843
- this.frequency = frequency;
2844
- this.element = $(element);
2845
- this.callback = callback;
2846
-
2847
- this.lastValue = this.getValue();
2848
- this.registerCallback();
2849
- },
2850
-
2851
- registerCallback: function() {
2852
- setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
2853
- },
2854
-
2855
- onTimerEvent: function() {
2856
- var value = this.getValue();
2857
- var changed = ('string' == typeof this.lastValue && 'string' == typeof value
2858
- ? this.lastValue != value : String(this.lastValue) != String(value));
2859
- if (changed) {
2860
- this.callback(this.element, value);
2861
- this.lastValue = value;
2862
- }
2863
- }
2864
- }
2865
-
2866
- Form.Element.Observer = Class.create();
2867
- Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
2868
- getValue: function() {
2869
- return Form.Element.getValue(this.element);
2870
- }
2871
- });
2872
-
2873
- Form.Observer = Class.create();
2874
- Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
2875
- getValue: function() {
2876
- return Form.serialize(this.element);
2877
- }
2878
- });
2879
-
2880
- /*--------------------------------------------------------------------------*/
2881
-
2882
- Abstract.EventObserver = function() {}
2883
- Abstract.EventObserver.prototype = {
2884
- initialize: function(element, callback) {
2885
- this.element = $(element);
2886
- this.callback = callback;
2887
-
2888
- this.lastValue = this.getValue();
2889
- if (this.element.tagName.toLowerCase() == 'form')
2890
- this.registerFormCallbacks();
2891
- else
2892
- this.registerCallback(this.element);
2893
- },
2894
-
2895
- onElementEvent: function() {
2896
- var value = this.getValue();
2897
- if (this.lastValue != value) {
2898
- this.callback(this.element, value);
2899
- this.lastValue = value;
2900
- }
2901
- },
2902
-
2903
- registerFormCallbacks: function() {
2904
- Form.getElements(this.element).each(this.registerCallback.bind(this));
2905
- },
2906
-
2907
- registerCallback: function(element) {
2908
- if (element.type) {
2909
- switch (element.type.toLowerCase()) {
2910
- case 'checkbox':
2911
- case 'radio':
2912
- Event.observe(element, 'click', this.onElementEvent.bind(this));
2913
- break;
2914
- default:
2915
- Event.observe(element, 'change', this.onElementEvent.bind(this));
2916
- break;
2917
- }
2918
- }
2919
- }
2920
- }
2921
-
2922
- Form.Element.EventObserver = Class.create();
2923
- Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
2924
- getValue: function() {
2925
- return Form.Element.getValue(this.element);
2926
- }
2927
- });
2928
-
2929
- Form.EventObserver = Class.create();
2930
- Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
2931
- getValue: function() {
2932
- return Form.serialize(this.element);
2933
- }
2934
- });
2935
- if (!window.Event) {
2936
- var Event = new Object();
2937
- }
2938
-
2939
- Object.extend(Event, {
2940
- KEY_BACKSPACE: 8,
2941
- KEY_TAB: 9,
2942
- KEY_RETURN: 13,
2943
- KEY_ESC: 27,
2944
- KEY_LEFT: 37,
2945
- KEY_UP: 38,
2946
- KEY_RIGHT: 39,
2947
- KEY_DOWN: 40,
2948
- KEY_DELETE: 46,
2949
- KEY_HOME: 36,
2950
- KEY_END: 35,
2951
- KEY_PAGEUP: 33,
2952
- KEY_PAGEDOWN: 34,
2953
-
2954
- element: function(event) {
2955
- return $(event.target || event.srcElement);
2956
- },
2957
-
2958
- isLeftClick: function(event) {
2959
- return (((event.which) && (event.which == 1)) ||
2960
- ((event.button) && (event.button == 1)));
2961
- },
2962
-
2963
- pointerX: function(event) {
2964
- return event.pageX || (event.clientX +
2965
- (document.documentElement.scrollLeft || document.body.scrollLeft));
2966
- },
2967
-
2968
- pointerY: function(event) {
2969
- return event.pageY || (event.clientY +
2970
- (document.documentElement.scrollTop || document.body.scrollTop));
2971
- },
2972
-
2973
- stop: function(event) {
2974
- if (event.preventDefault) {
2975
- event.preventDefault();
2976
- event.stopPropagation();
2977
- } else {
2978
- event.returnValue = false;
2979
- event.cancelBubble = true;
2980
- }
2981
- },
2982
-
2983
- // find the first node with the given tagName, starting from the
2984
- // node the event was triggered on; traverses the DOM upwards
2985
- findElement: function(event, tagName) {
2986
- var element = Event.element(event);
2987
- while (element.parentNode && (!element.tagName ||
2988
- (element.tagName.toUpperCase() != tagName.toUpperCase())))
2989
- element = element.parentNode;
2990
- return element;
2991
- },
2992
-
2993
- observers: false,
2994
-
2995
- _observeAndCache: function(element, name, observer, useCapture) {
2996
- if (!this.observers) this.observers = [];
2997
- if (element.addEventListener) {
2998
- this.observers.push([element, name, observer, useCapture]);
2999
- element.addEventListener(name, observer, useCapture);
3000
- } else if (element.attachEvent) {
3001
- this.observers.push([element, name, observer, useCapture]);
3002
- element.attachEvent('on' + name, observer);
3003
- }
3004
- },
3005
-
3006
- unloadCache: function() {
3007
- if (!Event.observers) return;
3008
- for (var i = 0, length = Event.observers.length; i < length; i++) {
3009
- Event.stopObserving.apply(this, Event.observers[i]);
3010
- Event.observers[i][0] = null;
3011
- }
3012
- Event.observers = false;
3013
- },
3014
-
3015
- observe: function(element, name, observer, useCapture) {
3016
- element = $(element);
3017
- useCapture = useCapture || false;
3018
-
3019
- if (name == 'keypress' &&
3020
- (Prototype.Browser.WebKit || element.attachEvent))
3021
- name = 'keydown';
3022
-
3023
- Event._observeAndCache(element, name, observer, useCapture);
3024
- },
3025
-
3026
- stopObserving: function(element, name, observer, useCapture) {
3027
- element = $(element);
3028
- useCapture = useCapture || false;
3029
-
3030
- if (name == 'keypress' &&
3031
- (Prototype.Browser.WebKit || element.attachEvent))
3032
- name = 'keydown';
3033
-
3034
- if (element.removeEventListener) {
3035
- element.removeEventListener(name, observer, useCapture);
3036
- } else if (element.detachEvent) {
3037
- try {
3038
- element.detachEvent('on' + name, observer);
3039
- } catch (e) {}
3040
- }
3041
- }
3042
- });
3043
-
3044
- /* prevent memory leaks in IE */
3045
- if (Prototype.Browser.IE)
3046
- Event.observe(window, 'unload', Event.unloadCache, false);
3047
- var Position = {
3048
- // set to true if needed, warning: firefox performance problems
3049
- // NOT neeeded for page scrolling, only if draggable contained in
3050
- // scrollable elements
3051
- includeScrollOffsets: false,
3052
-
3053
- // must be called before calling withinIncludingScrolloffset, every time the
3054
- // page is scrolled
3055
- prepare: function() {
3056
- this.deltaX = window.pageXOffset
3057
- || document.documentElement.scrollLeft
3058
- || document.body.scrollLeft
3059
- || 0;
3060
- this.deltaY = window.pageYOffset
3061
- || document.documentElement.scrollTop
3062
- || document.body.scrollTop
3063
- || 0;
3064
- },
3065
-
3066
- realOffset: function(element) {
3067
- var valueT = 0, valueL = 0;
3068
- do {
3069
- valueT += element.scrollTop || 0;
3070
- valueL += element.scrollLeft || 0;
3071
- element = element.parentNode;
3072
- } while (element);
3073
- return [valueL, valueT];
3074
- },
3075
-
3076
- cumulativeOffset: function(element) {
3077
- var valueT = 0, valueL = 0;
3078
- do {
3079
- valueT += element.offsetTop || 0;
3080
- valueL += element.offsetLeft || 0;
3081
- element = element.offsetParent;
3082
- } while (element);
3083
- return [valueL, valueT];
3084
- },
3085
-
3086
- positionedOffset: function(element) {
3087
- var valueT = 0, valueL = 0;
3088
- do {
3089
- valueT += element.offsetTop || 0;
3090
- valueL += element.offsetLeft || 0;
3091
- element = element.offsetParent;
3092
- if (element) {
3093
- if(element.tagName=='BODY') break;
3094
- var p = Element.getStyle(element, 'position');
3095
- if (p == 'relative' || p == 'absolute') break;
3096
- }
3097
- } while (element);
3098
- return [valueL, valueT];
3099
- },
3100
-
3101
- offsetParent: function(element) {
3102
- if (element.offsetParent) return element.offsetParent;
3103
- if (element == document.body) return element;
3104
-
3105
- while ((element = element.parentNode) && element != document.body)
3106
- if (Element.getStyle(element, 'position') != 'static')
3107
- return element;
3108
-
3109
- return document.body;
3110
- },
3111
-
3112
- // caches x/y coordinate pair to use with overlap
3113
- within: function(element, x, y) {
3114
- if (this.includeScrollOffsets)
3115
- return this.withinIncludingScrolloffsets(element, x, y);
3116
- this.xcomp = x;
3117
- this.ycomp = y;
3118
- this.offset = this.cumulativeOffset(element);
3119
-
3120
- return (y >= this.offset[1] &&
3121
- y < this.offset[1] + element.offsetHeight &&
3122
- x >= this.offset[0] &&
3123
- x < this.offset[0] + element.offsetWidth);
3124
- },
3125
-
3126
- withinIncludingScrolloffsets: function(element, x, y) {
3127
- var offsetcache = this.realOffset(element);
3128
-
3129
- this.xcomp = x + offsetcache[0] - this.deltaX;
3130
- this.ycomp = y + offsetcache[1] - this.deltaY;
3131
- this.offset = this.cumulativeOffset(element);
3132
-
3133
- return (this.ycomp >= this.offset[1] &&
3134
- this.ycomp < this.offset[1] + element.offsetHeight &&
3135
- this.xcomp >= this.offset[0] &&
3136
- this.xcomp < this.offset[0] + element.offsetWidth);
3137
- },
3138
-
3139
- // within must be called directly before
3140
- overlap: function(mode, element) {
3141
- if (!mode) return 0;
3142
- if (mode == 'vertical')
3143
- return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
3144
- element.offsetHeight;
3145
- if (mode == 'horizontal')
3146
- return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
3147
- element.offsetWidth;
3148
- },
3149
-
3150
- page: function(forElement) {
3151
- var valueT = 0, valueL = 0;
3152
-
3153
- var element = forElement;
3154
- do {
3155
- valueT += element.offsetTop || 0;
3156
- valueL += element.offsetLeft || 0;
3157
-
3158
- // Safari fix
3159
- if (element.offsetParent == document.body)
3160
- if (Element.getStyle(element,'position')=='absolute') break;
3161
-
3162
- } while (element = element.offsetParent);
3163
-
3164
- element = forElement;
3165
- do {
3166
- if (!window.opera || element.tagName=='BODY') {
3167
- valueT -= element.scrollTop || 0;
3168
- valueL -= element.scrollLeft || 0;
3169
- }
3170
- } while (element = element.parentNode);
3171
-
3172
- return [valueL, valueT];
3173
- },
3174
-
3175
- clone: function(source, target) {
3176
- var options = Object.extend({
3177
- setLeft: true,
3178
- setTop: true,
3179
- setWidth: true,
3180
- setHeight: true,
3181
- offsetTop: 0,
3182
- offsetLeft: 0
3183
- }, arguments[2] || {})
3184
-
3185
- // find page position of source
3186
- source = $(source);
3187
- var p = Position.page(source);
3188
-
3189
- // find coordinate system to use
3190
- target = $(target);
3191
- var delta = [0, 0];
3192
- var parent = null;
3193
- // delta [0,0] will do fine with position: fixed elements,
3194
- // position:absolute needs offsetParent deltas
3195
- if (Element.getStyle(target,'position') == 'absolute') {
3196
- parent = Position.offsetParent(target);
3197
- delta = Position.page(parent);
3198
- }
3199
-
3200
- // correct by body offsets (fixes Safari)
3201
- if (parent == document.body) {
3202
- delta[0] -= document.body.offsetLeft;
3203
- delta[1] -= document.body.offsetTop;
3204
- }
3205
-
3206
- // set position
3207
- if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
3208
- if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
3209
- if(options.setWidth) target.style.width = source.offsetWidth + 'px';
3210
- if(options.setHeight) target.style.height = source.offsetHeight + 'px';
3211
- },
3212
-
3213
- absolutize: function(element) {
3214
- element = $(element);
3215
- if (element.style.position == 'absolute') return;
3216
- Position.prepare();
3217
-
3218
- var offsets = Position.positionedOffset(element);
3219
- var top = offsets[1];
3220
- var left = offsets[0];
3221
- var width = element.clientWidth;
3222
- var height = element.clientHeight;
3223
-
3224
- element._originalLeft = left - parseFloat(element.style.left || 0);
3225
- element._originalTop = top - parseFloat(element.style.top || 0);
3226
- element._originalWidth = element.style.width;
3227
- element._originalHeight = element.style.height;
3228
-
3229
- element.style.position = 'absolute';
3230
- element.style.top = top + 'px';
3231
- element.style.left = left + 'px';
3232
- element.style.width = width + 'px';
3233
- element.style.height = height + 'px';
3234
- },
3235
-
3236
- relativize: function(element) {
3237
- element = $(element);
3238
- if (element.style.position == 'relative') return;
3239
- Position.prepare();
3240
-
3241
- element.style.position = 'relative';
3242
- var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
3243
- var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
3244
-
3245
- element.style.top = top + 'px';
3246
- element.style.left = left + 'px';
3247
- element.style.height = element._originalHeight;
3248
- element.style.width = element._originalWidth;
3249
- }
3250
- }
3251
-
3252
- // Safari returns margins on body which is incorrect if the child is absolutely
3253
- // positioned. For performance reasons, redefine Position.cumulativeOffset for
3254
- // KHTML/WebKit only.
3255
- if (Prototype.Browser.WebKit) {
3256
- Position.cumulativeOffset = function(element) {
3257
- var valueT = 0, valueL = 0;
3258
- do {
3259
- valueT += element.offsetTop || 0;
3260
- valueL += element.offsetLeft || 0;
3261
- if (element.offsetParent == document.body)
3262
- if (Element.getStyle(element, 'position') == 'absolute') break;
3263
-
3264
- element = element.offsetParent;
3265
- } while (element);
3266
-
3267
- return [valueL, valueT];
3268
- }
3269
- }
3270
-
3271
- Element.addMethods();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
js/dev/scriptaculous.js DELETED
@@ -1,58 +0,0 @@
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();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
js/lib.js CHANGED
@@ -1,207 +1,25 @@
1
- var Prototype={Version:"1.5.1",Browser:{IE:!!(window.attachEvent&&!window.opera),Opera:!!window.opera,WebKit:navigator.userAgent.indexOf("AppleWebKit/")>-1,Gecko:navigator.userAgent.indexOf("Gecko")>-1&&navigator.userAgent.indexOf("KHTML")==-1},BrowserFeatures:{XPath:!!document.evaluate,ElementExtensions:!!window.HTMLElement,SpecificElementExtensions:document.createElement("div").__proto__!==document.createElement("form").__proto__},ScriptFragment:"<script[^>]*>([\u0001-\uffff]*?)<\/script>",JSONFilter:/^\/\*-secure-\s*(.*)\s*\*\/\s*$/,
2
- emptyFunction:function(){},K:function(a){return a}},Class={create:function(){return function(){this.initialize.apply(this,arguments)}}},Abstract={};Object.extend=function(a,b){for(var c in b)a[c]=b[c];return a};
3
- Object.extend(Object,{inspect:function(a){try{if(a===undefined)return"undefined";if(a===null)return"null";return a.inspect?a.inspect():a.toString()}catch(b){if(b instanceof RangeError)return"...";throw b;}},toJSON:function(a){switch(typeof a){case "undefined":case "function":case "unknown":return;case "boolean":return a.toString()}if(a===null)return"null";if(a.toJSON)return a.toJSON();if(a.ownerDocument!==document){var b=[];for(var c in a){var d=Object.toJSON(a[c]);d!==undefined&&b.push(c.toJSON()+
4
- ": "+d)}return"{"+b.join(", ")+"}"}},keys:function(a){var b=[];for(var c in a)b.push(c);return b},values:function(a){var b=[];for(var c in a)b.push(a[c]);return b},clone:function(a){return Object.extend({},a)}});Function.prototype.bind=function(){var a=this,b=$A(arguments),c=b.shift();return function(){return a.apply(c,b.concat($A(arguments)))}};Function.prototype.bindAsEventListener=function(a){var b=this,c=$A(arguments);a=c.shift();return function(d){return b.apply(a,[d||window.event].concat(c))}};
5
- Object.extend(Number.prototype,{toColorPart:function(){return this.toPaddedString(2,16)},succ:function(){return this+1},times:function(a){$R(0,this,true).each(a);return this},toPaddedString:function(a,b){var c=this.toString(b||10);return"0".times(a-c.length)+c},toJSON:function(){return isFinite(this)?this.toString():"null"}});
6
- Date.prototype.toJSON=function(){return'"'+this.getFullYear()+"-"+(this.getMonth()+1).toPaddedString(2)+"-"+this.getDate().toPaddedString(2)+"T"+this.getHours().toPaddedString(2)+":"+this.getMinutes().toPaddedString(2)+":"+this.getSeconds().toPaddedString(2)+'"'};var Try={these:function(){for(var a,b=0,c=arguments.length;b<c;b++){var d=arguments[b];try{a=d();break}catch(e){}}return a}},PeriodicalExecuter=Class.create();
7
- PeriodicalExecuter.prototype={initialize:function(a,b){this.callback=a;this.frequency=b;this.currentlyExecuting=false;this.registerCallback()},registerCallback:function(){this.timer=setInterval(this.onTimerEvent.bind(this),this.frequency*1E3)},stop:function(){if(this.timer){clearInterval(this.timer);this.timer=null}},onTimerEvent:function(){if(!this.currentlyExecuting)try{this.currentlyExecuting=true;this.callback(this)}finally{this.currentlyExecuting=false}}};
8
- Object.extend(String,{interpret:function(a){return a==null?"":String(a)},specialChar:{"\u0008":"\\b","\t":"\\t","\n":"\\n","\u000c":"\\f","\r":"\\r","\\":"\\\\"}});
9
- Object.extend(String.prototype,{gsub:function(a,b){var c="",d=this,e;for(b=arguments.callee.prepareReplacement(b);d.length>0;)if(e=d.match(a)){c+=d.slice(0,e.index);c+=String.interpret(b(e));d=d.slice(e.index+e[0].length)}else{c+=d;d=""}return c},sub:function(a,b,c){b=this.gsub.prepareReplacement(b);c=c===undefined?1:c;return this.gsub(a,function(d){if(--c<0)return d[0];return b(d)})},scan:function(a,b){this.gsub(a,b);return this},truncate:function(a,b){a=a||30;b=b===undefined?"...":b;return this.length>
10
- a?this.slice(0,a-b.length)+b:this},strip:function(){return this.replace(/^\s+/,"").replace(/\s+$/,"")},stripTags:function(){return this.replace(/<\/?[^>]+>/gi,"")},stripScripts:function(){return this.replace(new RegExp(Prototype.ScriptFragment,"img"),"")},extractScripts:function(){var a=new RegExp(Prototype.ScriptFragment,"img"),b=new RegExp(Prototype.ScriptFragment,"im");return(this.match(a)||[]).map(function(c){return(c.match(b)||["",""])[1]})},evalScripts:function(){return this.extractScripts().map(function(a){return eval(a)})},
11
- escapeHTML:function(){var a=arguments.callee;a.text.data=this;return a.div.innerHTML},unescapeHTML:function(){var a=document.createElement("div");a.innerHTML=this.stripTags();return a.childNodes[0]?a.childNodes.length>1?$A(a.childNodes).inject("",function(b,c){return b+c.nodeValue}):a.childNodes[0].nodeValue:""},toQueryParams:function(a){var b=this.strip().match(/([^?#]*)(#.*)?$/);if(!b)return{};return b[1].split(a||"&").inject({},function(c,d){if((d=d.split("="))[0]){var e=decodeURIComponent(d.shift()),
12
- f=d.length>1?d.join("="):d[0];if(f!=undefined)f=decodeURIComponent(f);if(e in c){if(c[e].constructor!=Array)c[e]=[c[e]];c[e].push(f)}else c[e]=f}return c})},toArray:function(){return this.split("")},succ:function(){return this.slice(0,this.length-1)+String.fromCharCode(this.charCodeAt(this.length-1)+1)},times:function(a){for(var b="",c=0;c<a;c++)b+=this;return b},camelize:function(){var a=this.split("-"),b=a.length;if(b==1)return a[0];for(var c=this.charAt(0)=="-"?a[0].charAt(0).toUpperCase()+a[0].substring(1):
13
- a[0],d=1;d<b;d++)c+=a[d].charAt(0).toUpperCase()+a[d].substring(1);return c},capitalize:function(){return this.charAt(0).toUpperCase()+this.substring(1).toLowerCase()},underscore:function(){return this.gsub(/::/,"/").gsub(/([A-Z]+)([A-Z][a-z])/,"#{1}_#{2}").gsub(/([a-z\d])([A-Z])/,"#{1}_#{2}").gsub(/-/,"_").toLowerCase()},dasherize:function(){return this.gsub(/_/,"-")},inspect:function(a){var b=this.gsub(/[\x00-\x1f\\]/,function(c){var d=String.specialChar[c[0]];return d?d:"\\u00"+c[0].charCodeAt().toPaddedString(2,
14
- 16)});if(a)return'"'+b.replace(/"/g,'\\"')+'"';return"'"+b.replace(/'/g,"\\'")+"'"},toJSON:function(){return this.inspect(true)},unfilterJSON:function(a){return this.sub(a||Prototype.JSONFilter,"#{1}")},evalJSON:function(a){var b=this.unfilterJSON();try{if(!a||/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(b))return eval("("+b+")")}catch(c){}throw new SyntaxError("Badly formed JSON string: "+this.inspect());},include:function(a){return this.indexOf(a)>-1},startsWith:function(a){return this.indexOf(a)===
15
- 0},endsWith:function(a){var b=this.length-a.length;return b>=0&&this.lastIndexOf(a)===b},empty:function(){return this==""},blank:function(){return/^\s*$/.test(this)}});if(Prototype.Browser.WebKit||Prototype.Browser.IE)Object.extend(String.prototype,{escapeHTML:function(){return this.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")},unescapeHTML:function(){return this.replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/g,">")}});
16
- String.prototype.gsub.prepareReplacement=function(a){if(typeof a=="function")return a;var b=new Template(a);return function(c){return b.evaluate(c)}};String.prototype.parseQuery=String.prototype.toQueryParams;Object.extend(String.prototype.escapeHTML,{div:document.createElement("div"),text:document.createTextNode("")});with(String.prototype.escapeHTML)div.appendChild(text);var Template=Class.create();Template.Pattern=/(^|.|\r|\n)(#\{(.*?)\})/;
17
- Template.prototype={initialize:function(a,b){this.template=a.toString();this.pattern=b||Template.Pattern},evaluate:function(a){return this.template.gsub(this.pattern,function(b){var c=b[1];if(c=="\\")return b[2];return c+String.interpret(a[b[3]])})}};
18
- var $break={},$continue=new Error('"throw $continue" is deprecated, use "return" instead'),Enumerable={each:function(a){var b=0;try{this._each(function(d){a(d,b++)})}catch(c){if(c!=$break)throw c;}return this},eachSlice:function(a,b){for(var c=-a,d=[],e=this.toArray();(c+=a)<e.length;)d.push(e.slice(c,c+a));return d.map(b)},all:function(a){var b=true;this.each(function(c,d){b=b&&!!(a||Prototype.K)(c,d);if(!b)throw $break;});return b},any:function(a){var b=false;this.each(function(c,d){if(b=!!(a||
19
- Prototype.K)(c,d))throw $break;});return b},collect:function(a){var b=[];this.each(function(c,d){b.push((a||Prototype.K)(c,d))});return b},detect:function(a){var b;this.each(function(c,d){if(a(c,d)){b=c;throw $break;}});return b},findAll:function(a){var b=[];this.each(function(c,d){a(c,d)&&b.push(c)});return b},grep:function(a,b){var c=[];this.each(function(d,e){if(d.toString().match(a))c.push((b||Prototype.K)(d,e))});return c},include:function(a){var b=false;this.each(function(c){if(c==a){b=true;
20
- throw $break;}});return b},inGroupsOf:function(a,b){b=b===undefined?null:b;return this.eachSlice(a,function(c){for(;c.length<a;)c.push(b);return c})},inject:function(a,b){this.each(function(c,d){a=b(a,c,d)});return a},invoke:function(a){var b=$A(arguments).slice(1);return this.map(function(c){return c[a].apply(c,b)})},max:function(a){var b;this.each(function(c,d){c=(a||Prototype.K)(c,d);if(b==undefined||c>=b)b=c});return b},min:function(a){var b;this.each(function(c,d){c=(a||Prototype.K)(c,d);if(b==
21
- undefined||c<b)b=c});return b},partition:function(a){var b=[],c=[];this.each(function(d,e){((a||Prototype.K)(d,e)?b:c).push(d)});return[b,c]},pluck:function(a){var b=[];this.each(function(c){b.push(c[a])});return b},reject:function(a){var b=[];this.each(function(c,d){a(c,d)||b.push(c)});return b},sortBy:function(a){return this.map(function(b,c){return{value:b,criteria:a(b,c)}}).sort(function(b,c){var d=b.criteria,e=c.criteria;return d<e?-1:d>e?1:0}).pluck("value")},toArray:function(){return this.map()},
22
- zip:function(){var a=Prototype.K,b=$A(arguments);if(typeof b.last()=="function")a=b.pop();var c=[this].concat(b).map($A);return this.map(function(d,e){return a(c.pluck(e))})},size:function(){return this.toArray().length},inspect:function(){return"#<Enumerable:"+this.toArray().inspect()+">"}};Object.extend(Enumerable,{map:Enumerable.collect,find:Enumerable.detect,select:Enumerable.findAll,member:Enumerable.include,entries:Enumerable.toArray});
23
- var $A=Array.from=function(a){if(!a)return[];if(a.toArray)return a.toArray();else{for(var b=[],c=0,d=a.length;c<d;c++)b.push(a[c]);return b}};if(Prototype.Browser.WebKit)$A=Array.from=function(a){if(!a)return[];if(!(typeof a=="function"&&a=="[object NodeList]")&&a.toArray)return a.toArray();else{for(var b=[],c=0,d=a.length;c<d;c++)b.push(a[c]);return b}};Object.extend(Array.prototype,Enumerable);if(!Array.prototype._reverse)Array.prototype._reverse=Array.prototype.reverse;
24
- Object.extend(Array.prototype,{_each:function(a){for(var b=0,c=this.length;b<c;b++)a(this[b])},clear:function(){this.length=0;return this},first:function(){return this[0]},last:function(){return this[this.length-1]},compact:function(){return this.select(function(a){return a!=null})},flatten:function(){return this.inject([],function(a,b){return a.concat(b&&b.constructor==Array?b.flatten():[b])})},without:function(){var a=$A(arguments);return this.select(function(b){return!a.include(b)})},indexOf:function(a){for(var b=
25
- 0,c=this.length;b<c;b++)if(this[b]==a)return b;return-1},reverse:function(a){return(a!==false?this:this.toArray())._reverse()},reduce:function(){return this.length>1?this:this[0]},uniq:function(a){return this.inject([],function(b,c,d){if(0==d||(a?b.last()!=c:!b.include(c)))b.push(c);return b})},clone:function(){return[].concat(this)},size:function(){return this.length},inspect:function(){return"["+this.map(Object.inspect).join(", ")+"]"},toJSON:function(){var a=[];this.each(function(b){b=Object.toJSON(b);
26
- b!==undefined&&a.push(b)});return"["+a.join(", ")+"]"}});Array.prototype.toArray=Array.prototype.clone;function $w(a){return(a=a.strip())?a.split(/\s+/):[]}if(Prototype.Browser.Opera)Array.prototype.concat=function(){for(var a=[],b=0,c=this.length;b<c;b++)a.push(this[b]);b=0;for(c=arguments.length;b<c;b++)if(arguments[b].constructor==Array)for(var d=0,e=arguments[b].length;d<e;d++)a.push(arguments[b][d]);else a.push(arguments[b]);return a};
27
- var Hash=function(a){a instanceof Hash?this.merge(a):Object.extend(this,a||{})};
28
- Object.extend(Hash,{toQueryString:function(a){var b=[];b.add=arguments.callee.addPair;this.prototype._each.call(a,function(c){if(c.key){var d=c.value;if(d&&typeof d=="object")d.constructor==Array&&d.each(function(e){b.add(c.key,e)});else b.add(c.key,d)}});return b.join("&")},toJSON:function(a){var b=[];this.prototype._each.call(a,function(c){var d=Object.toJSON(c.value);d!==undefined&&b.push(c.key.toJSON()+": "+d)});return"{"+b.join(", ")+"}"}});
29
- Hash.toQueryString.addPair=function(a,b){a=encodeURIComponent(a);b===undefined?this.push(a):this.push(a+"="+(b==null?"":encodeURIComponent(b)))};Object.extend(Hash.prototype,Enumerable);
30
- Object.extend(Hash.prototype,{_each:function(a){for(var b in this){var c=this[b];if(!(c&&c==Hash.prototype[b])){var d=[b,c];d.key=b;d.value=c;a(d)}}},keys:function(){return this.pluck("key")},values:function(){return this.pluck("value")},merge:function(a){return $H(a).inject(this,function(b,c){b[c.key]=c.value;return b})},remove:function(){for(var a,b=0,c=arguments.length;b<c;b++){var d=this[arguments[b]];if(d!==undefined)if(a===undefined)a=d;else{if(a.constructor!=Array)a=[a];a.push(d)}delete this[arguments[b]]}return a},
31
- toQueryString:function(){return Hash.toQueryString(this)},inspect:function(){return"#<Hash:{"+this.map(function(a){return a.map(Object.inspect).join(": ")}).join(", ")+"}>"},toJSON:function(){return Hash.toJSON(this)}});function $H(a){if(a instanceof Hash)return a;return new Hash(a)}
32
- if(function(){var a=0,b=function(d){this.key=d};b.prototype.key="foo";for(var c in new b("bar"))a++;return a>1}())Hash.prototype._each=function(a){var b=[];for(var c in this){var d=this[c];if(!(d&&d==Hash.prototype[c]||b.include(c))){b.push(c);var e=[c,d];e.key=c;e.value=d;a(e)}}};ObjectRange=Class.create();Object.extend(ObjectRange.prototype,Enumerable);
33
- Object.extend(ObjectRange.prototype,{initialize:function(a,b,c){this.start=a;this.end=b;this.exclusive=c},_each:function(a){for(var b=this.start;this.include(b);){a(b);b=b.succ()}},include:function(a){if(a<this.start)return false;if(this.exclusive)return a<this.end;return a<=this.end}});
34
- var $R=function(a,b,c){return new ObjectRange(a,b,c)},Ajax={getTransport:function(){return Try.these(function(){return new XMLHttpRequest},function(){return new ActiveXObject("Msxml2.XMLHTTP")},function(){return new ActiveXObject("Microsoft.XMLHTTP")})||false},activeRequestCount:0};
35
- Ajax.Responders={responders:[],_each:function(a){this.responders._each(a)},register:function(a){this.include(a)||this.responders.push(a)},unregister:function(a){this.responders=this.responders.without(a)},dispatch:function(a,b,c,d){this.each(function(e){if(typeof e[a]=="function")try{e[a].apply(e,[b,c,d])}catch(f){}})}};Object.extend(Ajax.Responders,Enumerable);Ajax.Responders.register({onCreate:function(){Ajax.activeRequestCount++},onComplete:function(){Ajax.activeRequestCount--}});Ajax.Base=function(){};
36
- Ajax.Base.prototype={setOptions:function(a){this.options={method:"post",asynchronous:true,contentType:"application/x-www-form-urlencoded",encoding:"UTF-8",parameters:""};Object.extend(this.options,a||{});this.options.method=this.options.method.toLowerCase();if(typeof this.options.parameters=="string")this.options.parameters=this.options.parameters.toQueryParams()}};Ajax.Request=Class.create();Ajax.Request.Events=["Uninitialized","Loading","Loaded","Interactive","Complete"];
37
- Ajax.Request.prototype=Object.extend(new Ajax.Base,{_complete:false,initialize:function(a,b){this.transport=Ajax.getTransport();this.setOptions(b);this.request(a)},request:function(a){this.url=a;this.method=this.options.method;a=Object.clone(this.options.parameters);if(!["get","post"].include(this.method)){a._method=this.method;this.method="post"}this.parameters=a;if(a=Hash.toQueryString(a))if(this.method=="get")this.url+=(this.url.include("?")?"&":"?")+a;else if(/Konqueror|Safari|KHTML/.test(navigator.userAgent))a+=
38
- "&_=";try{this.options.onCreate&&this.options.onCreate(this.transport);Ajax.Responders.dispatch("onCreate",this,this.transport);this.transport.open(this.method.toUpperCase(),this.url,this.options.asynchronous);this.options.asynchronous&&setTimeout(function(){this.respondToReadyState(1)}.bind(this),10);this.transport.onreadystatechange=this.onStateChange.bind(this);this.setRequestHeaders();this.body=this.method=="post"?this.options.postBody||a:null;this.transport.send(this.body);!this.options.asynchronous&&
39
- this.transport.overrideMimeType&&this.onStateChange()}catch(b){this.dispatchException(b)}},onStateChange:function(){var a=this.transport.readyState;a>1&&!(a==4&&this._complete)&&this.respondToReadyState(this.transport.readyState)},setRequestHeaders:function(){var a={"X-Requested-With":"XMLHttpRequest","X-Prototype-Version":Prototype.Version,Accept:"text/javascript, text/html, application/xml, text/xml, */*"};if(this.method=="post"){a["Content-type"]=this.options.contentType+(this.options.encoding?
40
- "; charset="+this.options.encoding:"");if(this.transport.overrideMimeType&&(navigator.userAgent.match(/Gecko\/(\d{4})/)||[0,2005])[1]<2005)a.Connection="close"}if(typeof this.options.requestHeaders=="object"){var b=this.options.requestHeaders;if(typeof b.push=="function")for(var c=0,d=b.length;c<d;c+=2)a[b[c]]=b[c+1];else $H(b).each(function(f){a[f.key]=f.value})}for(var e in a)this.transport.setRequestHeader(e,a[e])},success:function(){return!this.transport.status||this.transport.status>=200&&this.transport.status<
41
- 300},respondToReadyState:function(a){a=Ajax.Request.Events[a];var b=this.transport,c=this.evalJSON();if(a=="Complete"){try{this._complete=true;(this.options["on"+this.transport.status]||this.options["on"+(this.success()?"Success":"Failure")]||Prototype.emptyFunction)(b,c)}catch(d){this.dispatchException(d)}var e=this.getHeader("Content-type");e&&e.strip().match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i)&&this.evalResponse()}try{(this.options["on"+a]||Prototype.emptyFunction)(b,c);Ajax.Responders.dispatch("on"+
42
- a,this,b,c)}catch(f){this.dispatchException(f)}if(a=="Complete")this.transport.onreadystatechange=Prototype.emptyFunction},getHeader:function(a){try{return this.transport.getResponseHeader(a)}catch(b){return null}},evalJSON:function(){try{var a=this.getHeader("X-JSON");return a?a.evalJSON():null}catch(b){return null}},evalResponse:function(){try{return eval((this.transport.responseText||"").unfilterJSON())}catch(a){this.dispatchException(a)}},dispatchException:function(a){(this.options.onException||
43
- Prototype.emptyFunction)(this,a);Ajax.Responders.dispatch("onException",this,a)}});Ajax.Updater=Class.create();
44
- Object.extend(Object.extend(Ajax.Updater.prototype,Ajax.Request.prototype),{initialize:function(a,b,c){this.container={success:a.success||a,failure:a.failure||(a.success?null:a)};this.transport=Ajax.getTransport();this.setOptions(c);var d=this.options.onComplete||Prototype.emptyFunction;this.options.onComplete=function(e,f){this.updateContent();d(e,f)}.bind(this);this.request(b)},updateContent:function(){var a=this.container[this.success()?"success":"failure"],b=this.transport.responseText;this.options.evalScripts||
45
- (b=b.stripScripts());if(a=$(a))if(this.options.insertion)new this.options.insertion(a,b);else a.update(b);this.success()&&this.onComplete&&setTimeout(this.onComplete.bind(this),10)}});Ajax.PeriodicalUpdater=Class.create();
46
- Ajax.PeriodicalUpdater.prototype=Object.extend(new Ajax.Base,{initialize:function(a,b,c){this.setOptions(c);this.onComplete=this.options.onComplete;this.frequency=this.options.frequency||2;this.decay=this.options.decay||1;this.updater={};this.container=a;this.url=b;this.start()},start:function(){this.options.onComplete=this.updateComplete.bind(this);this.onTimerEvent()},stop:function(){this.updater.options.onComplete=undefined;clearTimeout(this.timer);(this.onComplete||Prototype.emptyFunction).apply(this,
47
- arguments)},updateComplete:function(a){if(this.options.decay){this.decay=a.responseText==this.lastText?this.decay*this.options.decay:1;this.lastText=a.responseText}this.timer=setTimeout(this.onTimerEvent.bind(this),this.decay*this.frequency*1E3)},onTimerEvent:function(){this.updater=new Ajax.Updater(this.container,this.url,this.options)}});
48
- function $(a){if(arguments.length>1){for(var b=0,c=[],d=arguments.length;b<d;b++)c.push($(arguments[b]));return c}if(typeof a=="string")a=document.getElementById(a);return Element.extend(a)}
49
- if(Prototype.BrowserFeatures.XPath){document._getElementsByXPath=function(a,b){for(var c=[],d=document.evaluate(a,$(b)||document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null),e=0,f=d.snapshotLength;e<f;e++)c.push(d.snapshotItem(e));return c};document.getElementsByClassName=function(a,b){return document._getElementsByXPath(".//*[contains(concat(' ', @class, ' '), ' "+a+" ')]",b)}}else document.getElementsByClassName=function(a,b){for(var c=($(b)||document.body).getElementsByTagName("*"),d=[],e,
50
- f=0,g=c.length;f<g;f++){e=c[f];Element.hasClassName(e,a)&&d.push(Element.extend(e))}return d};if(!window.Element)var Element={};
51
- Element.extend=function(a){var b=Prototype.BrowserFeatures;if(!a||!a.tagName||a.nodeType==3||a._extended||b.SpecificElementExtensions||a==window)return a;var c={},d=a.tagName,e=Element.extend.cache,f=Element.Methods.ByTag;if(!b.ElementExtensions){Object.extend(c,Element.Methods);Object.extend(c,Element.Methods.Simulated)}f[d]&&Object.extend(c,f[d]);for(var g in c){b=c[g];if(typeof b=="function"&&!(g in a))a[g]=e.findOrStore(b)}a._extended=Prototype.emptyFunction;return a};
52
- Element.extend.cache={findOrStore:function(a){return this[a]=this[a]||function(){return a.apply(null,[this].concat($A(arguments)))}}};
53
- Element.Methods={visible:function(a){return $(a).style.display!="none"},toggle:function(a){a=$(a);Element[Element.visible(a)?"hide":"show"](a);return a},hide:function(a){$(a).style.display="none";return a},show:function(a){$(a).style.display="";return a},remove:function(a){a=$(a);a.parentNode.removeChild(a);return a},update:function(a,b){b=typeof b=="undefined"?"":b.toString();$(a).innerHTML=b.stripScripts();setTimeout(function(){b.evalScripts()},10);return a},replace:function(a,b){a=$(a);b=typeof b==
54
- "undefined"?"":b.toString();if(a.outerHTML)a.outerHTML=b.stripScripts();else{var c=a.ownerDocument.createRange();c.selectNodeContents(a);a.parentNode.replaceChild(c.createContextualFragment(b.stripScripts()),a)}setTimeout(function(){b.evalScripts()},10);return a},inspect:function(a){a=$(a);var b="<"+a.tagName.toLowerCase();$H({id:"id",className:"class"}).each(function(c){var d=c.first();c=c.last();if(d=(a[d]||"").toString())b+=" "+c+"="+d.inspect(true)});return b+">"},recursivelyCollect:function(a,
55
- b){a=$(a);for(var c=[];a=a[b];)a.nodeType==1&&c.push(Element.extend(a));return c},ancestors:function(a){return $(a).recursivelyCollect("parentNode")},descendants:function(a){return $A($(a).getElementsByTagName("*")).each(Element.extend)},firstDescendant:function(a){for(a=$(a).firstChild;a&&a.nodeType!=1;)a=a.nextSibling;return $(a)},immediateDescendants:function(a){if(!(a=$(a).firstChild))return[];for(;a&&a.nodeType!=1;)a=a.nextSibling;if(a)return[a].concat($(a).nextSiblings());return[]},previousSiblings:function(a){return $(a).recursivelyCollect("previousSibling")},
56
- nextSiblings:function(a){return $(a).recursivelyCollect("nextSibling")},siblings:function(a){a=$(a);return a.previousSiblings().reverse().concat(a.nextSiblings())},match:function(a,b){if(typeof b=="string")b=new Selector(b);return b.match($(a))},up:function(a,b,c){a=$(a);if(arguments.length==1)return $(a.parentNode);var d=a.ancestors();return b?Selector.findElement(d,b,c):d[c||0]},down:function(a,b,c){a=$(a);if(arguments.length==1)return a.firstDescendant();var d=a.descendants();return b?Selector.findElement(d,
57
- b,c):d[c||0]},previous:function(a,b,c){a=$(a);if(arguments.length==1)return $(Selector.handlers.previousElementSibling(a));var d=a.previousSiblings();return b?Selector.findElement(d,b,c):d[c||0]},next:function(a,b,c){a=$(a);if(arguments.length==1)return $(Selector.handlers.nextElementSibling(a));var d=a.nextSiblings();return b?Selector.findElement(d,b,c):d[c||0]},getElementsBySelector:function(){var a=$A(arguments),b=$(a.shift());return Selector.findChildElements(b,a)},getElementsByClassName:function(a,
58
- b){return document.getElementsByClassName(b,a)},readAttribute:function(a,b){a=$(a);if(Prototype.Browser.IE){if(!a.attributes)return null;var c=Element._attributeTranslations;if(c.values[b])return c.values[b](a,b);if(c.names[b])b=c.names[b];return(c=a.attributes[b])?c.nodeValue:null}return a.getAttribute(b)},getHeight:function(a){return $(a).getDimensions().height},getWidth:function(a){return $(a).getDimensions().width},classNames:function(a){return new Element.ClassNames(a)},hasClassName:function(a,
59
- b){if(a=$(a)){var c=a.className;if(c.length==0)return false;if(c==b||c.match(new RegExp("(^|\\s)"+b+"(\\s|$)")))return true;return false}},addClassName:function(a,b){if(a=$(a)){Element.classNames(a).add(b);return a}},removeClassName:function(a,b){if(a=$(a)){Element.classNames(a).remove(b);return a}},toggleClassName:function(a,b){if(a=$(a)){Element.classNames(a)[a.hasClassName(b)?"remove":"add"](b);return a}},observe:function(){Event.observe.apply(Event,arguments);return $A(arguments).first()},stopObserving:function(){Event.stopObserving.apply(Event,
60
- arguments);return $A(arguments).first()},cleanWhitespace:function(a){a=$(a);for(var b=a.firstChild;b;){var c=b.nextSibling;b.nodeType==3&&!/\S/.test(b.nodeValue)&&a.removeChild(b);b=c}return a},empty:function(a){return $(a).innerHTML.blank()},descendantOf:function(a,b){a=$(a);for(b=$(b);a=a.parentNode;)if(a==b)return true;return false},scrollTo:function(a){a=$(a);var b=Position.cumulativeOffset(a);window.scrollTo(b[0],b[1]);return a},getStyle:function(a,b){a=$(a);b=b=="float"?"cssFloat":b.camelize();
61
- var c=a.style[b];if(!c)c=(c=document.defaultView.getComputedStyle(a,null))?c[b]:null;if(b=="opacity")return c?parseFloat(c):1;return c=="auto"?null:c},getOpacity:function(a){return $(a).getStyle("opacity")},setStyle:function(a,b,c){a=$(a);var d=a.style;for(var e in b)if(e=="opacity")a.setOpacity(b[e]);else d[e=="float"||e=="cssFloat"?d.styleFloat===undefined?"cssFloat":"styleFloat":c?e:e.camelize()]=b[e];return a},setOpacity:function(a,b){a=$(a);a.style.opacity=b==1||b===""?"":b<1.0E-5?0:b;return a},
62
- getDimensions:function(a){a=$(a);var b=$(a).getStyle("display");if(b!="none"&&b!=null)return{width:a.offsetWidth,height:a.offsetHeight};b=a.style;var c=b.visibility,d=b.position,e=b.display;b.visibility="hidden";b.position="absolute";b.display="block";var f=a.clientWidth;a=a.clientHeight;b.display=e;b.position=d;b.visibility=c;return{width:f,height:a}},makePositioned:function(a){a=$(a);var b=Element.getStyle(a,"position");if(b=="static"||!b){a._madePositioned=true;a.style.position="relative";if(window.opera){a.style.top=
63
- 0;a.style.left=0}}return a},undoPositioned:function(a){a=$(a);if(a._madePositioned){a._madePositioned=undefined;a.style.position=a.style.top=a.style.left=a.style.bottom=a.style.right=""}return a},makeClipping:function(a){a=$(a);if(a._overflow)return a;a._overflow=a.style.overflow||"auto";if((Element.getStyle(a,"overflow")||"visible")!="hidden")a.style.overflow="hidden";return a},undoClipping:function(a){a=$(a);if(!a._overflow)return a;a.style.overflow=a._overflow=="auto"?"":a._overflow;a._overflow=
64
- null;return a}};Object.extend(Element.Methods,{childOf:Element.Methods.descendantOf,childElements:Element.Methods.immediateDescendants});
65
- if(Prototype.Browser.Opera){Element.Methods._getStyle=Element.Methods.getStyle;Element.Methods.getStyle=function(a,b){switch(b){case "left":case "top":case "right":case "bottom":if(Element._getStyle(a,"position")=="static")return null;default:return Element._getStyle(a,b)}}}else if(Prototype.Browser.IE){Element.Methods.getStyle=function(a,b){a=$(a);b=b=="float"||b=="cssFloat"?"styleFloat":b.camelize();var c=a.style[b];if(!c&&a.currentStyle)c=a.currentStyle[b];if(b=="opacity"){if(c=(a.getStyle("filter")||
66
- "").match(/alpha\(opacity=(.*)\)/))if(c[1])return parseFloat(c[1])/100;return 1}if(c=="auto"){if((b=="width"||b=="height")&&a.getStyle("display")!="none")return a["offset"+b.capitalize()]+"px";return null}return c};Element.Methods.setOpacity=function(a,b){a=$(a);var c=a.getStyle("filter"),d=a.style;if(b==1||b===""){d.filter=c.replace(/alpha\([^\)]*\)/gi,"");return a}else if(b<1.0E-5)b=0;d.filter=c.replace(/alpha\([^\)]*\)/gi,"")+"alpha(opacity="+b*100+")";return a};Element.Methods.update=function(a,
67
- b){a=$(a);b=typeof b=="undefined"?"":b.toString();var c=a.tagName.toUpperCase();if(["THEAD","TBODY","TR","TD"].include(c)){var d=document.createElement("div");switch(c){case "THEAD":case "TBODY":d.innerHTML="<table><tbody>"+b.stripScripts()+"</tbody></table>";depth=2;break;case "TR":d.innerHTML="<table><tbody><tr>"+b.stripScripts()+"</tr></tbody></table>";depth=3;break;case "TD":d.innerHTML="<table><tbody><tr><td>"+b.stripScripts()+"</td></tr></tbody></table>";depth=4}$A(a.childNodes).each(function(e){a.removeChild(e)});
68
- depth.times(function(){d=d.firstChild});$A(d.childNodes).each(function(e){a.appendChild(e)})}else a.innerHTML=b.stripScripts();setTimeout(function(){b.evalScripts()},10);return a}}else if(Prototype.Browser.Gecko)Element.Methods.setOpacity=function(a,b){a=$(a);a.style.opacity=b==1?0.999999:b===""?"":b<1.0E-5?0:b;return a};
69
- Element._attributeTranslations={names:{colspan:"colSpan",rowspan:"rowSpan",valign:"vAlign",datetime:"dateTime",accesskey:"accessKey",tabindex:"tabIndex",enctype:"encType",maxlength:"maxLength",readonly:"readOnly",longdesc:"longDesc"},values:{_getAttr:function(a,b){return a.getAttribute(b,2)},_flag:function(a,b){return $(a).hasAttribute(b)?b:null},style:function(a){return a.style.cssText.toLowerCase()},title:function(a){a=a.getAttributeNode("title");return a.specified?a.nodeValue:null}}};
70
- (function(){Object.extend(this,{href:this._getAttr,src:this._getAttr,type:this._getAttr,disabled:this._flag,checked:this._flag,readonly:this._flag,multiple:this._flag})}).call(Element._attributeTranslations.values);Element.Methods.Simulated={hasAttribute:function(a,b){var c;b=Element._attributeTranslations.names[b]||b;return(c=$(a).getAttributeNode(b))&&c.specified}};Element.Methods.ByTag={};Object.extend(Element,Element.Methods);
71
- if(!Prototype.BrowserFeatures.ElementExtensions&&document.createElement("div").__proto__){window.HTMLElement={};window.HTMLElement.prototype=document.createElement("div").__proto__;Prototype.BrowserFeatures.ElementExtensions=true}Element.hasAttribute=function(a,b){if(a.hasAttribute)return a.hasAttribute(b);return Element.Methods.Simulated.hasAttribute(a,b)};
72
- Element.addMethods=function(a){function b(i){i=i.toUpperCase();Element.Methods.ByTag[i]||(Element.Methods.ByTag[i]={});Object.extend(Element.Methods.ByTag[i],a)}function c(i,j,k){var m=Element.extend.cache;for(var l in i){var n=i[l];if(!(k||false)||!(l in j))j[l]=m.findOrStore(n)}}function d(i){var j,k={OPTGROUP:"OptGroup",TEXTAREA:"TextArea",P:"Paragraph",FIELDSET:"FieldSet",UL:"UList",OL:"OList",DL:"DList",DIR:"Directory",H1:"Heading",H2:"Heading",H3:"Heading",H4:"Heading",H5:"Heading",H6:"Heading",
73
- Q:"Quote",INS:"Mod",DEL:"Mod",A:"Anchor",IMG:"Image",CAPTION:"TableCaption",COL:"TableCol",COLGROUP:"TableCol",THEAD:"TableSection",TFOOT:"TableSection",TBODY:"TableSection",TR:"TableRow",TH:"TableCell",TD:"TableCell",FRAMESET:"FrameSet",IFRAME:"IFrame"};if(k[i])j="HTML"+k[i]+"Element";if(window[j])return window[j];j="HTML"+i+"Element";if(window[j])return window[j];j="HTML"+i.capitalize()+"Element";if(window[j])return window[j];window[j]={};window[j].prototype=document.createElement(i).__proto__;
74
- return window[j]}var e=Prototype.BrowserFeatures,f=Element.Methods.ByTag;if(!a){Object.extend(Form,Form.Methods);Object.extend(Form.Element,Form.Element.Methods);Object.extend(Element.Methods.ByTag,{FORM:Object.clone(Form.Methods),INPUT:Object.clone(Form.Element.Methods),SELECT:Object.clone(Form.Element.Methods),TEXTAREA:Object.clone(Form.Element.Methods)})}if(arguments.length==2){var g=a;a=arguments[1]}if(g)g.constructor==Array?g.each(b):b(g);else Object.extend(Element.Methods,a||{});if(e.ElementExtensions){c(Element.Methods,
75
- HTMLElement.prototype);c(Element.Methods.Simulated,HTMLElement.prototype,true)}if(e.SpecificElementExtensions)for(var h in Element.Methods.ByTag){e=d(h);typeof e!="undefined"&&c(f[h],e.prototype)}Object.extend(Element,Element.Methods);delete Element.ByTag};var Toggle={display:Element.toggle};Abstract.Insertion=function(a){this.adjacency=a};
76
- Abstract.Insertion.prototype={initialize:function(a,b){this.element=$(a);this.content=b.stripScripts();if(this.adjacency&&this.element.insertAdjacentHTML)try{this.element.insertAdjacentHTML(this.adjacency,this.content)}catch(c){if(["TBODY","TR"].include(this.element.tagName.toUpperCase()))this.insertContent(this.contentFromAnonymousTable());else throw c;}else{this.range=this.element.ownerDocument.createRange();this.initializeRange&&this.initializeRange();this.insertContent([this.range.createContextualFragment(this.content)])}setTimeout(function(){b.evalScripts()},
77
- 10)},contentFromAnonymousTable:function(){var a=document.createElement("div");a.innerHTML="<table><tbody>"+this.content+"</tbody></table>";return $A(a.childNodes[0].childNodes[0].childNodes)}};var Insertion={};Insertion.Before=Class.create();Insertion.Before.prototype=Object.extend(new Abstract.Insertion("beforeBegin"),{initializeRange:function(){this.range.setStartBefore(this.element)},insertContent:function(a){a.each(function(b){this.element.parentNode.insertBefore(b,this.element)}.bind(this))}});
78
- Insertion.Top=Class.create();Insertion.Top.prototype=Object.extend(new Abstract.Insertion("afterBegin"),{initializeRange:function(){this.range.selectNodeContents(this.element);this.range.collapse(true)},insertContent:function(a){a.reverse(false).each(function(b){this.element.insertBefore(b,this.element.firstChild)}.bind(this))}});Insertion.Bottom=Class.create();
79
- Insertion.Bottom.prototype=Object.extend(new Abstract.Insertion("beforeEnd"),{initializeRange:function(){this.range.selectNodeContents(this.element);this.range.collapse(this.element)},insertContent:function(a){a.each(function(b){this.element.appendChild(b)}.bind(this))}});Insertion.After=Class.create();
80
- Insertion.After.prototype=Object.extend(new Abstract.Insertion("afterEnd"),{initializeRange:function(){this.range.setStartAfter(this.element)},insertContent:function(a){a.each(function(b){this.element.parentNode.insertBefore(b,this.element.nextSibling)}.bind(this))}});Element.ClassNames=Class.create();
81
- Element.ClassNames.prototype={initialize:function(a){this.element=$(a)},_each:function(a){this.element.className.split(/\s+/).select(function(b){return b.length>0})._each(a)},set:function(a){this.element.className=a},add:function(a){this.include(a)||this.set($A(this).concat(a).join(" "))},remove:function(a){this.include(a)&&this.set($A(this).without(a).join(" "))},toString:function(){return $A(this).join(" ")}};Object.extend(Element.ClassNames.prototype,Enumerable);var Selector=Class.create();
82
- Selector.prototype={initialize:function(a){this.expression=a.strip();this.compileMatcher()},compileMatcher:function(){if(Prototype.BrowserFeatures.XPath&&!/\[[\w-]*?:/.test(this.expression))return this.compileXPathMatcher();var a=this.expression,b=Selector.patterns,c=Selector.criteria,d,e;if(Selector._cache[a])this.matcher=Selector._cache[a];else{for(this.matcher=["this.matcher = function(root) {","var r = root, h = Selector.handlers, c = false, n;"];a&&d!=a&&/\S/.test(a);){d=a;for(var f in b){e=
83
- b[f];if(e=a.match(e)){this.matcher.push(typeof c[f]=="function"?c[f](e):(new Template(c[f])).evaluate(e));a=a.replace(e[0],"");break}}}this.matcher.push("return h.unique(n);\n}");eval(this.matcher.join("\n"));Selector._cache[this.expression]=this.matcher}},compileXPathMatcher:function(){var a=this.expression,b=Selector.patterns,c=Selector.xpath,d,e;if(Selector._cache[a])this.xpath=Selector._cache[a];else{for(this.matcher=[".//*"];a&&d!=a&&/\S/.test(a);){d=a;for(var f in b)if(e=a.match(b[f])){this.matcher.push(typeof c[f]==
84
- "function"?c[f](e):(new Template(c[f])).evaluate(e));a=a.replace(e[0],"");break}}this.xpath=this.matcher.join("");Selector._cache[this.expression]=this.xpath}},findElements:function(a){a=a||document;if(this.xpath)return document._getElementsByXPath(this.xpath,a);return this.matcher(a)},match:function(a){return this.findElements(document).include(a)},toString:function(){return this.expression},inspect:function(){return"#<Selector:"+this.expression.inspect()+">"}};
85
- Object.extend(Selector,{_cache:{},xpath:{descendant:"//*",child:"/*",adjacent:"/following-sibling::*[1]",laterSibling:"/following-sibling::*",tagName:function(a){if(a[1]=="*")return"";return"[local-name()='"+a[1].toLowerCase()+"' or local-name()='"+a[1].toUpperCase()+"']"},className:"[contains(concat(' ', @class, ' '), ' #{1} ')]",id:"[@id='#{1}']",attrPresence:"[@#{1}]",attr:function(a){a[3]=a[5]||a[6];return(new Template(Selector.xpath.operators[a[2]])).evaluate(a)},pseudo:function(a){var b=Selector.xpath.pseudos[a[1]];
86
- if(!b)return"";if(typeof b==="function")return b(a);return(new Template(Selector.xpath.pseudos[a[1]])).evaluate(a)},operators:{"=":"[@#{1}='#{3}']","!=":"[@#{1}!='#{3}']","^=":"[starts-with(@#{1}, '#{3}')]","$=":"[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']","*=":"[contains(@#{1}, '#{3}')]","~=":"[contains(concat(' ', @#{1}, ' '), ' #{3} ')]","|=":"[contains(concat('-', @#{1}, '-'), '-#{3}-')]"},pseudos:{"first-child":"[not(preceding-sibling::*)]","last-child":"[not(following-sibling::*)]",
87
- "only-child":"[not(preceding-sibling::* or following-sibling::*)]",empty:"[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",checked:"[@checked]",disabled:"[@disabled]",enabled:"[not(@disabled)]",not:function(a){for(var b=a[6],c=Selector.patterns,d=Selector.xpath,e,f,g=[];b&&e!=b&&/\S/.test(b);){e=b;for(var h in c)if(a=b.match(c[h])){f=typeof d[h]=="function"?d[h](a):(new Template(d[h])).evaluate(a);g.push("("+f.substring(1,f.length-1)+")");b=b.replace(a[0],"");break}}return"[not("+
88
- g.join(" and ")+")]"},"nth-child":function(a){return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ",a)},"nth-last-child":function(a){return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ",a)},"nth-of-type":function(a){return Selector.xpath.pseudos.nth("position() ",a)},"nth-last-of-type":function(a){return Selector.xpath.pseudos.nth("(last() + 1 - position()) ",a)},"first-of-type":function(a){a[6]="1";return Selector.xpath.pseudos["nth-of-type"](a)},"last-of-type":function(a){a[6]=
89
- "1";return Selector.xpath.pseudos["nth-last-of-type"](a)},"only-of-type":function(a){var b=Selector.xpath.pseudos;return b["first-of-type"](a)+b["last-of-type"](a)},nth:function(a,b){var c,d=b[6];if(d=="even")d="2n+0";if(d=="odd")d="2n+1";if(c=d.match(/^(\d+)$/))return"["+a+"= "+c[1]+"]";if(c=d.match(/^(-?\d*)?n(([+-])(\d+))?/)){if(c[1]=="-")c[1]=-1;d=c[1]?Number(c[1]):1;c=c[2]?Number(c[2]):0;return(new Template("[((#{fragment} - #{b}) mod #{a} = 0) and ((#{fragment} - #{b}) div #{a} >= 0)]")).evaluate({fragment:a,
90
- a:d,b:c})}}}},criteria:{tagName:'n = h.tagName(n, r, "#{1}", c); c = false;',className:'n = h.className(n, r, "#{1}", c); c = false;',id:'n = h.id(n, r, "#{1}", c); c = false;',attrPresence:'n = h.attrPresence(n, r, "#{1}"); c = false;',attr:function(a){a[3]=a[5]||a[6];return(new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;')).evaluate(a)},pseudo:function(a){if(a[6])a[6]=a[6].replace(/"/g,'\\"');return(new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;')).evaluate(a)},
91
- descendant:'c = "descendant";',child:'c = "child";',adjacent:'c = "adjacent";',laterSibling:'c = "laterSibling";'},patterns:{laterSibling:/^\s*~\s*/,child:/^\s*>\s*/,adjacent:/^\s*\+\s*/,descendant:/^\s/,tagName:/^\s*(\*|[\w\-]+)(\b|$)?/,id:/^#([\w\-\*]+)(\b|$)/,className:/^\.([\w\-\*]+)(\b|$)/,pseudo:/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|\s|(?=:))/,attrPresence:/^\[([\w]+)\]/,attr:/\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\]]*?)\4|([^'"][^\]]*?)))?\]/},
92
- handlers:{concat:function(a,b){for(var c=0,d;d=b[c];c++)a.push(d);return a},mark:function(a){for(var b=0,c;c=a[b];b++)c._counted=true;return a},unmark:function(a){for(var b=0,c;c=a[b];b++)c._counted=undefined;return a},index:function(a,b,c){a._counted=true;if(b){a=a.childNodes;b=a.length-1;for(var d=1;b>=0;b--){node=a[b];if(node.nodeType==1&&(!c||node._counted))node.nodeIndex=d++}}else{b=0;d=1;for(a=a.childNodes;node=a[b];b++)if(node.nodeType==1&&(!c||node._counted))node.nodeIndex=d++}},unique:function(a){if(a.length==
93
- 0)return a;for(var b=[],c,d=0,e=a.length;d<e;d++)if(!(c=a[d])._counted){c._counted=true;b.push(Element.extend(c))}return Selector.handlers.unmark(b)},descendant:function(a){for(var b=Selector.handlers,c=0,d=[],e;e=a[c];c++)b.concat(d,e.getElementsByTagName("*"));return d},child:function(a){for(var b=0,c=[],d;d=a[b];b++)for(var e=0,f;f=d.childNodes[e];e++)f.nodeType==1&&f.tagName!="!"&&c.push(f);return c},adjacent:function(a){for(var b=0,c=[],d;d=a[b];b++)(d=this.nextElementSibling(d))&&c.push(d);
94
- return c},laterSibling:function(a){for(var b=Selector.handlers,c=0,d=[],e;e=a[c];c++)b.concat(d,Element.nextSiblings(e));return d},nextElementSibling:function(a){for(;a=a.nextSibling;)if(a.nodeType==1)return a;return null},previousElementSibling:function(a){for(;a=a.previousSibling;)if(a.nodeType==1)return a;return null},tagName:function(a,b,c,d){c=c.toUpperCase();var e=[],f=Selector.handlers;if(a){if(d){if(d=="descendant"){for(b=0;d=a[b];b++)f.concat(e,d.getElementsByTagName(c));return e}else a=
95
- this[d](a);if(c=="*")return a}for(b=0;d=a[b];b++)d.tagName.toUpperCase()==c&&e.push(d);return e}else return b.getElementsByTagName(c)},id:function(a,b,c,d){c=$(c);var e=Selector.handlers;if(!a&&b==document)return c?[c]:[];if(a){if(d)if(d=="child")for(b=0;d=a[b];b++){if(c.parentNode==d)return[c]}else if(d=="descendant")for(b=0;d=a[b];b++){if(Element.descendantOf(c,d))return[c]}else if(d=="adjacent")for(b=0;d=a[b];b++){if(Selector.handlers.previousElementSibling(c)==d)return[c]}else a=e[d](a);for(b=
96
- 0;d=a[b];b++)if(d==c)return[c];return[]}return c&&Element.descendantOf(c,b)?[c]:[]},className:function(a,b,c,d){if(a&&d)a=this[d](a);return Selector.handlers.byClassName(a,b,c)},byClassName:function(a,b,c){a||(a=Selector.handlers.descendant([b]));b=0;for(var d=[],e,f;e=a[b];b++){f=e.className;if(f.length!=0)if(f==c||(" "+f+" ").include(" "+c+" "))d.push(e)}return d},attrPresence:function(a,b,c){b=[];for(var d=0,e;e=a[d];d++)Element.hasAttribute(e,c)&&b.push(e);return b},attr:function(a,b,c,d,e){a||
97
- (a=b.getElementsByTagName("*"));b=Selector.operators[e];e=[];for(var f=0,g;g=a[f];f++){var h=Element.readAttribute(g,c);h!==null&&b(h,d)&&e.push(g)}return e},pseudo:function(a,b,c,d,e){if(a&&e)a=this[e](a);a||(a=d.getElementsByTagName("*"));return Selector.pseudos[b](a,c,d)}},pseudos:{"first-child":function(a){for(var b=0,c=[],d;d=a[b];b++)Selector.handlers.previousElementSibling(d)||c.push(d);return c},"last-child":function(a){for(var b=0,c=[],d;d=a[b];b++)Selector.handlers.nextElementSibling(d)||
98
- c.push(d);return c},"only-child":function(a){for(var b=Selector.handlers,c=0,d=[],e;e=a[c];c++)!b.previousElementSibling(e)&&!b.nextElementSibling(e)&&d.push(e);return d},"nth-child":function(a,b,c){return Selector.pseudos.nth(a,b,c)},"nth-last-child":function(a,b,c){return Selector.pseudos.nth(a,b,c,true)},"nth-of-type":function(a,b,c){return Selector.pseudos.nth(a,b,c,false,true)},"nth-last-of-type":function(a,b,c){return Selector.pseudos.nth(a,b,c,true,true)},"first-of-type":function(a,b,c){return Selector.pseudos.nth(a,
99
- "1",c,false,true)},"last-of-type":function(a,b,c){return Selector.pseudos.nth(a,"1",c,true,true)},"only-of-type":function(a,b,c){var d=Selector.pseudos;return d["last-of-type"](d["first-of-type"](a,b,c),b,c)},getIndices:function(a,b,c){if(a==0)return b>0?[b]:[];return $R(1,c).inject([],function(d,e){0==(e-b)%a&&(e-b)/a>=0&&d.push(e);return d})},nth:function(a,b,c,d,e){if(a.length==0)return[];if(b=="even")b="2n+0";if(b=="odd")b="2n+1";c=Selector.handlers;var f=[],g=[],h;c.mark(a);for(var i=0;h=a[i];i++)if(!h.parentNode._counted){c.index(h.parentNode,
100
- d,e);g.push(h.parentNode)}if(b.match(/^\d+$/)){b=Number(b);for(i=0;h=a[i];i++)h.nodeIndex==b&&f.push(h)}else if(h=b.match(/^(-?\d*)?n(([+-])(\d+))?/)){if(h[1]=="-")h[1]=-1;i=h[1]?Number(h[1]):1;h=h[2]?Number(h[2]):0;b=Selector.pseudos.getIndices(i,h,a.length);i=0;for(d=b.length;h=a[i];i++)for(e=0;e<d;e++)h.nodeIndex==b[e]&&f.push(h)}c.unmark(a);c.unmark(g);return f},empty:function(a){for(var b=0,c=[],d;d=a[b];b++)d.tagName=="!"||d.firstChild&&!d.innerHTML.match(/^\s*$/)||c.push(d);return c},not:function(a,
101
- b,c){var d=Selector.handlers;b=(new Selector(b)).findElements(c);d.mark(b);c=0;for(var e=[],f;f=a[c];c++)f._counted||e.push(f);d.unmark(b);return e},enabled:function(a){for(var b=0,c=[],d;d=a[b];b++)d.disabled||c.push(d);return c},disabled:function(a){for(var b=0,c=[],d;d=a[b];b++)d.disabled&&c.push(d);return c},checked:function(a){for(var b=0,c=[],d;d=a[b];b++)d.checked&&c.push(d);return c}},operators:{"=":function(a,b){return a==b},"!=":function(a,b){return a!=b},"^=":function(a,b){return a.startsWith(b)},
102
- "$=":function(a,b){return a.endsWith(b)},"*=":function(a,b){return a.include(b)},"~=":function(a,b){return(" "+a+" ").include(" "+b+" ")},"|=":function(a,b){return("-"+a.toUpperCase()+"-").include("-"+b.toUpperCase()+"-")}},matchElements:function(a,b){var c=(new Selector(b)).findElements(),d=Selector.handlers;d.mark(c);for(var e=0,f=[],g;g=a[e];e++)g._counted&&f.push(g);d.unmark(c);return f},findElement:function(a,b,c){if(typeof b=="number"){c=b;b=false}return Selector.matchElements(a,b||"*")[c||
103
- 0]},findChildElements:function(a,b){var c=b.join(",");b=[];c.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/,function(h){b.push(h[1].strip())});c=[];for(var d=Selector.handlers,e=0,f=b.length,g;e<f;e++){g=new Selector(b[e].strip());d.concat(c,g.findElements(a))}return f>1?d.unique(c):c}});function $$(){return Selector.findChildElements(document,$A(arguments))}
104
- var Form={reset:function(a){$(a).reset();return a},serializeElements:function(a,b){var c=a.inject({},function(d,e){if(!e.disabled&&e.name){var f=e.name,g=$(e).getValue();if(g!=null)if(f in d){if(d[f].constructor!=Array)d[f]=[d[f]];d[f].push(g)}else d[f]=g}return d});return b?c:Hash.toQueryString(c)}};
105
- Form.Methods={serialize:function(a,b){return Form.serializeElements(Form.getElements(a),b)},getElements:function(a){return $A($(a).getElementsByTagName("*")).inject([],function(b,c){Form.Element.Serializers[c.tagName.toLowerCase()]&&b.push(Element.extend(c));return b})},getInputs:function(a,b,c){a=$(a);a=a.getElementsByTagName("input");if(!b&&!c)return $A(a).map(Element.extend);for(var d=0,e=[],f=a.length;d<f;d++){var g=a[d];b&&g.type!=b||c&&g.name!=c||e.push(Element.extend(g))}return e},disable:function(a){a=
106
- $(a);Form.getElements(a).invoke("disable");return a},enable:function(a){a=$(a);Form.getElements(a).invoke("enable");return a},findFirstElement:function(a){return $(a).getElements().find(function(b){return b.type!="hidden"&&!b.disabled&&["input","select","textarea"].include(b.tagName.toLowerCase())})},focusFirstElement:function(a){a=$(a);a.findFirstElement().activate();return a},request:function(a,b){a=$(a);b=Object.clone(b||{});var c=b.parameters;b.parameters=a.serialize(true);if(c){if(typeof c==
107
- "string")c=c.toQueryParams();Object.extend(b.parameters,c)}if(a.hasAttribute("method")&&!b.method)b.method=a.method;return new Ajax.Request(a.readAttribute("action"),b)}};Form.Element={focus:function(a){$(a).focus();return a},select:function(a){$(a).select();return a}};
108
- Form.Element.Methods={serialize:function(a){a=$(a);if(!a.disabled&&a.name){var b=a.getValue();if(b!=undefined){var c={};c[a.name]=b;return Hash.toQueryString(c)}}return""},getValue:function(a){a=$(a);var b=a.tagName.toLowerCase();return Form.Element.Serializers[b](a)},clear:function(a){$(a).value="";return a},present:function(a){return $(a).value!=""},activate:function(a){a=$(a);try{a.focus();if(a.select&&(a.tagName.toLowerCase()!="input"||!["button","reset","submit"].include(a.type)))a.select()}catch(b){}return a},
109
- disable:function(a){a=$(a);a.blur();a.disabled=true;return a},enable:function(a){a=$(a);a.disabled=false;return a}};var Field=Form.Element,$F=Form.Element.Methods.getValue;
110
- Form.Element.Serializers={input:function(a){switch(a.type.toLowerCase()){case "checkbox":case "radio":return Form.Element.Serializers.inputSelector(a);default:return Form.Element.Serializers.textarea(a)}},inputSelector:function(a){return a.checked?a.value:null},textarea:function(a){return a.value},select:function(a){return this[a.type=="select-one"?"selectOne":"selectMany"](a)},selectOne:function(a){var b=a.selectedIndex;return b>=0?this.optionValue(a.options[b]):null},selectMany:function(a){var b,
111
- c=a.length;if(!c)return null;var d=0;for(b=[];d<c;d++){var e=a.options[d];e.selected&&b.push(this.optionValue(e))}return b},optionValue:function(a){return Element.extend(a).hasAttribute("value")?a.value:a.text}};Abstract.TimedObserver=function(){};
112
- Abstract.TimedObserver.prototype={initialize:function(a,b,c){this.frequency=b;this.element=$(a);this.callback=c;this.lastValue=this.getValue();this.registerCallback()},registerCallback:function(){setInterval(this.onTimerEvent.bind(this),this.frequency*1E3)},onTimerEvent:function(){var a=this.getValue();if("string"==typeof this.lastValue&&"string"==typeof a?this.lastValue!=a:String(this.lastValue)!=String(a)){this.callback(this.element,a);this.lastValue=a}}};Form.Element.Observer=Class.create();
113
- Form.Element.Observer.prototype=Object.extend(new Abstract.TimedObserver,{getValue:function(){return Form.Element.getValue(this.element)}});Form.Observer=Class.create();Form.Observer.prototype=Object.extend(new Abstract.TimedObserver,{getValue:function(){return Form.serialize(this.element)}});Abstract.EventObserver=function(){};
114
- Abstract.EventObserver.prototype={initialize:function(a,b){this.element=$(a);this.callback=b;this.lastValue=this.getValue();this.element.tagName.toLowerCase()=="form"?this.registerFormCallbacks():this.registerCallback(this.element)},onElementEvent:function(){var a=this.getValue();if(this.lastValue!=a){this.callback(this.element,a);this.lastValue=a}},registerFormCallbacks:function(){Form.getElements(this.element).each(this.registerCallback.bind(this))},registerCallback:function(a){if(a.type)switch(a.type.toLowerCase()){case "checkbox":case "radio":Event.observe(a,
115
- "click",this.onElementEvent.bind(this));break;default:Event.observe(a,"change",this.onElementEvent.bind(this));break}}};Form.Element.EventObserver=Class.create();Form.Element.EventObserver.prototype=Object.extend(new Abstract.EventObserver,{getValue:function(){return Form.Element.getValue(this.element)}});Form.EventObserver=Class.create();Form.EventObserver.prototype=Object.extend(new Abstract.EventObserver,{getValue:function(){return Form.serialize(this.element)}});if(!window.Event)var Event={};
116
- Object.extend(Event,{KEY_BACKSPACE:8,KEY_TAB:9,KEY_RETURN:13,KEY_ESC:27,KEY_LEFT:37,KEY_UP:38,KEY_RIGHT:39,KEY_DOWN:40,KEY_DELETE:46,KEY_HOME:36,KEY_END:35,KEY_PAGEUP:33,KEY_PAGEDOWN:34,element:function(a){return $(a.target||a.srcElement)},isLeftClick:function(a){return a.which&&a.which==1||a.button&&a.button==1},pointerX:function(a){return a.pageX||a.clientX+(document.documentElement.scrollLeft||document.body.scrollLeft)},pointerY:function(a){return a.pageY||a.clientY+(document.documentElement.scrollTop||
117
- document.body.scrollTop)},stop:function(a){if(a.preventDefault){a.preventDefault();a.stopPropagation()}else{a.returnValue=false;a.cancelBubble=true}},findElement:function(a,b){for(var c=Event.element(a);c.parentNode&&(!c.tagName||c.tagName.toUpperCase()!=b.toUpperCase());)c=c.parentNode;return c},observers:false,_observeAndCache:function(a,b,c,d){if(!this.observers)this.observers=[];if(a.addEventListener){this.observers.push([a,b,c,d]);a.addEventListener(b,c,d)}else if(a.attachEvent){this.observers.push([a,
118
- b,c,d]);a.attachEvent("on"+b,c)}},unloadCache:function(){if(Event.observers){for(var a=0,b=Event.observers.length;a<b;a++){Event.stopObserving.apply(this,Event.observers[a]);Event.observers[a][0]=null}Event.observers=false}},observe:function(a,b,c,d){a=$(a);if(b=="keypress"&&(Prototype.Browser.WebKit||a.attachEvent))b="keydown";Event._observeAndCache(a,b,c,d||false)},stopObserving:function(a,b,c,d){a=$(a);d=d||false;if(b=="keypress"&&(Prototype.Browser.WebKit||a.attachEvent))b="keydown";if(a.removeEventListener)a.removeEventListener(b,
119
- c,d);else if(a.detachEvent)try{a.detachEvent("on"+b,c)}catch(e){}}});Prototype.Browser.IE&&Event.observe(window,"unload",Event.unloadCache,false);
120
- var Position={includeScrollOffsets:false,prepare:function(){this.deltaX=window.pageXOffset||document.documentElement.scrollLeft||document.body.scrollLeft||0;this.deltaY=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0},realOffset:function(a){var b=0,c=0;do{b+=a.scrollTop||0;c+=a.scrollLeft||0;a=a.parentNode}while(a);return[c,b]},cumulativeOffset:function(a){var b=0,c=0;do{b+=a.offsetTop||0;c+=a.offsetLeft||0;a=a.offsetParent}while(a);return[c,b]},positionedOffset:function(a){var b=
121
- 0,c=0;do{b+=a.offsetTop||0;c+=a.offsetLeft||0;if(a=a.offsetParent){if(a.tagName=="BODY")break;var d=Element.getStyle(a,"position");if(d=="relative"||d=="absolute")break}}while(a);return[c,b]},offsetParent:function(a){if(a.offsetParent)return a.offsetParent;if(a==document.body)return a;for(;(a=a.parentNode)&&a!=document.body;)if(Element.getStyle(a,"position")!="static")return a;return document.body},within:function(a,b,c){if(this.includeScrollOffsets)return this.withinIncludingScrolloffsets(a,b,c);
122
- this.xcomp=b;this.ycomp=c;this.offset=this.cumulativeOffset(a);return c>=this.offset[1]&&c<this.offset[1]+a.offsetHeight&&b>=this.offset[0]&&b<this.offset[0]+a.offsetWidth},withinIncludingScrolloffsets:function(a,b,c){var d=this.realOffset(a);this.xcomp=b+d[0]-this.deltaX;this.ycomp=c+d[1]-this.deltaY;this.offset=this.cumulativeOffset(a);return this.ycomp>=this.offset[1]&&this.ycomp<this.offset[1]+a.offsetHeight&&this.xcomp>=this.offset[0]&&this.xcomp<this.offset[0]+a.offsetWidth},overlap:function(a,
123
- b){if(!a)return 0;if(a=="vertical")return(this.offset[1]+b.offsetHeight-this.ycomp)/b.offsetHeight;if(a=="horizontal")return(this.offset[0]+b.offsetWidth-this.xcomp)/b.offsetWidth},page:function(a){var b=0,c=0,d=a;do{b+=d.offsetTop||0;c+=d.offsetLeft||0;if(d.offsetParent==document.body)if(Element.getStyle(d,"position")=="absolute")break}while(d=d.offsetParent);d=a;do if(!window.opera||d.tagName=="BODY"){b-=d.scrollTop||0;c-=d.scrollLeft||0}while(d=d.parentNode);return[c,b]},clone:function(a,b,c){c=
124
- Object.extend({setLeft:true,setTop:true,setWidth:true,setHeight:true,offsetTop:0,offsetLeft:0},c||{});a=$(a);var d=Position.page(a);b=$(b);var e=[0,0],f=null;if(Element.getStyle(b,"position")=="absolute"){f=Position.offsetParent(b);e=Position.page(f)}if(f==document.body){e[0]-=document.body.offsetLeft;e[1]-=document.body.offsetTop}if(c.setLeft)b.style.left=d[0]-e[0]+c.offsetLeft+"px";if(c.setTop)b.style.top=d[1]-e[1]+c.offsetTop+"px";if(c.setWidth)b.style.width=a.offsetWidth+"px";if(c.setHeight)b.style.height=
125
- a.offsetHeight+"px"},absolutize:function(a){a=$(a);if(a.style.position!="absolute"){Position.prepare();var b=Position.positionedOffset(a),c=b[1];b=b[0];var d=a.clientWidth,e=a.clientHeight;a._originalLeft=b-parseFloat(a.style.left||0);a._originalTop=c-parseFloat(a.style.top||0);a._originalWidth=a.style.width;a._originalHeight=a.style.height;a.style.position="absolute";a.style.top=c+"px";a.style.left=b+"px";a.style.width=d+"px";a.style.height=e+"px"}},relativize:function(a){a=$(a);if(a.style.position!=
126
- "relative"){Position.prepare();a.style.position="relative";var b=parseFloat(a.style.top||0)-(a._originalTop||0),c=parseFloat(a.style.left||0)-(a._originalLeft||0);a.style.top=b+"px";a.style.left=c+"px";a.style.height=a._originalHeight;a.style.width=a._originalWidth}}};
127
- if(Prototype.Browser.WebKit)Position.cumulativeOffset=function(a){var b=0,c=0;do{b+=a.offsetTop||0;c+=a.offsetLeft||0;if(a.offsetParent==document.body)if(Element.getStyle(a,"position")=="absolute")break;a=a.offsetParent}while(a);return[c,b]};Element.addMethods();var Scriptaculous={Version:"1.7.1_beta3",require:function(a){document.write('<script type="text/javascript" src="'+a+'"><\/script>')},REQUIRED_PROTOTYPE:"1.5.1",load:function(){function a(b){b=b.split(".");return parseInt(b[0])*1E5+parseInt(b[1])*1E3+parseInt(b[2])}if(typeof Prototype=="undefined"||typeof Element=="undefined"||typeof Element.Methods=="undefined"||a(Prototype.Version)<a(Scriptaculous.REQUIRED_PROTOTYPE))throw"script.aculo.us requires the Prototype JavaScript framework >= "+Scriptaculous.REQUIRED_PROTOTYPE;
128
- $A(document.getElementsByTagName("script")).findAll(function(b){return b.src&&b.src.match(/scriptaculous\.js(\?.*)?$/)}).each(function(b){var c=b.src.replace(/scriptaculous\.js(\?.*)?$/,"");b=b.src.match(/\?.*load=([a-z,]*)/);(b?b[1]:"builder,effects,dragdrop,controls,slider,sound").split(",").each(function(d){Scriptaculous.require(c+d+".js")})})}};Scriptaculous.load();String.prototype.parseColor=function(a){var b="#";if(this.slice(0,4)=="rgb("){var c=this.slice(4,this.length-1).split(","),d=0;do b+=parseInt(c[d]).toColorPart();while(++d<3)}else if(this.slice(0,1)=="#"){if(this.length==4)for(d=1;d<4;d++)b+=(this.charAt(d)+this.charAt(d)).toLowerCase();if(this.length==7)b=this.toLowerCase()}return b.length==7?b:a||this};
129
- Element.collectTextNodes=function(a){return $A($(a).childNodes).collect(function(b){return b.nodeType==3?b.nodeValue:b.hasChildNodes()?Element.collectTextNodes(b):""}).flatten().join("")};Element.collectTextNodesIgnoreClass=function(a,b){return $A($(a).childNodes).collect(function(c){return c.nodeType==3?c.nodeValue:c.hasChildNodes()&&!Element.hasClassName(c,b)?Element.collectTextNodesIgnoreClass(c,b):""}).flatten().join("")};
130
- Element.setContentZoom=function(a,b){a=$(a);a.setStyle({fontSize:b/100+"em"});Prototype.Browser.WebKit&&window.scrollBy(0,0);return a};Element.getInlineOpacity=function(a){return $(a).style.opacity||""};Element.forceRerendering=function(a){try{a=$(a);var b=document.createTextNode(" ");a.appendChild(b);a.removeChild(b)}catch(c){}};Array.prototype.call=function(){var a=arguments;this.each(function(b){b.apply(this,a)})};
131
- var Effect={_elementDoesNotExistError:{name:"ElementDoesNotExistError",message:"The specified DOM element does not exist, but is required for this effect to operate"},tagifyText:function(a){if(typeof Builder=="undefined")throw"Effect.tagifyText requires including script.aculo.us' builder.js library";var b="position:relative";if(Prototype.Browser.IE)b+=";zoom:1";a=$(a);$A(a.childNodes).each(function(c){if(c.nodeType==3){c.nodeValue.toArray().each(function(d){a.insertBefore(Builder.node("span",{style:b},
132
- d==" "?String.fromCharCode(160):d),c)});Element.remove(c)}})},multiple:function(a,b,c){a=(typeof a=="object"||typeof a=="function")&&a.length?a:$(a).childNodes;var d=Object.extend({speed:0.1,delay:0},c||{}),e=d.delay;$A(a).each(function(f,g){new b(f,Object.extend(d,{delay:g*d.speed+e}))})},PAIRS:{slide:["SlideDown","SlideUp"],blind:["BlindDown","BlindUp"],appear:["Appear","Fade"]},toggle:function(a,b,c){a=$(a);b=(b||"appear").toLowerCase();c=Object.extend({queue:{position:"end",scope:a.id||"global",
133
- limit:1}},c||{});Effect[a.visible()?Effect.PAIRS[b][1]:Effect.PAIRS[b][0]](a,c)}},Effect2=Effect;Effect.Transitions={linear:Prototype.K,sinoidal:function(a){return-Math.cos(a*Math.PI)/2+0.5},reverse:function(a){return 1-a},flicker:function(a){a=-Math.cos(a*Math.PI)/4+0.75+Math.random()/4;return a>1?1:a},wobble:function(a){return-Math.cos(a*Math.PI*9*a)/2+0.5},pulse:function(a,b){b=b||5;return Math.round(a%(1/b)*b)==0?a*b*2-Math.floor(a*b*2):1-(a*b*2-Math.floor(a*b*2))},none:function(){return 0},full:function(){return 1}};
134
- Effect.ScopedQueue=Class.create();
135
- Object.extend(Object.extend(Effect.ScopedQueue.prototype,Enumerable),{initialize:function(){this.effects=[];this.interval=null},_each:function(a){this.effects._each(a)},add:function(a){var b=(new Date).getTime();switch(typeof a.options.queue=="string"?a.options.queue:a.options.queue.position){case "front":this.effects.findAll(function(c){return c.state=="idle"}).each(function(c){c.startOn+=a.finishOn;c.finishOn+=a.finishOn});break;case "with-last":b=this.effects.pluck("startOn").max()||b;break;case "end":b=
136
- this.effects.pluck("finishOn").max()||b;break}a.startOn+=b;a.finishOn+=b;if(!a.options.queue.limit||this.effects.length<a.options.queue.limit)this.effects.push(a);if(!this.interval)this.interval=setInterval(this.loop.bind(this),15)},remove:function(a){this.effects=this.effects.reject(function(b){return b==a});if(this.effects.length==0){clearInterval(this.interval);this.interval=null}},loop:function(){for(var a=(new Date).getTime(),b=0,c=this.effects.length;b<c;b++)this.effects[b]&&this.effects[b].loop(a)}});
137
- Effect.Queues={instances:$H(),get:function(a){if(typeof a!="string")return a;this.instances[a]||(this.instances[a]=new Effect.ScopedQueue);return this.instances[a]}};Effect.Queue=Effect.Queues.get("global");Effect.DefaultOptions={transition:Effect.Transitions.sinoidal,duration:1,fps:100,sync:false,from:0,to:1,delay:0,queue:"parallel"};Effect.Base=function(){};
138
- Effect.Base.prototype={position:null,start:function(a){function b(c,d){return(c[d+"Internal"]?"this.options."+d+"Internal(this);":"")+(c[d]?"this.options."+d+"(this);":"")}if(a.transition===false)a.transition=Effect.Transitions.linear;this.options=Object.extend(Object.extend({},Effect.DefaultOptions),a||{});this.currentFrame=0;this.state="idle";this.startOn=this.options.delay*1E3;this.finishOn=this.startOn+this.options.duration*1E3;this.fromToDelta=this.options.to-this.options.from;this.totalTime=
139
- this.finishOn-this.startOn;this.totalFrames=this.options.fps*this.options.duration;eval('this.render = function(pos){ if(this.state=="idle"){this.state="running";'+b(a,"beforeSetup")+(this.setup?"this.setup();":"")+b(a,"afterSetup")+'};if(this.state=="running"){pos=this.options.transition(pos)*'+this.fromToDelta+"+"+this.options.from+";this.position=pos;"+b(a,"beforeUpdate")+(this.update?"this.update(pos);":"")+b(a,"afterUpdate")+"}}");this.event("beforeStart");this.options.sync||Effect.Queues.get(typeof this.options.queue==
140
- "string"?"global":this.options.queue.scope).add(this)},loop:function(a){if(a>=this.startOn)if(a>=this.finishOn){this.render(1);this.cancel();this.event("beforeFinish");this.finish&&this.finish();this.event("afterFinish")}else{a=(a-this.startOn)/this.totalTime;var b=Math.round(a*this.totalFrames);if(b>this.currentFrame){this.render(a);this.currentFrame=b}}},cancel:function(){this.options.sync||Effect.Queues.get(typeof this.options.queue=="string"?"global":this.options.queue.scope).remove(this);this.state=
141
- "finished"},event:function(a){this.options[a+"Internal"]&&this.options[a+"Internal"](this);this.options[a]&&this.options[a](this)},inspect:function(){var a=$H();for(property in this)if(typeof this[property]!="function")a[property]=this[property];return"#<Effect:"+a.inspect()+",options:"+$H(this.options).inspect()+">"}};Effect.Parallel=Class.create();
142
- Object.extend(Object.extend(Effect.Parallel.prototype,Effect.Base.prototype),{initialize:function(a,b){this.effects=a||[];this.start(b)},update:function(a){this.effects.invoke("render",a)},finish:function(a){this.effects.each(function(b){b.render(1);b.cancel();b.event("beforeFinish");b.finish&&b.finish(a);b.event("afterFinish")})}});Effect.Event=Class.create();
143
- Object.extend(Object.extend(Effect.Event.prototype,Effect.Base.prototype),{initialize:function(a){this.start(Object.extend({duration:0},a||{}))},update:Prototype.emptyFunction});Effect.Opacity=Class.create();
144
- Object.extend(Object.extend(Effect.Opacity.prototype,Effect.Base.prototype),{initialize:function(a,b){this.element=$(a);if(!this.element)throw Effect._elementDoesNotExistError;Prototype.Browser.IE&&!this.element.currentStyle.hasLayout&&this.element.setStyle({zoom:1});this.start(Object.extend({from:this.element.getOpacity()||0,to:1},b||{}))},update:function(a){this.element.setOpacity(a)}});Effect.Move=Class.create();
145
- Object.extend(Object.extend(Effect.Move.prototype,Effect.Base.prototype),{initialize:function(a,b){this.element=$(a);if(!this.element)throw Effect._elementDoesNotExistError;this.start(Object.extend({x:0,y:0,mode:"relative"},b||{}))},setup:function(){this.element.makePositioned();this.originalLeft=parseFloat(this.element.getStyle("left")||"0");this.originalTop=parseFloat(this.element.getStyle("top")||"0");if(this.options.mode=="absolute"){this.options.x-=this.originalLeft;this.options.y-=this.originalTop}},
146
- update:function(a){this.element.setStyle({left:Math.round(this.options.x*a+this.originalLeft)+"px",top:Math.round(this.options.y*a+this.originalTop)+"px"})}});Effect.MoveBy=function(a,b,c,d){return new Effect.Move(a,Object.extend({x:c,y:b},d||{}))};Effect.Scale=Class.create();
147
- Object.extend(Object.extend(Effect.Scale.prototype,Effect.Base.prototype),{initialize:function(a,b,c){this.element=$(a);if(!this.element)throw Effect._elementDoesNotExistError;this.start(Object.extend({scaleX:true,scaleY:true,scaleContent:true,scaleFromCenter:false,scaleMode:"box",scaleFrom:100,scaleTo:b},c||{}))},setup:function(){this.restoreAfterFinish=this.options.restoreAfterFinish||false;this.elementPositioning=this.element.getStyle("position");this.originalStyle={};["top","left","width","height",
148
- "fontSize"].each(function(b){this.originalStyle[b]=this.element.style[b]}.bind(this));this.originalTop=this.element.offsetTop;this.originalLeft=this.element.offsetLeft;var a=this.element.getStyle("font-size")||"100%";["em","px","%","pt"].each(function(b){if(a.indexOf(b)>0){this.fontSize=parseFloat(a);this.fontSizeType=b}}.bind(this));this.factor=(this.options.scaleTo-this.options.scaleFrom)/100;this.dims=null;if(this.options.scaleMode=="box")this.dims=[this.element.offsetHeight,this.element.offsetWidth];
149
- if(/^content/.test(this.options.scaleMode))this.dims=[this.element.scrollHeight,this.element.scrollWidth];if(!this.dims)this.dims=[this.options.scaleMode.originalHeight,this.options.scaleMode.originalWidth]},update:function(a){a=this.options.scaleFrom/100+this.factor*a;this.options.scaleContent&&this.fontSize&&this.element.setStyle({fontSize:this.fontSize*a+this.fontSizeType});this.setDimensions(this.dims[0]*a,this.dims[1]*a)},finish:function(){this.restoreAfterFinish&&this.element.setStyle(this.originalStyle)},
150
- setDimensions:function(a,b){var c={};if(this.options.scaleX)c.width=Math.round(b)+"px";if(this.options.scaleY)c.height=Math.round(a)+"px";if(this.options.scaleFromCenter){var d=(a-this.dims[0])/2,e=(b-this.dims[1])/2;if(this.elementPositioning=="absolute"){if(this.options.scaleY)c.top=this.originalTop-d+"px";if(this.options.scaleX)c.left=this.originalLeft-e+"px"}else{if(this.options.scaleY)c.top=-d+"px";if(this.options.scaleX)c.left=-e+"px"}}this.element.setStyle(c)}});Effect.Highlight=Class.create();
151
- Object.extend(Object.extend(Effect.Highlight.prototype,Effect.Base.prototype),{initialize:function(a,b){this.element=$(a);if(!this.element)throw Effect._elementDoesNotExistError;this.start(Object.extend({startcolor:"#ffff99"},b||{}))},setup:function(){if(this.element.getStyle("display")=="none")this.cancel();else{this.oldStyle={};if(!this.options.keepBackgroundImage){this.oldStyle.backgroundImage=this.element.getStyle("background-image");this.element.setStyle({backgroundImage:"none"})}if(!this.options.endcolor)this.options.endcolor=
152
- this.element.getStyle("background-color").parseColor("#ffffff");if(!this.options.restorecolor)this.options.restorecolor=this.element.getStyle("background-color");this._base=$R(0,2).map(function(a){return parseInt(this.options.startcolor.slice(a*2+1,a*2+3),16)}.bind(this));this._delta=$R(0,2).map(function(a){return parseInt(this.options.endcolor.slice(a*2+1,a*2+3),16)-this._base[a]}.bind(this))}},update:function(a){this.element.setStyle({backgroundColor:$R(0,2).inject("#",function(b,c,d){return b+
153
- Math.round(this._base[d]+this._delta[d]*a).toColorPart()}.bind(this))})},finish:function(){this.element.setStyle(Object.extend(this.oldStyle,{backgroundColor:this.options.restorecolor}))}});Effect.ScrollTo=Class.create();
154
- Object.extend(Object.extend(Effect.ScrollTo.prototype,Effect.Base.prototype),{initialize:function(a,b){this.element=$(a);this.start(b||{})},setup:function(){Position.prepare();var a=Position.cumulativeOffset(this.element);if(this.options.offset)a[1]+=this.options.offset;var b=window.innerHeight?window.height-window.innerHeight:document.body.scrollHeight-(document.documentElement.clientHeight?document.documentElement.clientHeight:document.body.clientHeight);this.scrollStart=Position.deltaY;this.delta=
155
- (a[1]>b?b:a[1])-this.scrollStart},update:function(a){Position.prepare();window.scrollTo(Position.deltaX,this.scrollStart+a*this.delta)}});Effect.Fade=function(a,b){a=$(a);var c=a.getInlineOpacity(),d=Object.extend({from:a.getOpacity()||1,to:0,afterFinishInternal:function(e){e.options.to==0&&e.element.hide().setStyle({opacity:c})}},b||{});return new Effect.Opacity(a,d)};
156
- Effect.Appear=function(a,b){a=$(a);var c=Object.extend({from:a.getStyle("display")=="none"?0:a.getOpacity()||0,to:1,afterFinishInternal:function(d){d.element.forceRerendering()},beforeSetup:function(d){d.element.setOpacity(d.options.from).show()}},b||{});return new Effect.Opacity(a,c)};
157
- Effect.Puff=function(a,b){a=$(a);var c={opacity:a.getInlineOpacity(),position:a.getStyle("position"),top:a.style.top,left:a.style.left,width:a.style.width,height:a.style.height};return new Effect.Parallel([new Effect.Scale(a,200,{sync:true,scaleFromCenter:true,scaleContent:true,restoreAfterFinish:true}),new Effect.Opacity(a,{sync:true,to:0})],Object.extend({duration:1,beforeSetupInternal:function(d){Position.absolutize(d.effects[0].element)},afterFinishInternal:function(d){d.effects[0].element.hide().setStyle(c)}},
158
- b||{}))};Effect.BlindUp=function(a,b){a=$(a);a.makeClipping();return new Effect.Scale(a,0,Object.extend({scaleContent:false,scaleX:false,restoreAfterFinish:true,afterFinishInternal:function(c){c.element.hide().undoClipping()}},b||{}))};
159
- Effect.BlindDown=function(a,b){a=$(a);var c=a.getDimensions();return new Effect.Scale(a,100,Object.extend({scaleContent:false,scaleX:false,scaleFrom:0,scaleMode:{originalHeight:c.height,originalWidth:c.width},restoreAfterFinish:true,afterSetup:function(d){d.element.makeClipping().setStyle({height:"0px"}).show()},afterFinishInternal:function(d){d.element.undoClipping()}},b||{}))};
160
- Effect.SwitchOff=function(a,b){a=$(a);var c=a.getInlineOpacity();return new Effect.Appear(a,Object.extend({duration:0.4,from:0,transition:Effect.Transitions.flicker,afterFinishInternal:function(d){new Effect.Scale(d.element,1,{duration:0.3,scaleFromCenter:true,scaleX:false,scaleContent:false,restoreAfterFinish:true,beforeSetup:function(e){e.element.makePositioned().makeClipping()},afterFinishInternal:function(e){e.element.hide().undoClipping().undoPositioned().setStyle({opacity:c})}})}},b||{}))};
161
- Effect.DropOut=function(a,b){a=$(a);var c={top:a.getStyle("top"),left:a.getStyle("left"),opacity:a.getInlineOpacity()};return new Effect.Parallel([new Effect.Move(a,{x:0,y:100,sync:true}),new Effect.Opacity(a,{sync:true,to:0})],Object.extend({duration:0.5,beforeSetup:function(d){d.effects[0].element.makePositioned()},afterFinishInternal:function(d){d.effects[0].element.hide().undoPositioned().setStyle(c)}},b||{}))};
162
- Effect.Shake=function(a){a=$(a);var b={top:a.getStyle("top"),left:a.getStyle("left")};return new Effect.Move(a,{x:20,y:0,duration:0.05,afterFinishInternal:function(c){new Effect.Move(c.element,{x:-40,y:0,duration:0.1,afterFinishInternal:function(d){new Effect.Move(d.element,{x:40,y:0,duration:0.1,afterFinishInternal:function(e){new Effect.Move(e.element,{x:-40,y:0,duration:0.1,afterFinishInternal:function(f){new Effect.Move(f.element,{x:40,y:0,duration:0.1,afterFinishInternal:function(g){new Effect.Move(g.element,
163
- {x:-20,y:0,duration:0.05,afterFinishInternal:function(h){h.element.undoPositioned().setStyle(b)}})}})}})}})}})}})};
164
- Effect.SlideDown=function(a,b){a=$(a).cleanWhitespace();var c=a.down().getStyle("bottom"),d=a.getDimensions();return new Effect.Scale(a,100,Object.extend({scaleContent:false,scaleX:false,scaleFrom:window.opera?0:1,scaleMode:{originalHeight:d.height,originalWidth:d.width},restoreAfterFinish:true,afterSetup:function(e){e.element.makePositioned();e.element.down().makePositioned();window.opera&&e.element.setStyle({top:""});e.element.makeClipping().setStyle({height:"0px"}).show()},afterUpdateInternal:function(e){e.element.down().setStyle({bottom:e.dims[0]-
165
- e.element.clientHeight+"px"})},afterFinishInternal:function(e){e.element.undoClipping().undoPositioned();e.element.down().undoPositioned().setStyle({bottom:c})}},b||{}))};
166
- Effect.SlideUp=function(a,b){a=$(a).cleanWhitespace();var c=a.down().getStyle("bottom");return new Effect.Scale(a,window.opera?0:1,Object.extend({scaleContent:false,scaleX:false,scaleMode:"box",scaleFrom:100,restoreAfterFinish:true,beforeStartInternal:function(d){d.element.makePositioned();d.element.down().makePositioned();window.opera&&d.element.setStyle({top:""});d.element.makeClipping().show()},afterUpdateInternal:function(d){d.element.down().setStyle({bottom:d.dims[0]-d.element.clientHeight+"px"})},
167
- afterFinishInternal:function(d){d.element.hide().undoClipping().undoPositioned().setStyle({bottom:c});d.element.down().undoPositioned()}},b||{}))};Effect.Squish=function(a){return new Effect.Scale(a,window.opera?1:0,{restoreAfterFinish:true,beforeSetup:function(b){b.element.makeClipping()},afterFinishInternal:function(b){b.element.hide().undoClipping()}})};
168
- Effect.Grow=function(a,b){a=$(a);var c=Object.extend({direction:"center",moveTransition:Effect.Transitions.sinoidal,scaleTransition:Effect.Transitions.sinoidal,opacityTransition:Effect.Transitions.full},b||{}),d={top:a.style.top,left:a.style.left,height:a.style.height,width:a.style.width,opacity:a.getInlineOpacity()},e=a.getDimensions(),f,g,h,i;switch(c.direction){case "top-left":f=g=h=i=0;break;case "top-right":f=e.width;g=i=0;h=-e.width;break;case "bottom-left":f=h=0;g=e.height;i=-e.height;break;
169
- case "bottom-right":f=e.width;g=e.height;h=-e.width;i=-e.height;break;case "center":f=e.width/2;g=e.height/2;h=-e.width/2;i=-e.height/2;break}return new Effect.Move(a,{x:f,y:g,duration:0.01,beforeSetup:function(j){j.element.hide().makeClipping().makePositioned()},afterFinishInternal:function(j){new Effect.Parallel([new Effect.Opacity(j.element,{sync:true,to:1,from:0,transition:c.opacityTransition}),new Effect.Move(j.element,{x:h,y:i,sync:true,transition:c.moveTransition}),new Effect.Scale(j.element,
170
- 100,{scaleMode:{originalHeight:e.height,originalWidth:e.width},sync:true,scaleFrom:window.opera?1:0,transition:c.scaleTransition,restoreAfterFinish:true})],Object.extend({beforeSetup:function(k){k.effects[0].element.setStyle({height:"0px"}).show()},afterFinishInternal:function(k){k.effects[0].element.undoClipping().undoPositioned().setStyle(d)}},c))}})};
171
- Effect.Shrink=function(a,b){a=$(a);var c=Object.extend({direction:"center",moveTransition:Effect.Transitions.sinoidal,scaleTransition:Effect.Transitions.sinoidal,opacityTransition:Effect.Transitions.none},b||{}),d={top:a.style.top,left:a.style.left,height:a.style.height,width:a.style.width,opacity:a.getInlineOpacity()},e=a.getDimensions(),f,g;switch(c.direction){case "top-left":f=g=0;break;case "top-right":f=e.width;g=0;break;case "bottom-left":f=0;g=e.height;break;case "bottom-right":f=e.width;g=
172
- e.height;break;case "center":f=e.width/2;g=e.height/2;break}return new Effect.Parallel([new Effect.Opacity(a,{sync:true,to:0,from:1,transition:c.opacityTransition}),new Effect.Scale(a,window.opera?1:0,{sync:true,transition:c.scaleTransition,restoreAfterFinish:true}),new Effect.Move(a,{x:f,y:g,sync:true,transition:c.moveTransition})],Object.extend({beforeStartInternal:function(h){h.effects[0].element.makePositioned().makeClipping()},afterFinishInternal:function(h){h.effects[0].element.hide().undoClipping().undoPositioned().setStyle(d)}},
173
- c))};Effect.Pulsate=function(a,b){a=$(a);var c=b||{},d=a.getInlineOpacity(),e=c.transition||Effect.Transitions.sinoidal,f=function(g){return e(1-Effect.Transitions.pulse(g,c.pulses))};f.bind(e);return new Effect.Opacity(a,Object.extend(Object.extend({duration:2,from:0,afterFinishInternal:function(g){g.element.setStyle({opacity:d})}},c),{transition:f}))};
174
- Effect.Fold=function(a,b){a=$(a);var c={top:a.style.top,left:a.style.left,width:a.style.width,height:a.style.height};a.makeClipping();return new Effect.Scale(a,5,Object.extend({scaleContent:false,scaleX:false,afterFinishInternal:function(){new Effect.Scale(a,1,{scaleContent:false,scaleY:false,afterFinishInternal:function(d){d.element.hide().undoClipping().setStyle(c)}})}},b||{}))};Effect.Morph=Class.create();
175
- Object.extend(Object.extend(Effect.Morph.prototype,Effect.Base.prototype),{initialize:function(a,b){this.element=$(a);if(!this.element)throw Effect._elementDoesNotExistError;var c=Object.extend({style:{}},b||{});if(typeof c.style=="string")if(c.style.indexOf(":")==-1){var d="",e="."+c.style;$A(document.styleSheets).reverse().each(function(f){if(f.cssRules)cssRules=f.cssRules;else if(f.rules)cssRules=f.rules;$A(cssRules).reverse().each(function(g){if(e==g.selectorText){d=g.style.cssText;throw $break;
176
- }});if(d)throw $break;});this.style=d.parseStyle();c.afterFinishInternal=function(f){f.element.addClassName(f.options.style);f.transforms.each(function(g){if(g.style!="opacity")f.element.style[g.style]=""})}}else this.style=c.style.parseStyle();else this.style=$H(c.style);this.start(c)},setup:function(){function a(b){if(!b||["rgba(0, 0, 0, 0)","transparent"].include(b))b="#ffffff";b=b.parseColor();return $R(0,2).map(function(c){return parseInt(b.slice(c*2+1,c*2+3),16)})}this.transforms=this.style.map(function(b){var c=
177
- b[0];b=b[1];var d=null;if(b.parseColor("#zzzzzz")!="#zzzzzz"){b=b.parseColor();d="color"}else if(c=="opacity"){b=parseFloat(b);Prototype.Browser.IE&&!this.element.currentStyle.hasLayout&&this.element.setStyle({zoom:1})}else if(Element.CSS_LENGTH.test(b)){d=b.match(/^([\+\-]?[0-9\.]+)(.*)$/);b=parseFloat(d[1]);d=d.length==3?d[2]:null}var e=this.element.getStyle(c);return{style:c.camelize(),originalValue:d=="color"?a(e):parseFloat(e||0),targetValue:d=="color"?a(b):b,unit:d}}.bind(this)).reject(function(b){return b.originalValue==
178
- b.targetValue||b.unit!="color"&&(isNaN(b.originalValue)||isNaN(b.targetValue))})},update:function(a){for(var b={},c,d=this.transforms.length;d--;)b[(c=this.transforms[d]).style]=c.unit=="color"?"#"+Math.round(c.originalValue[0]+(c.targetValue[0]-c.originalValue[0])*a).toColorPart()+Math.round(c.originalValue[1]+(c.targetValue[1]-c.originalValue[1])*a).toColorPart()+Math.round(c.originalValue[2]+(c.targetValue[2]-c.originalValue[2])*a).toColorPart():c.originalValue+Math.round((c.targetValue-c.originalValue)*
179
- a*1E3)/1E3+c.unit;this.element.setStyle(b,true)}});Effect.Transform=Class.create();
180
- Object.extend(Effect.Transform.prototype,{initialize:function(a,b){this.tracks=[];this.options=b||{};this.addTracks(a)},addTracks:function(a){a.each(function(b){var c=$H(b).values().first();this.tracks.push($H({ids:$H(b).keys().first(),effect:Effect.Morph,options:{style:c}}))}.bind(this));return this},play:function(){return new Effect.Parallel(this.tracks.map(function(a){return[$(a.ids)||$$(a.ids)].flatten().map(function(b){return new a.effect(b,Object.extend({sync:true},a.options))})}).flatten(),
181
- this.options)}});Element.CSS_PROPERTIES=$w("backgroundColor backgroundPosition borderBottomColor borderBottomStyle borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth borderRightColor borderRightStyle borderRightWidth borderSpacing borderTopColor borderTopStyle borderTopWidth bottom clip color fontSize fontWeight height left letterSpacing lineHeight marginBottom marginLeft marginRight marginTop markerOffset maxHeight maxWidth minHeight minWidth opacity outlineColor outlineOffset outlineWidth paddingBottom paddingLeft paddingRight paddingTop right textIndent top width wordSpacing zIndex");
182
- Element.CSS_LENGTH=/^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;String.prototype.parseStyle=function(){var a=document.createElement("div");a.innerHTML='<div style="'+this+'"></div>';var b=a.childNodes[0].style,c=$H();Element.CSS_PROPERTIES.each(function(d){if(b[d])c[d]=b[d]});if(Prototype.Browser.IE&&this.indexOf("opacity")>-1)c.opacity=this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1];return c};Element.morph=function(a,b,c){new Effect.Morph(a,Object.extend({style:b},c||{}));return a};
183
- ["getInlineOpacity","forceRerendering","setContentZoom","collectTextNodes","collectTextNodesIgnoreClass","morph"].each(function(a){Element.Methods[a]=Element[a]});Element.Methods.visualEffect=function(a,b,c){s=b.dasherize().camelize();effect_class=s.charAt(0).toUpperCase()+s.substring(1);new Effect[effect_class](a,c);return $(a)};Element.addMethods();var Lightbox={activeImage:null,badObjects:["select","object","embed"],container:null,enableSlideshow:null,groupName:null,imageArray:[],options:null,overlayDuration:null,overlayOpacity:null,playSlides:null,refTags:["a","area"],relAttribute:null,resizeDuration:null,slideShowTimer:null,startImage:null,initialize:function(a){if(document.getElementsByTagName){this.options=$H({animate:true,autoPlay:true,borderSize:10,containerID:document,enableSlideshow:true,googleAnalytics:false,imageDataLocation:"south",
184
- initImage:"",loop:true,overlayDuration:0.2,overlayOpacity:0.8,prefix:"",relAttribute:"lightbox",resizeSpeed:7,showGroupName:false,slideTime:4,strings:{closeLink:"close",loadingMsg:"loading",nextLink:"next &raquo;",prevLink:"&laquo; prev",startSlideshow:"start slideshow",stopSlideshow:"stop slideshow",numDisplayPrefix:"Image",numDisplaySeparator:"of"}}).merge(a);if(this.options.animate){this.overlayDuration=Math.max(this.options.overlayDuration,0);this.options.resizeSpeed=Math.max(Math.min(this.options.resizeSpeed,
185
- 10),1);this.resizeDuration=(11-this.options.resizeSpeed)*0.15}else this.resizeDuration=this.overlayDuration=0;this.enableSlideshow=this.options.enableSlideshow;this.overlayOpacity=Math.max(Math.min(this.options.overlayOpacity,1),0);this.playSlides=this.options.autoPlay;this.container=$(this.options.containerID);this.relAttribute=this.options.relAttribute;this.updateImageList();var b=this.container!=document?this.container:document.getElementsByTagName("body").item(0);a=document.createElement("div");
186
- a.setAttribute("id",this.getID("overlay"));a.style.display="none";b.appendChild(a);Event.observe(a,"click",this.end.bindAsEventListener(this));a=document.createElement("div");a.setAttribute("id",this.getID("lightbox"));a.style.display="none";b.appendChild(a);b=document.createElement("div");b.setAttribute("id",this.getID("imageDataContainer"));b.className=this.getID("clearfix");var c=document.createElement("div");c.setAttribute("id",this.getID("imageData"));b.appendChild(c);var d=document.createElement("div");
187
- d.setAttribute("id",this.getID("imageDetails"));c.appendChild(d);var e=document.createElement("span");e.setAttribute("id",this.getID("caption"));d.appendChild(e);e=document.createElement("span");e.setAttribute("id",this.getID("numberDisplay"));d.appendChild(e);e=document.createElement("span");e.setAttribute("id",this.getID("detailsNav"));d.appendChild(e);d=document.createElement("a");d.setAttribute("id",this.getID("prevLinkDetails"));d.setAttribute("href","javascript:void(0);");d.innerHTML=this.options.strings.prevLink;
188
- e.appendChild(d);Event.observe(d,"click",this.showPrev.bindAsEventListener(this));d=document.createElement("a");d.setAttribute("id",this.getID("nextLinkDetails"));d.setAttribute("href","javascript:void(0);");d.innerHTML=this.options.strings.nextLink;e.appendChild(d);Event.observe(d,"click",this.showNext.bindAsEventListener(this));d=document.createElement("a");d.setAttribute("id",this.getID("slideShowControl"));d.setAttribute("href","javascript:void(0);");e.appendChild(d);Event.observe(d,"click",this.toggleSlideShow.bindAsEventListener(this));
189
- e=document.createElement("div");e.setAttribute("id",this.getID("close"));c.appendChild(e);c=document.createElement("a");c.setAttribute("id",this.getID("closeLink"));c.setAttribute("href","javascript:void(0);");c.innerHTML=this.options.strings.closeLink;e.appendChild(c);Event.observe(c,"click",this.end.bindAsEventListener(this));this.options.imageDataLocation=="north"&&a.appendChild(b);e=document.createElement("div");e.setAttribute("id",this.getID("outerImageContainer"));a.appendChild(e);c=document.createElement("div");
190
- c.setAttribute("id",this.getID("imageContainer"));e.appendChild(c);e=document.createElement("img");e.setAttribute("id",this.getID("lightboxImage"));c.appendChild(e);e=document.createElement("div");e.setAttribute("id",this.getID("hoverNav"));c.appendChild(e);d=document.createElement("a");d.setAttribute("id",this.getID("prevLinkImg"));d.setAttribute("href","javascript:void(0);");e.appendChild(d);Event.observe(d,"click",this.showPrev.bindAsEventListener(this));d=document.createElement("a");d.setAttribute("id",
191
- this.getID("nextLinkImg"));d.setAttribute("href","javascript:void(0);");e.appendChild(d);Event.observe(d,"click",this.showNext.bindAsEventListener(this));e=document.createElement("div");e.setAttribute("id",this.getID("loading"));c.appendChild(e);c=document.createElement("a");c.setAttribute("id",this.getID("loadingLink"));c.setAttribute("href","javascript:void(0);");c.innerHTML=this.options.strings.loadingMsg;e.appendChild(c);Event.observe(c,"click",this.end.bindAsEventListener(this));this.options.imageDataLocation!=
192
- "north"&&a.appendChild(b);this.options.initImage!=""&&this.start($(this.options.initImage))}},updateImageList:function(){for(var a,b,c,d=0;d<this.refTags.length;d++){b=this.container.getElementsByTagName(this.refTags[d]);for(var e=0;e<b.length;e++){a=b[e];c=String(a.getAttribute("rel"));if(a.getAttribute("href")&&c.toLowerCase().match(this.relAttribute))a.onclick=function(){Lightbox.start(this);return false}}}},getCaption:function(a){var b=a.title||"";if(b==""){var c=$(a).getElementsBySelector("img").first();
193
- if(c)b=c.getAttribute("title")||c.getAttribute("alt");b||(b=a.innerHTML.stripTags()||a.href||"")}return b},start:function(a){this.hideBadObjects();var b=this.getPageSize();$(this.getID("overlay")).setStyle({height:b.pageHeight+"px"});new Effect.Appear(this.getID("overlay"),{duration:this.overlayDuration,from:0,to:this.overlayOpacity});this.imageArray=[];this.groupName=null;var c=a.getAttribute("rel"),d="";if(c==this.relAttribute){d=this.getCaption(a);this.imageArray.push({link:a.getAttribute("href"),
194
- title:d});this.startImage=0}else{for(var e=this.container.getElementsByTagName(a.tagName),f=0;f<e.length;f++){var g=e[f];if(g.getAttribute("href")&&g.getAttribute("rel")==c){d=this.getCaption(g);this.imageArray.push({link:g.getAttribute("href"),title:d});if(g==a)this.startImage=this.imageArray.length-1}}this.groupName=c.substring(this.relAttribute.length+1,c.length-1)}a=this.getPageScroll().y+b.winHeight/15;$(this.getID("lightbox")).setStyle({top:a+"px"}).show();this.changeImage(this.startImage)},
195
- changeImage:function(a){this.activeImage=a;this.disableKeyboardNav();this.pauseSlideShow();$(this.getID("loading")).show();$(this.getID("lightboxImage")).hide();$(this.getID("hoverNav")).hide();$(this.getID("imageDataContainer")).hide();$(this.getID("numberDisplay")).hide();$(this.getID("detailsNav")).hide();var b=new Image;b.onload=function(){$(Lightbox.getID("lightboxImage")).src=b.src;Lightbox.resizeImageContainer(b.width,b.height)};b.src=this.imageArray[this.activeImage].link;this.options.googleAnalytics&&
196
- urchinTracker(this.imageArray[this.activeImage].link)},resizeImageContainer:function(a,b){var c=$(this.getID("outerImageContainer")).getDimensions(),d=(a+this.options.borderSize*2)/c.width*100,e=(b+this.options.borderSize*2)/c.height*100,f=c.width-this.options.borderSize*2-a;c=c.height-this.options.borderSize*2-b;c!=0&&new Effect.Scale(this.getID("outerImageContainer"),e,{scaleX:false,duration:this.resizeDuration,queue:"front"});f!=0&&new Effect.Scale(this.getID("outerImageContainer"),d,{scaleY:false,
197
- delay:this.resizeDuration,duration:this.resizeDuration});if(c==0&&f==0)navigator.appVersion.indexOf("MSIE")!=-1?this.pause(250):this.pause(100);$(this.getID("prevLinkImg")).setStyle({height:b+"px"});$(this.getID("nextLinkImg")).setStyle({height:b+"px"});$(this.getID("imageDataContainer")).setStyle({width:a+this.options.borderSize*2+"px"});this.showImage()},showImage:function(){$(this.getID("loading")).hide();new Effect.Appear(this.getID("lightboxImage"),{duration:0.5,queue:"end",afterFinish:function(){Lightbox.updateDetails()}});
198
- this.preloadNeighborImages()},updateDetails:function(){$(this.getID("caption")).show();$(this.getID("caption")).update(this.imageArray[this.activeImage].title);if(this.imageArray.length>1){var a=this.options.strings.numDisplayPrefix+" "+eval(this.activeImage+1)+" "+this.options.strings.numDisplaySeparator+" "+this.imageArray.length;if(this.options.showGroupName&&this.groupName!="")a+=" "+this.options.strings.numDisplaySeparator+" "+this.groupName;$(this.getID("numberDisplay")).update(a).show();this.enableSlideshow||
199
- $(this.getID("slideShowControl")).hide();$(this.getID("detailsNav")).show()}new Effect.Parallel([new Effect.SlideDown(this.getID("imageDataContainer"),{sync:true}),new Effect.Appear(this.getID("imageDataContainer"),{sync:true})],{duration:0.65,afterFinish:function(){Lightbox.updateNav()}})},updateNav:function(){if(this.imageArray.length>1){$(this.getID("hoverNav")).show();if(this.enableSlideshow)this.playSlides?this.startSlideShow():this.stopSlideShow()}this.enableKeyboardNav()},startSlideShow:function(){this.playSlides=
200
- true;this.slideShowTimer=new PeriodicalExecuter(function(a){Lightbox.showNext();a.stop()},this.options.slideTime);$(this.getID("slideShowControl")).update(this.options.strings.stopSlideshow)},stopSlideShow:function(){this.playSlides=false;this.slideShowTimer&&this.slideShowTimer.stop();$(this.getID("slideShowControl")).update(this.options.strings.startSlideshow)},toggleSlideShow:function(){this.playSlides?this.stopSlideShow():this.startSlideShow()},pauseSlideShow:function(){this.slideShowTimer&&this.slideShowTimer.stop()},
201
- showNext:function(){if(this.imageArray.length>1){if(!this.options.loop&&(this.activeImage==this.imageArray.length-1&&this.startImage==0||this.activeImage+1==this.startImage))return this.end();this.activeImage==this.imageArray.length-1?this.changeImage(0):this.changeImage(this.activeImage+1)}},showPrev:function(){if(this.imageArray.length>1)this.activeImage==0?this.changeImage(this.imageArray.length-1):this.changeImage(this.activeImage-1)},showFirst:function(){this.imageArray.length>1&&this.changeImage(0)},
202
- showLast:function(){this.imageArray.length>1&&this.changeImage(this.imageArray.length-1)},enableKeyboardNav:function(){document.onkeydown=this.keyboardAction},disableKeyboardNav:function(){document.onkeydown=""},keyboardAction:function(a){keycode=a==null?event.keyCode:a.which;key=String.fromCharCode(keycode).toLowerCase();if(key=="x"||key=="o"||key=="c")Lightbox.end();else if(key=="p"||key=="%")Lightbox.showPrev();else if(key=="n"||key=="'")Lightbox.showNext();else if(key=="f")Lightbox.showFirst();
203
- else if(key=="l")Lightbox.showLast();else key=="s"&&Lightbox.imageArray.length>0&&Lightbox.options.enableSlideshow&&Lightbox.toggleSlideShow()},preloadNeighborImages:function(){var a=this.imageArray.length-1==this.activeImage?0:this.activeImage+1;nextImage=new Image;nextImage.src=this.imageArray[a].link;a=this.activeImage==0?this.imageArray.length-1:this.activeImage-1;prevImage=new Image;prevImage.src=this.imageArray[a].link},end:function(){this.disableKeyboardNav();this.pauseSlideShow();$(this.getID("lightbox")).hide();
204
- new Effect.Fade(this.getID("overlay"),{duration:this.overlayDuration});this.showBadObjects()},showBadObjects:function(){for(var a,b=Lightbox.badObjects,c=0;c<b.length;c++){a=document.getElementsByTagName(b[c]);for(var d=0;d<a.length;d++)$(a[d]).setStyle({visibility:"visible"})}},hideBadObjects:function(){for(var a,b=Lightbox.badObjects,c=0;c<b.length;c++){a=document.getElementsByTagName(b[c]);for(var d=0;d<a.length;d++)$(a[d]).setStyle({visibility:"hidden"})}},pause:function(a){var b=new Date;for(a=
205
- b.getTime()+a;;){b=new Date;if(b.getTime()>a)return}},getPageScroll:function(){var a,b;if(self.pageYOffset){a=self.pageXOffset;b=self.pageYOffset}else if(document.documentElement&&document.documentElement.scrollTop){a=document.documentElement.scrollLeft;b=document.documentElement.scrollTop}else if(document.body){a=document.body.scrollLeft;b=document.body.scrollTop}return{x:a,y:b}},getPageSize:function(){var a,b,c,d;if(window.innerHeight&&window.scrollMaxY){a=document.body.scrollWidth;b=window.innerHeight+
206
- window.scrollMaxY}else if(document.body.scrollHeight>document.body.offsetHeight){a=document.body.scrollWidth;b=document.body.scrollHeight}else{a=document.body.offsetWidth;b=document.body.offsetHeight}if(self.innerHeight){c=self.innerWidth;d=self.innerHeight}else if(document.documentElement&&document.documentElement.clientHeight){c=document.documentElement.clientWidth;d=document.documentElement.clientHeight}else if(document.body){c=document.body.clientWidth;d=document.body.clientHeight}return{pageWidth:a<
207
- c?c:a,pageHeight:b<d?d:b,winWidth:c,winHeight:d}},getID:function(a){return this.options.prefix+a}};
1
+ (function(d){SLB={activeImage:null,badObjects:["select","object","embed"],container:null,enableSlideshow:null,groupName:null,imageArray:[],options:null,overlayDuration:null,overlayOpacity:null,playSlides:null,refTags:["a"],relAttribute:null,resizeDuration:null,slideShowTimer:null,startImage:null,prefix:"",checkedUrls:{},media:{},initialize:function(a){this.options=d.extend(true,{animate:true,validateLinks:false,captionEnabled:true,captionSrc:true,descEnabled:true,autoPlay:true,borderSize:10,containerID:document,
2
+ enableSlideshow:true,googleAnalytics:false,imageDataLocation:"south",initImage:"",loop:true,overlayDuration:0.2,overlayOpacity:0.8,relAttribute:null,resizeSpeed:400,showGroupName:false,slideTime:4,altsrc:"src",mId:"id",strings:{closeLink:"close",loadingMsg:"loading",nextLink:"next &raquo;",prevLink:"&laquo; prev",startSlideshow:"start slideshow",stopSlideshow:"stop slideshow",numDisplayPrefix:"Image",numDisplaySeparator:"of"},placeholders:{slbContent:'<img id="slb_slbContent" />',slbLoading:'<span id="slb_slbLoading">loading</span>',
3
+ slbClose:'<a class="slb_slbClose" href="#">close</a>',navPrev:'<a class="slb_navPrev slb_nav" href="#">&laquo; prev</a>',navNext:'<a class="slb_navNext slb_nav" href="#">&raquo; next</a>',navSlideControl:'<a class="slb_navSlideControl" href="#">Stop</a>',dataCaption:'<span class="slb_dataCaption"></span>',dataDescription:'<span class="slb_dataDescription"></span>',dataNumber:'<span class="slb_dataNumber"></span>'},layout:null},a);if(!this.options.layout||this.options.layout.toString().length==0)this.end();
4
+ if("prefix"in this.options)this.prefix=this.options.prefix;if(null==this.options.relAttribute)this.options.relAttribute=[this.prefix];else if(!d.isArray(this.options.relAttribute))this.options.relAttribute=[this.options.relAttribute.toString()];this.relAttribute=this.options.relAttribute;if(this.options.animate){this.overlayDuration=Math.max(this.options.overlayDuration,0);this.resizeDuration=this.options.resizeSpeed}else this.resizeDuration=this.overlayDuration=0;this.enableSlideshow=this.options.enableSlideshow;
5
+ this.overlayOpacity=Math.max(Math.min(this.options.overlayOpacity,1),0);this.playSlides=this.options.autoPlay;this.container=d(this.options.containerID);this.updateImageList();var b=this;a=d(this.container).get(0)!=document?this.container:d("body");d("<div/>",{id:this.getID("overlay"),css:{display:"none"}}).appendTo(a).click(function(){b.end()});a=d("<div/>",{id:this.getID("lightbox"),css:{display:"none"}}).appendTo(a).click(function(){b.end()});var c=this.getLayout();d(c).appendTo(a);this.setUI();
6
+ this.setEvents();this.options.initImage!=""&&this.start(d(this.options.initImage))},getLayout:function(){var a=this.options.layout,b,c;for(b in this.options.placeholders){c="{"+b+"}";if(a.indexOf(c)!=-1){c=new RegExp(c,"g");a=a.replace(c,this.options.placeholders[b])}}return a},setUI:function(){var a=this.options.strings;this.get("slbClose").html(a.closeLink);this.get("navNext").html(a.nextLink);this.get("navPrev").html(a.prevLink);this.get("navSlideControl").html(this.playSlides?a.stopSlideshow:
7
+ a.startSlideshow)},setEvents:function(){var a=this;this.get("container,details").click(function(e){e.stopPropagation()});var b=function(){a.get("navPrev").unbind("click").click(false);setTimeout(function(){a.get("navPrev").click(b)},500);a.showPrev();return false};this.get("navPrev").click(function(){return b()});var c=function(){a.get("navNext").unbind("click").click(false);setTimeout(function(){a.get("navNext").click(c)},500);a.showNext();return false};this.get("navNext").click(function(){return c()});
8
+ this.get("navSlideControl").click(function(){a.toggleSlideShow();return false});this.get("slbClose").click(function(){a.end();return false})},updateImageList:function(){for(var a=this,b=[],c='[href][rel*="{relattr}"]:not([rel~="'+this.addPrefix("off")+'"])',e=0;e<this.refTags.length;e++)for(var h=0;h<this.relAttribute.length;h++)b.push(this.refTags[e]+c.replace("{relattr}",this.relAttribute[h]));b=b.join(",");d(b,d(this.container)).live("click",function(){a.start(this);return false})},start:function(a){a=
9
+ d(a);this.hideBadObjects();this.imageArray=[];this.groupName=this.getGroup(a);d(a).attr("rel");var b=this,c={};this.fileExists(this.getSourceFile(a),function(){b.get("overlay").height(d(document).height()).fadeTo(b.overlayDuration,b.overlayOpacity);var e=function(){b.startImage=0;var f=[],l;for(var k in c)f.push(k);f.sort(function(n,o){return n-o});for(k=0;k<f.length;k++){l=c[f[k]];if(d(l).get(0)==d(a).get(0))b.startImage=k;b.imageArray.push({link:b.getSourceFile(d(l)),title:b.getCaption(l),desc:b.getDescription(l)})}f=
10
+ d(document).scrollTop()+d(window).height()/15;b.get("lightbox").css("top",f+"px").show();b.changeImage(b.startImage)};if(null==b.groupName){c[0]=a;b.startImage=0;e()}else{var h=d(b.container).find(d(a).get(0).tagName.toLowerCase()),j=[],g,i;for(g=0;g<h.length;g++){i=d(h[g]);b.getSourceFile(i)&&b.getGroup(i)==b.groupName&&j.push(i)}var m=0;for(g=0;g<j.length;g++){i=j[g];b.fileExists(b.getSourceFile(d(i)),function(f){c[f.idx]=f.els[f.idx];m++;m==f.els.length&&e()},function(f){m++;f.idx==f.els.length&&
11
+ e()},{idx:g,els:j})}}},function(){b.end()})},getMediaId:function(a){a=d(a).attr("rel")||"";var b=0;if(a.length){var c=new RegExp("\\b"+this.addPrefix(this.options.mId)+"\\[(.+?)\\](?:\\b|$)");if(c.test(a))b=c.exec(a)[1]}return b},getMediaProperties:function(a){var b={};a=this.getMediaId(a);if(a in this.media)b=this.media[a];return b},getMediaProperty:function(a,b){var c=this.getMediaProperties(a);return b in c?c[b]:false},getCaption:function(a){a=d(a);var b="";if(this.options.captionEnabled){var c=
12
+ {capt:".wp-caption-text",gIcon:".gallery-icon"};a={link:a,origin:a,sibs:null,img:null};if(d(a.link).parent(c.gIcon).length>0)a.origin=d(a.link).parent();if((a.sibs=d(a.origin).siblings(c.capt))&&d(a.sibs).length>0)b=d(a.sibs).first().text();b=d.trim(b);if(""==b){a.img=d(a.link).find("img").first();if(d(a.img).length)b=d(a.img).attr("title")||d(a.img).attr("alt")}b=d.trim(b);if(""==b)if(d.trim(d(c.link).text()).length)b=d.trim(d(c.link).text());else if(this.options.captionSrc)b=d(c.link).attr("href");
13
+ b=d.trim(b)}return b},getDescription:function(a){var b="";if(this.options.descEnabled)(b=this.inGallery(a,"ng")?d(a).attr("title"):this.getMediaProperty(a,"desc"))||(b="");return b},inGallery:function(a,b){var c={wp:".gallery-icon",ng:".ngg-gallery-thumbnail"};if(typeof b=="undefined"||!(b in c))b="wp";return d(a).parent(c[b]).length>0?true:false},getSourceFile:function(a){var b=d(a).attr("href"),c=d(a).attr("rel")||"";if(c.length){relSrc=this.getMediaProperty(a,"source");if(!relSrc||!relSrc.length){a=
14
+ new RegExp("\\b"+this.addPrefix(this.options.altsrc)+"\\[(.+?)\\](?:\\b|$)");if(a.test(c))relSrc=a.exec(c)[1]}if(relSrc.length)b=relSrc}return b},getGroup:function(a){var b=null;a=d(a).attr("rel")||"";if(a!=""){var c="";c="";for(var e,h=0;h<this.relAttribute.length;h++){c=this.relAttribute[h];e=a.indexOf(c);if(" "!=c.charAt(0)&&e>0){c=" "+c;e=a.indexOf(c)}if(e!=-1){c=d.trim(a.substring(e).replace(c,""));if(c.length&&"["==c.charAt(0)&&c.indexOf("]")!=-1)b=c.substring(1,c.indexOf("]"))}}}return b},
15
+ changeImage:function(a){this.activeImage=a;this.disableKeyboardNav();this.pauseSlideShow();this.get("slbLoading").show();this.get("slbContent").hide();this.get("details").hide();var b=new Image,c=this;d(b).bind("load",function(){c.get("slbContent").attr("src",b.src);c.resizeImageContainer(b.width,b.height);c.isSlideShowActive()&&c.startSlideShow()});b.src=this.imageArray[this.activeImage].link},resizeImageContainer:function(a,b){this.get("container");var c=this.options.borderSize*2;this.get("container").animate({width:a+
16
+ c,height:b+c},this.resizeDuration);this.showImage()},showImage:function(){this.get("slbLoading").hide();var a=this;this.get("slbContent").fadeIn(500,function(){a.updateDetails()});this.preloadNeighborImages()},updateDetails:function(){if(this.options.captionEnabled){this.get("dataCaption").text(this.imageArray[this.activeImage].title);this.get("dataCaption").show()}else this.get("dataCaption").hide();this.get("dataDescription").html(this.imageArray[this.activeImage].desc);if(this.hasImages()){var a=
17
+ this.options.strings.numDisplayPrefix+" "+(this.activeImage+1)+" "+this.options.strings.numDisplaySeparator+" "+this.imageArray.length;if(this.options.showGroupName&&this.groupName!="")a+=" "+this.options.strings.numDisplaySeparator+" "+this.groupName;this.get("dataNumber").text(a).show()}this.get("details").width(this.get("slbContent").width()+this.options.borderSize*2);this.updateNav();this.get("details").animate({height:"show",opacity:"show"},650)},updateNav:function(){if(this.hasImages()){this.get("navPrev").show();
18
+ this.get("navNext").show();if(this.enableSlideshow){this.get("navSlideControl").show();this.playSlides?this.startSlideShow():this.stopSlideShow()}else this.get("navSlideControl").hide()}else{this.get("dataNumber").hide();this.get("navPrev").hide();this.get("navNext").hide();this.get("navSlideControl").hide()}this.enableKeyboardNav()},isSlideShowActive:function(){return this.playSlides},startSlideShow:function(){this.playSlides=true;var a=this;clearInterval(this.slideShowTimer);this.slideShowTimer=
19
+ setInterval(function(){a.showNext();a.pauseSlideShow()},this.options.slideTime*1E3);this.get("navSlideControl").text(this.options.strings.stopSlideshow)},stopSlideShow:function(){this.playSlides=false;this.slideShowTimer&&clearInterval(this.slideShowTimer);this.get("navSlideControl").text(this.options.strings.startSlideshow)},toggleSlideShow:function(){this.playSlides?this.stopSlideShow():this.startSlideShow()},pauseSlideShow:function(){this.slideShowTimer&&clearInterval(this.slideShowTimer)},hasImage:function(){return this.imageArray.length>
20
+ 0},hasImages:function(){return this.imageArray.length>1},isFirstImage:function(){return this.activeImage==0},isLastImage:function(){return this.activeImage==this.imageArray.length-1},showNext:function(){if(this.hasImages()){if(!this.options.loop&&this.isLastImage())return this.end();this.isLastImage()?this.showFirst():this.changeImage(this.activeImage+1)}},showPrev:function(){if(this.hasImages()){if(!this.options.loop&&this.isFirstImage())return this.end();this.activeImage==0?this.showLast():this.changeImage(this.activeImage-
21
+ 1)}},showFirst:function(){this.hasImages()&&this.changeImage(0)},showLast:function(){this.hasImages()&&this.changeImage(this.imageArray.length-1)},enableKeyboardNav:function(){var a=this;d(document).keydown(function(b){a.keyboardAction(b)})},disableKeyboardNav:function(){d(document).unbind("keydown")},keyboardAction:function(a){keycode=a==null?event.keyCode:a.which;key=String.fromCharCode(keycode).toLowerCase();if(keycode==27||key=="x"||key=="o"||key=="c")this.end();else if(key=="p"||key=="%")this.showPrev();
22
+ else if(key=="n"||key=="'")this.showNext();else if(key=="f")this.showFirst();else if(key=="l")this.showLast();else key=="s"&&this.hasImage()&&this.options.enableSlideshow&&this.toggleSlideShow()},preloadNeighborImages:function(){var a=this.imageArray.length-1==this.activeImage?0:this.activeImage+1;nextImage=new Image;nextImage.src=this.imageArray[a].link;a=this.activeImage==0?this.imageArray.length-1:this.activeImage-1;prevImage=new Image;prevImage.src=this.imageArray[a].link},end:function(){this.disableKeyboardNav();
23
+ this.pauseSlideShow();this.get("lightbox").hide();this.get("overlay").fadeOut(this.overlayDuration);this.showBadObjects()},showBadObjects:function(a){d(this.badObjects.join(",")).css("visibility",(typeof a=="undefined"?true:!!a)?"visible":"hidden")},hideBadObjects:function(){this.showBadObjects(false)},getSep:function(a){return typeof a=="undefined"?"_":a},getPrefix:function(){return this.prefix},addPrefix:function(a,b){return this.getPrefix()+this.getSep(b)+a},hasPrefix:function(a){return a.indexOf(this.addPrefix(""))==
24
+ 0?true:false},getID:function(a){return this.addPrefix(a)},getSel:function(a){var b="#";if(a.toString().indexOf(",")!=-1){a=a.toString().split(",");for(b=0;b<a.length;b++)a[b]=this.getSel(d.trim(a[b]));a=a.join(",")}else{if(a in this.options.placeholders)d(this.options.placeholders[a]).attr("id")||(b=".");a=b+this.getID(a)}return a},get:function(a){return d(this.getSel(a))},fileExists:function(a,b,c,e){if(!this.options.validateLinks)return b(e);var h=this,j=function(i){if(i.status<400)d.isFunction(b)&&
25
+ b(e);else d.isFunction(c)&&c(e)};if(a in this.checkedUrls)j(this.checkedUrls[a]);else{var g=new XMLHttpRequest;g.open("HEAD",a,true);g.onreadystatechange=function(){if(4==this.readyState){h.addUrl(a,this);j(this)}};g.send()}},addUrl:function(a,b){a in this.checkedUrls||(this.checkedUrls[a]=b)}}})(jQuery);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
main.php CHANGED
Binary file
model.php CHANGED
@@ -1,6 +1,7 @@
1
  <?php
2
 
3
  require_once 'includes/class.base.php';
 
4
 
5
  /**
6
  * Lightbox functionality class
@@ -11,6 +12,14 @@ class SLB_Lightbox extends SLB_Base {
11
 
12
  /*-** Properties **-*/
13
 
 
 
 
 
 
 
 
 
14
  /**
15
  * Page that plugin options are on
16
  * @var string
@@ -23,38 +32,87 @@ class SLB_Lightbox extends SLB_Base {
23
  */
24
  var $options_admin_form = 'options.php';
25
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  /**
27
- * Default options
28
- * 0: Value
29
- * 1: Label
30
  * @var array
31
  */
32
- var $options_default = array (
33
- 'header_enabled' => 'Activation',
34
- 'enabled' => array(true, 'Enable Lightbox Functionality'),
35
- 'enabled_home' => array(true, 'Enable on Home page'),
36
- 'enabled_single' => array(true, 'Enable on Single Posts/Pages'),
37
- 'enabled_archive' => array(true, 'Enable on Archive Pages (tags, categories, etc.)'),
38
- 'activate_links' => array(true, 'Activate all image links on page'),
39
- 'header_activation' => 'Grouping',
40
- 'group_links' => array(true, 'Group automatically activated links (for displaying as a slideshow)'),
41
- 'group_post' => array(true, 'Group image links by Post (e.g. on pages with multiple posts)'),
42
- 'header_ui' => 'UI',
43
- 'autostart' => array(true, 'Automatically Start Slideshow'),
44
- 'duration' => array(6, 'Slide Duration (Seconds)', array('size' => 3, 'maxlength' => 3)),
45
- 'loop' => array(true, 'Loop through images'),
46
- 'overlay_opacity' => array(0.8, 'Overlay Opacity (0 - 1)', array('size' => 3, 'maxlength' => 3)),
47
- 'header_strings' => 'Labels',
48
- 'txt_closeLink' => array('close', 'Close link (for accessibility only, image used for button)'),
49
- 'txt_loadingMsg' => array('loading', 'Loading indicator'),
50
- 'txt_nextLink' => array('next &raquo;', 'Next Image link'),
51
- 'txt_prevLink' => array('&laquo; prev', 'Previous Image link'),
52
- 'txt_startSlideshow' => array('start slideshow', 'Start Slideshow link'),
53
- 'txt_stopSlideshow' => array('stop slideshow', 'Stop Slideshow link'),
54
- 'txt_numDisplayPrefix' => array('Image', 'Image number prefix (e.g. <strong>Image</strong> x of y)'),
55
- 'txt_numDisplaySeparator' => array('of', 'Image number separator (e.g. Image x <strong>of</strong> y)')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  );
57
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  /*-** Init **-*/
59
 
60
  function SLB_Lightbox() {
@@ -64,15 +122,23 @@ class SLB_Lightbox extends SLB_Base {
64
  function __construct() {
65
  parent::__construct();
66
  $this->init();
67
- }
68
-
69
- function init() {
70
- $this->register_hooks();
 
 
 
 
 
 
71
  }
72
 
73
  function register_hooks() {
74
- register_activation_hook($this->util->get_plugin_base_file(), $this->m('activate'));
 
75
  /* Admin */
 
76
  //Init lightbox admin
77
  add_action('admin_init', $this->m('admin_settings'));
78
  //Enqueue header files (CSS/JS)
@@ -80,34 +146,21 @@ class SLB_Lightbox extends SLB_Base {
80
  //Reset Settings
81
  add_action('admin_action_' . $this->add_prefix('reset'), $this->m('admin_reset'));
82
  add_action('admin_notices', $this->m('admin_notices'));
 
 
83
 
84
  /* Client-side */
85
- //Init lightbox (client-side)
 
86
  add_action('wp_enqueue_scripts', $this->m('enqueue_files'));
87
  add_action('wp_head', $this->m('client_init'));
88
- add_filter('plugin_action_links_' . $this->util->get_plugin_base_name(), $this->m('admin_plugin_action_links'), 10, 4);
89
- add_filter('the_content', $this->m('activate_post_links'));
90
- }
91
-
92
- function activate() {
93
- //Set default options (if not yet set)
94
- $this->reset_options(false);
95
- }
96
-
97
- /**
98
- * Resets option values to their default values
99
- * @param bool $hard Reset all options if TRUE (default), Reset only unset options if FALSE
100
- */
101
- function reset_options($hard = true) {
102
- foreach ( $this->options_default as $id => $data ) {
103
- $opt = $this->add_prefix($id);
104
- if ( !$hard && !is_null(get_option($opt, null)) ) {
105
- continue;
106
- }
107
- update_option($opt, $data[0]);
108
- }
109
  }
110
-
111
  /*-** Helpers **-*/
112
 
113
  /**
@@ -115,88 +168,177 @@ class SLB_Lightbox extends SLB_Base {
115
  * @return bool TRUE if lightbox is currently enabled, FALSE otherwise
116
  */
117
  function is_enabled($check_request = true) {
118
- $ret = ( get_option($this->add_prefix('enabled')) ) ? true : false;
119
  if ( $ret && $check_request ) {
120
  $opt = '';
121
  //Determine option to check
122
  if ( is_home() )
123
  $opt = 'home';
124
- elseif ( is_single() )
125
- $opt = 'single';
 
126
  elseif ( is_archive() || is_search() )
127
  $opt = 'archive';
128
  //Check option
129
- if ( ! empty($opt) && ( $opt = 'enabled_' . $opt ) && isset($this->options_default[$opt]) ) {
130
- $ret = ( get_option($this->add_prefix($opt)) ) ? true : false;
131
  }
132
  }
133
  return $ret;
134
  }
135
 
 
 
136
  /**
137
- * Builds object of option data
138
- * Properties:
139
- * > id: Option ID
140
- * > value: Option's value (uses default value if option not yet set)
141
- * > value_default: Option's default value (formatted)
142
- *
143
- * @param string $option Option name
144
- * @return object Option data
145
  */
146
- function get_option($option) {
147
- $ret = new stdClass();
148
- $ret->id = $this->add_prefix($option);
149
- $ret->value = get_option($ret->id, $this->get_default_value($option, false));
150
- $ret->value_default = $this->get_default_value($option, false);
151
- $ret->value_default_formatted = $this->get_default_value($option);
152
- $ret->attr = $this->get_default_attr($option);
153
- return $ret;
154
  }
155
 
156
  /**
157
- * Retrieve an option's value
158
- * @param string $option Option name
159
- * @return mixed Option value
 
160
  */
161
- function get_option_value($option) {
162
- $opt = $this->get_option($option);
163
- return $opt->value;
 
 
 
 
 
 
164
  }
165
 
166
  /**
167
- * Retrieve default attributes for an option
168
- * @param string $option Option name
169
- * @return array Default attributes
 
 
170
  */
171
- function get_default_attr($option) {
172
- $ret = array();
173
- if ( isset($this->options_default[$option][2]) )
174
- $ret = $this->options_default[$option][2];
175
- return $ret;
176
  }
177
 
178
  /**
179
- * Retrieve default value for specified option
180
- * @param string $option Option name
181
- * @param bool $formatted Whether to return formatted value (e.g. for use in admin UI)
182
- * @return mixed Option default value
183
  */
184
- function get_default_value($option, $formatted = true) {
185
- $ret = '';
186
- if ( isset($this->options_default[$option][0]) ) {
187
- $ret = $this->options_default[$option][0];
188
- //Format value (if required)
189
- if ( $formatted ) {
190
- if ( is_bool($ret) || ( is_string($ret) && 'on' == $ret ) )
191
- $ret = ( $ret ) ? 'Enabled' : 'Disabled';
192
- if ( is_numeric($ret) )
193
- $ret = strval($ret);
194
- $ret = htmlentities($ret);
195
- }
196
- } elseif ( ! is_array($this->options_default[$option]) ) {
197
- $ret = $this->options_default[$option];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
198
  }
199
- return $ret;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  }
201
 
202
  /*-** Frontend **-*/
@@ -208,34 +350,126 @@ class SLB_Lightbox extends SLB_Base {
208
  * @param $content
209
  */
210
  function activate_post_links($content) {
 
211
  //Check option
212
- if ( ! is_feed() && $this->is_enabled() && $this->get_option_value('activate_links') && $this->get_option_value('group_links') && $this->get_option_value('group_post') ) {
213
- //Scan for links
214
- $matches = array();
215
- if ( preg_match_all("/\<a[^\>]*href=[^\s]+\.(?:jp[e]*g|gif|png).*?\>/i", $content, $matches) ) {
 
 
 
 
 
216
  global $post;
 
 
 
217
  //Iterate through links & add lightbox if necessary
218
- foreach ($matches[0] as $link) {
 
 
219
  //Check if rel attribute exists
220
  $link_new = $link;
221
- $rel = '';
222
- if ( strpos(strtolower($link_new), ' rel=') !== false && preg_match("/\s+rel=(?:\"|')(.*?)(?:\"|')(\s|\>)/i", $link_new, $rel) ) {
223
- //Check if lightbox is already set in rel attribute
224
- $link_new = str_replace($rel[0], $rel[2], $link_new);
225
- $rel = $rel[1];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
  }
 
227
 
228
- if ( strpos($rel, 'lightbox') === false) {
229
- //Add rel attribute to link
230
- $rel .= ' lightbox[' . $this->add_prefix($post->ID) . ']';
231
- $link_new = '<a rel="' . $rel . '"' . substr($link_new,2);
232
- //Insert modified link
233
- $content = str_replace($link, $link_new, $content);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
  }
 
 
 
 
 
 
 
 
 
235
  }
236
  }
237
  }
238
- return $content;
239
  }
240
 
241
  /**
@@ -244,8 +478,21 @@ class SLB_Lightbox extends SLB_Base {
244
  function enqueue_files() {
245
  if ( ! $this->is_enabled() )
246
  return;
247
- wp_enqueue_script($this->add_prefix('lib'), $this->util->get_file_url('js/lib.js'));
248
- wp_enqueue_style($this->add_prefix('lightbox_css'), $this->util->get_file_url('css/lightbox.css'));
 
 
 
 
 
 
 
 
 
 
 
 
 
249
  }
250
 
251
  /**
@@ -258,55 +505,66 @@ class SLB_Lightbox extends SLB_Base {
258
 
259
  $options = array();
260
  $out = array();
261
- $out['script_start'] = '<script type="text/javascript">Event.observe(window,"load",function(){';
262
- $out['script_end'] = '});</script>';
263
  $js_code = array();
264
- //Activate links on page
265
- if ( $this->get_option_value('activate_links') ) {
266
- $rel = ( $this->get_option_value('group_links') ) ? 'lightbox[' . $this->get_prefix() . ']' : 'lightbox';
267
- ob_start();
268
- ?>
269
- $$('a[href$=".jpg"]:not([rel~="lightbox"])','a[href$=".jpeg"]:not([rel~="lightbox"])','a[href$=".gif"]:not([rel~="lightbox"])','a[href$=".png"]:not([rel~="lightbox"])').each(function(el){if (! /(^|\b)lightbox\[.+\]($|\b)/i.test(el.rel)){var rel=(el.rel.length > 0) ? el.rel + ' ' : '';el.rel=rel + '<?php echo $rel; ?>';}});
270
- <?php
271
- $js_code[] = ob_get_clean();
272
- }
273
  //Get options
274
  $options = array(
275
- 'autoPlay' => $this->get_option_value('autostart'),
276
- 'slideTime' => $this->get_option_value('duration'),
277
- 'loop' => $this->get_option_value('loop'),
278
- 'overlayOpacity' => $this->get_option_value('overlay_opacity')
 
 
 
 
 
 
 
 
 
279
  );
280
- $lb_obj = array();
281
- foreach ($options as $option => $val) {
282
- if ($val === TRUE || $val == 'on')
283
- $val = 'true';
284
- elseif ($val === FALSE || empty($val))
285
- $val = 'false';
286
- $lb_obj[] = "'{$option}':{$val}";
287
- }
288
  //Load UI Strings
289
  if ( ($strings = $this->build_strings()) && !empty($strings) )
290
- $lb_obj[] = $strings;
291
- $js_code[] = 'Lightbox.initialize({' . implode(',', $lb_obj) . '});';
292
- echo $out['script_start'] . implode('', $js_code) . $out['script_end'];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
293
  }
294
 
295
  /**
296
  * Build JS object of UI strings when initializing lightbox
297
- * @return string JS object of UI strings
298
  */
299
  function build_strings() {
300
- $ret = '';
 
301
  $prefix = 'txt_';
302
- $opt_strings = array_filter(array_keys($this->options_default), create_function('$opt', 'return ( strpos($opt, "' . $prefix . '") === 0 );'));
303
- if ( $opt_strings ) {
304
- $strings = array();
305
  foreach ( $opt_strings as $key ) {
306
  $name = substr($key, strlen($prefix));
307
- $strings[] = "'" . $name . "':'" . $this->get_option_value($key) . "'";
308
  }
309
- $ret = "'strings':{" . implode(',', $strings) . "}";
310
  }
311
  return $ret;
312
  }
@@ -345,7 +603,7 @@ class SLB_Lightbox extends SLB_Base {
345
  $action = 'reset';
346
  if ( isset($_REQUEST['action']) && $this->add_prefix($action) == $_REQUEST['action'] ) {
347
  //Reset settings
348
- $this->reset_options(true);
349
  $uri = remove_query_arg(array('_wpnonce', 'action'), add_query_arg(array($this->add_prefix('action') => $action), $_SERVER['REQUEST_URI']));
350
  //Redirect user
351
  wp_redirect($uri);
@@ -378,6 +636,7 @@ class SLB_Lightbox extends SLB_Base {
378
  /**
379
  * Adds settings section for Lightbox functionality
380
  * Section is added to Settings > Media Admin menu
 
381
  */
382
  function admin_settings() {
383
  $page = $this->options_admin_page;
@@ -391,36 +650,25 @@ class SLB_Lightbox extends SLB_Base {
391
  $section = $this->get_prefix();
392
  //Section
393
  add_settings_section($section, '<span id="' . $this->admin_get_settings_section() . '">' . __('Lightbox Settings') . '</span>', $this->m('admin_section'), $page);
394
- //Fields
395
- foreach ($this->options_default as $key => $defaults) {
396
- $id = $this->add_prefix($key);
397
- $func = 'admin_field_' . $key;
398
- $label = ( isset($defaults[1]) ) ? $defaults[1] : '';
399
- $callback = ( method_exists($this, $func) ) ? $this->m($func) : $this->m('admin_field_default');
400
- $args = array('opt' => $key);
401
- //Check if option is a section header
402
- if ( ! is_array($defaults) ) {
403
- $label = '<h4 class="subhead">' . $defaults . '</h4>';
404
- $callback = $this->m('admin_field_header');
405
- } elseif ( is_null(get_option($id, null)) ) {
406
- //Add option to DB if not yet set
407
- $args['label_for'] = $id;
408
- update_option($id, htmlentities2($defaults[0]));
409
- }
410
- add_settings_field($id, __($label), $callback, $page, $section, $args);
411
- register_setting($page, $id);
412
- }
413
- }
414
-
415
  function admin_enqueue_files() {
 
416
  if ( is_admin() && basename($_SERVER['SCRIPT_NAME']) == $this->options_admin_page ) {
417
- wp_enqueue_style($this->add_prefix('admin_styles'), $this->util->get_file_url('css/admin.css'));
418
  }
419
  }
420
 
421
  /**
422
  * Get ID of settings section on admin page
423
  * @return string ID of settings section
 
424
  */
425
  function admin_get_settings_section() {
426
  return $this->add_prefix('settings');
@@ -429,67 +677,34 @@ class SLB_Lightbox extends SLB_Base {
429
  /**
430
  * Placeholder function for lightbox admin settings
431
  * Required because setting init function requires a callback
 
432
  */
433
- function admin_section() { }
434
-
435
- /**
436
- * General field builder
437
- * @param string $option Option name to build field for
438
- * @param string $format Field markup (using sprintf specifiers)
439
- * @param string $type (optional) Type of field being build (e.g. checkbox, text, etc.)
440
- * Specifiers:
441
- * 1. Field ID
442
- * 2. Field Value
443
- * 3. Field Default Value (formatted)
444
- * 4. Field Type
445
- */
446
- function admin_the_field($option, $format = '', $type = '') {
447
- $opt = $this->get_option($option);
448
- $format_default = '<input id="%1$s" name="%1$s" %4$s class="code" /> (Default: %3$s)';
449
- if ( empty($format) && $format !== false )
450
- $format = $format_default;
451
- if ( empty($type) || !is_string($type) ) {
452
- $type_default = 'text';
453
- $type = ( is_bool($opt->value_default) ) ? 'checkbox' : $type_default;
454
- }
455
- //Adjust type and value formatting based on type
456
- switch ( $type ) {
457
- case 'checkbox' :
458
- if ( $opt->value )
459
- $opt->attr['checked'] = 'checked';
460
- break;
461
- case 'text' :
462
- if ( $format == $format_default )
463
- $format = str_replace('%4$s', '%4$s value="%2$s"', $format);
464
- break;
465
- }
466
- $opt->attr['type'] = $type;
467
- //Build attribute string
468
- $attr = '';
469
- if ( ! empty($opt->attr) ) {
470
- $attr = $this->util->build_attribute_string($opt->attr);
471
- }
472
-
473
- echo sprintf($format, $opt->id, htmlentities($opt->value), $opt->value_default_formatted, $attr);
474
  }
475
 
476
- /**
477
- * Builds header for settings subsection
478
- * @param array $args Arguments set in admin_settings
479
- */
480
- function admin_field_header($args) {
481
- $opt = ( isset($args['opt']) ) ? $args['opt'] : '';
482
- $this->admin_the_field($opt, false, 'header');
483
- }
484
 
485
- /**
486
- * Default field output generator
487
- * @param array $args Arguments set in admin_settings
488
- */
489
- function admin_field_default($args = array()) {
490
- $opt = ( isset($args['opt']) ) ? $args['opt'] : '';
491
- $this->admin_the_field($opt);
 
 
 
 
 
 
 
 
 
 
 
 
492
  }
493
  }
494
 
495
- ?>
1
  <?php
2
 
3
  require_once 'includes/class.base.php';
4
+ require_once 'includes/class.options.php';
5
 
6
  /**
7
  * Lightbox functionality class
12
 
13
  /*-** Properties **-*/
14
 
15
+ /**
16
+ * Themes
17
+ * @var array
18
+ */
19
+ var $themes = array();
20
+
21
+ var $theme_default = 'default';
22
+
23
  /**
24
  * Page that plugin options are on
25
  * @var string
32
  */
33
  var $options_admin_form = 'options.php';
34
 
35
+ var $attr = null;
36
+
37
+ /**
38
+ * Legacy attribute (for backwards compatibility)
39
+ * @var string
40
+ */
41
+ var $attr_legacy = 'lightbox';
42
+
43
+ /**
44
+ * Properties for media attachments in current request
45
+ * Key (int) Attachment ID
46
+ * Value (assoc-array) Attachment properties (url, etc.)
47
+ * > source: Source URL
48
+ * @var array
49
+ */
50
+ var $media_attachments = array();
51
+
52
  /**
53
+ * Options Configuration
 
 
54
  * @var array
55
  */
56
+ var $options_config = array (
57
+ 'groups' => array (
58
+ 'activation' => 'Activation',
59
+ 'grouping' => 'Grouping',
60
+ 'ui' => 'UI',
61
+ 'labels' => 'Labels'
62
+ ),
63
+ 'items' => array (
64
+ 'enabled' => array('title' => 'Enable Lightbox Functionality', 'default' => true, 'group' => 'activation'),
65
+ 'enabled_home' => array('title' => 'Enable on Home page', 'default' => true, 'group' => 'activation'),
66
+ 'enabled_post' => array('title' => 'Enable on Posts', 'default' => true, 'group' => 'activation'),
67
+ 'enabled_page' => array('title' => 'Enable on Pages', 'default' => true, 'group' => 'activation'),
68
+ 'enabled_archive' => array('title' => 'Enable on Archive Pages (tags, categories, etc.)', 'default' => true, 'group' => 'activation'),
69
+ 'enabled_compat' => array('title' => 'Enable backwards-compatibility with legacy lightbox links', 'default' => false, 'group' => 'activation'),
70
+ 'activate_links' => array('title' => 'Activate all image links in item content', 'default' => true, 'group' => 'activation'),
71
+ 'activate_attachments' => array('title' => 'Activate all image attachment links', 'default' => true, 'group' => 'activation'),
72
+ 'validate_links' => array('title' => 'Validate links', 'default' => false, 'group' => 'activation'),
73
+ 'group_links' => array('title' => 'Group automatically activated links (for displaying as a slideshow)', 'default' => true, 'group' => 'grouping'),
74
+ 'group_post' => array('title' => 'Group image links by Post (e.g. on pages with multiple posts)', 'default' => true, 'group' => 'grouping'),
75
+ 'theme' => array('title' => 'Theme', 'default' => 'default', 'group' => 'ui', 'parent' => 'option_theme'),
76
+ 'animate' => array('title' => 'Animate lightbox resizing', 'default' => true, 'group' => 'ui'),
77
+ 'autostart' => array('title' => 'Automatically Start Slideshow', 'default' => true, 'group' => 'ui'),
78
+ 'duration' => array('title' => 'Slide Duration (Seconds)', 'default' => '6', 'attr' => array('size' => 3, 'maxlength' => 3), 'group' => 'ui'),
79
+ 'loop' => array('title' => 'Loop through images', 'default' => true, 'group' => 'ui'),
80
+ 'overlay_opacity' => array('title' => 'Overlay Opacity (0 - 1)', 'default' => '0.8', 'attr' => array('size' => 3, 'maxlength' => 3), 'group' => 'ui'),
81
+ 'enabled_caption' => array('title' => 'Enable caption', 'default' => true, 'group' => 'ui'),
82
+ 'caption_src' => array('title' => 'Use image URI as caption when link title not set', 'default' => true, 'group' => 'ui'),
83
+ 'enabled_desc' => array('title' => 'Enable description', 'default' => true, 'group' => 'ui'),
84
+ 'txt_closeLink' => array('title' => 'Close link (for accessibility only, image used for button)', 'default' => 'close', 'group' => 'labels'),
85
+ 'txt_loadingMsg' => array('title' => 'Loading indicator', 'default' => 'loading', 'group' => 'labels'),
86
+ 'txt_nextLink' => array('title' => 'Next Image link', 'default' => 'next &raquo;', 'group' => 'labels'),
87
+ 'txt_prevLink' => array('title' => 'Previous Image link', 'default' => '&laquo; prev', 'group' => 'labels'),
88
+ 'txt_startSlideshow' => array('title' => 'Start Slideshow link', 'default' => 'start slideshow', 'group' => 'labels'),
89
+ 'txt_stopSlideshow' => array('title' => 'Stop Slideshow link', 'default' => 'stop slideshow', 'group' => 'labels'),
90
+ 'txt_numDisplayPrefix' => array('title' => 'Image number prefix (e.g. <strong>Image</strong> x of y)', 'default' => 'Image', 'group' => 'labels'),
91
+ 'txt_numDisplaySeparator' => array('title' => 'Image number separator (e.g. Image x <strong>of</strong> y)', 'default' => 'of', 'group' => 'labels')
92
+ ),
93
+ 'legacy' => array (
94
+ 'header_activation' => null,
95
+ 'header_enabled' => null,
96
+ 'header_strings' => null,
97
+ 'header_ui' => null,
98
+ 'enabled_single' => array('enabled_post', 'enabled_page')
99
+ )
100
  );
101
 
102
+ /* Instance members */
103
+
104
+ /**
105
+ * Options instance
106
+ * @var SLB_Options
107
+ */
108
+ var $options = null;
109
+
110
+ /**
111
+ * Base field definitions
112
+ * @var SLB_Fields
113
+ */
114
+ var $fields = null;
115
+
116
  /*-** Init **-*/
117
 
118
  function SLB_Lightbox() {
122
  function __construct() {
123
  parent::__construct();
124
  $this->init();
125
+
126
+ //Setup options
127
+ $opt_theme =& $this->options_config['items']['theme'];
128
+ $opt_theme['default'] = $this->theme_default = $this->add_prefix($this->theme_default);
129
+ $opt_theme['options'] = $this->m('get_theme_options');
130
+
131
+ //Init objects
132
+ $this->attr = $this->get_prefix();
133
+ $this->fields =& new SLB_Fields();
134
+ $this->options =& new SLB_Options('options', $this->options_config);
135
  }
136
 
137
  function register_hooks() {
138
+ parent::register_hooks();
139
+
140
  /* Admin */
141
+
142
  //Init lightbox admin
143
  add_action('admin_init', $this->m('admin_settings'));
144
  //Enqueue header files (CSS/JS)
146
  //Reset Settings
147
  add_action('admin_action_' . $this->add_prefix('reset'), $this->m('admin_reset'));
148
  add_action('admin_notices', $this->m('admin_notices'));
149
+ //Plugin listing
150
+ add_filter('plugin_action_links_' . $this->util->get_plugin_base_name(), $this->m('admin_plugin_action_links'), 10, 4);
151
 
152
  /* Client-side */
153
+
154
+ //Init lightbox
155
  add_action('wp_enqueue_scripts', $this->m('enqueue_files'));
156
  add_action('wp_head', $this->m('client_init'));
157
+ add_action('wp_footer', $this->m('client_footer'), 99);
158
+ add_filter('the_content', $this->m('activate_post_links'), 99);
159
+
160
+ /* Themes */
161
+ $this->util->add_action('init_themes', $this->m('init_default_themes'));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
162
  }
163
+
164
  /*-** Helpers **-*/
165
 
166
  /**
168
  * @return bool TRUE if lightbox is currently enabled, FALSE otherwise
169
  */
170
  function is_enabled($check_request = true) {
171
+ $ret = ( $this->options->get_value('enabled') ) ? true : false;
172
  if ( $ret && $check_request ) {
173
  $opt = '';
174
  //Determine option to check
175
  if ( is_home() )
176
  $opt = 'home';
177
+ elseif ( is_singular() ) {
178
+ $opt = ( is_page() ) ? 'page' : 'post';
179
+ }
180
  elseif ( is_archive() || is_search() )
181
  $opt = 'archive';
182
  //Check option
183
+ if ( !empty($opt) && ( $opt = 'enabled_' . $opt ) && $this->options->has($opt) ) {
184
+ $ret = ( $this->options->get_value($opt) ) ? true : false;
185
  }
186
  }
187
  return $ret;
188
  }
189
 
190
+ /*-** Theme **-*/
191
+
192
  /**
193
+ * Retrieve themes
194
+ * @uses do_action() Calls 'slb_init_themes' hook to allow plugins to register themes
195
+ * @uses $themes to return registered themes
196
+ * @return array Retrieved themes
 
 
 
 
197
  */
198
+ function get_themes() {
199
+ static $fetched = false;
200
+ if ( !$fetched ) {
201
+ $this->themes = array();
202
+ $this->util->do_action('init_themes');
203
+ $fetched = true;
204
+ }
205
+ return $this->themes;
206
  }
207
 
208
  /**
209
+ * Retrieve theme
210
+ * @param string $name Name of theme to retrieve
211
+ * @uses theme_exists() to check for existence of theme
212
+ * @return array Theme data
213
  */
214
+ function get_theme($name = '') {
215
+ $name = strval($name);
216
+ //Default: Get current theme if no theme specified
217
+ if ( empty($name) ) {
218
+ $name = $this->options->get_value('theme');
219
+ }
220
+ if ( !$this->theme_exists($name) )
221
+ $name = $this->theme_default;
222
+ return $this->themes[$name];
223
  }
224
 
225
  /**
226
+ * Retrieve specific of theme data
227
+ * @uses get_theme() to retrieve theme data
228
+ * @param string $name Theme name
229
+ * @param string $field Theme field to retrieve
230
+ * @return mixed Field data
231
  */
232
+ function get_theme_data($name = '', $field) {
233
+ $theme = $this->get_theme($name);
234
+ return ( isset($theme[$field]) ) ? $theme[$field] : '';
 
 
235
  }
236
 
237
  /**
238
+ * Retrieve theme stylesheet URL
239
+ * @param string $name Theme name
240
+ * @uses get_theme_data() to retrieve theme data
241
+ * @return string Stylesheet URL
242
  */
243
+ function get_theme_style($name = '') {
244
+ return $this->get_theme_data($name, 'stylesheet_url');
245
+ }
246
+
247
+ /**
248
+ * Retrieve theme layout
249
+ * @uses get_theme_data() to retrieve theme data
250
+ * @param string $name Theme name
251
+ * @param bool $filter (optional) Filter layout based on user preferences
252
+ * @return string Theme layout HTML
253
+ */
254
+ function get_theme_layout($name = '', $filter = true) {
255
+ $l = $this->get_theme_data($name, 'layout');
256
+ //Filter
257
+ if ( !$this->options->get_value('enabled_caption') )
258
+ $l = str_replace($this->get_theme_placeholder('dataCaption'), '', $l);
259
+ if ( !$this->options->get_value('enabled_desc') )
260
+ $l = str_replace($this->get_theme_placeholder('dataDescription'), '', $l);
261
+ return $l;
262
+ }
263
+
264
+ /**
265
+ * Check whether a theme exists
266
+ * @param string $name Theme to look for
267
+ * @uses get_themes() to intialize themes if not already performed
268
+ * @return bool TRUE if theme exists, FALSE otherwise
269
+ */
270
+ function theme_exists($name) {
271
+ $this->get_themes();
272
+ return ( isset($this->themes[trim(strval($name))]) );
273
+ }
274
+
275
+ /**
276
+ * Register lightbox theme
277
+ * @param string $name Unique theme name
278
+ * @param string $title Display name for theme
279
+ * @param string $stylesheet_url URL to stylesheet
280
+ * @param string $layout Layout HTML
281
+ * @uses $themes to store the registered theme
282
+ */
283
+ function register_theme($name, $title, $stylesheet_url, $layout) {
284
+ if ( !is_array($this->themes) ) {
285
+ $this->themes = array();
286
  }
287
+
288
+ //Validate parameters
289
+ $name = trim(strval($name));
290
+ $title = trim(strval($title));
291
+ $stylesheet_url = trim(strval($stylesheet_url));
292
+ $layout = $this->format_theme_layout($layout);
293
+
294
+ $defaults = array(
295
+ 'name' => '',
296
+ 'title' => '',
297
+ 'stylesheet_url' => '',
298
+ 'layout' => ''
299
+ );
300
+
301
+ //Add theme to array
302
+ $this->themes[$name] = wp_parse_args(compact(array_keys($defaults), $defaults));
303
+ }
304
+
305
+ /**
306
+ * Build theme placeholder
307
+ * @param string $name Placeholder name
308
+ * @return string Placeholder
309
+ */
310
+ function get_theme_placeholder($name) {
311
+ return '{' . $name . '}';
312
+ }
313
+
314
+ /**
315
+ * Formats layout for usage in JS
316
+ * @param string $layout Layout to format
317
+ * @return string Formatted layout
318
+ */
319
+ function format_theme_layout($layout = '') {
320
+ //Remove line breaks
321
+ $layout = str_replace(array("\r\n", "\n", "\r", "\t"), '', $layout);
322
+
323
+ //Escape quotes
324
+ $layout = str_replace("'", "\'", $layout);
325
+
326
+ //Return
327
+ return "'" . $layout . "'";
328
+ }
329
+
330
+ /**
331
+ * Add default themes
332
+ * @uses register_theme() to register the theme(s)
333
+ */
334
+ function init_default_themes() {
335
+ $name = $this->theme_default;
336
+ $title = 'Default';
337
+ $stylesheet_url = $this->util->get_file_url('css/lightbox.css');
338
+ $layout = file_get_contents($this->util->normalize_path($this->util->get_path_base(), 'templates', 'default', 'layout.html'));
339
+ $this->register_theme($name, $title, $stylesheet_url, $layout);
340
+ //Testing: Additional themes
341
+ $this->register_theme('black', 'Black', $this->util->get_file_url('css/lb_black.css'), $layout);
342
  }
343
 
344
  /*-** Frontend **-*/
350
  * @param $content
351
  */
352
  function activate_post_links($content) {
353
+ global $wpdb;
354
  //Check option
355
+ if ( !is_feed() && $this->is_enabled() && $this->options->get_value('activate_links') ) {
356
+ $links = array();
357
+ //Get all links in content
358
+ $rgx = "/\<a[^\>]+href=.*?\>/i";
359
+ preg_match_all($rgx, $content, $links);
360
+ $links = $links[0];
361
+ $domain = str_replace(array('http://', 'https://'), '', get_bloginfo('url'));
362
+ //Process links
363
+ if ( count($links) > 0 ) {
364
  global $post;
365
+ $types = (object) array('img' => 'image', 'att' => 'attachment');
366
+ $img_types = array('jpg', 'jpeg', 'gif', 'png');
367
+ $rgx = "/\b(\w+.*?)=([\"'])(.*?)\\2(?:\s|$)/i";
368
  //Iterate through links & add lightbox if necessary
369
+ foreach ( $links as $link ) {
370
+ $m_props = array();
371
+ $pid = 0;
372
  //Check if rel attribute exists
373
  $link_new = $link;
374
+ //Parse link
375
+ $link_attr = substr($link_new, 2, strlen($link_new) - 3);
376
+ $attr_matches = $attr = array();
377
+ preg_match_all($rgx, $link_attr, $attr_matches);
378
+ foreach ( $attr_matches[1] as $key => $val ) {
379
+ if ( isset($attr_matches[3][$key]) )
380
+ $attr[trim($val)] = trim($attr_matches[3][$key]);
381
+ }
382
+ //Destroy parsing vars
383
+ unset($link_attr, $attr_matches);
384
+
385
+ //Set default attributes
386
+ $attr = array_merge(array('rel' => '', 'href' => ''), $attr);
387
+ $h =& $attr['href'];
388
+ $r =& $attr['rel'];
389
+
390
+
391
+ //Stop processing link if lightbox attribute has already been set
392
+ $lb = $this->attr;
393
+ if ( empty($h) || '#' == $h || ( !empty($r) && ( strpos($r, $lb) !== false || strpos($r, $this->add_prefix('off')) !== false || strpos($r, $this->attr_legacy) !== false ) ) )
394
+ continue;
395
+ //Determine link type
396
+ $type = false;
397
+ if ( $this->util->has_file_extension($h, $img_types) ) {
398
+ $type = $types->img;
399
+ //Check if item links to internal media (attachment)
400
+ if ( strpos($h, $domain) !== false ) {
401
+ $pid_temp = $wpdb->get_var($wpdb->prepare("SELECT post_id FROM $wpdb->postmeta WHERE `meta_key` = %s AND `meta_value` = %s LIMIT 1", '_wp_attached_file', basename($h)));
402
+ if ( is_numeric($pid_temp) )
403
+ $pid = intval($pid_temp);
404
+ }
405
+ }
406
+ elseif ( strpos($h, $domain) !== false && is_local_attachment($h) && ( $pid = url_to_postid($h) ) && wp_attachment_is_image($pid) )
407
+ $type = $types->att;
408
+ if ( !$type ) {
409
+ continue;
410
+ }
411
+
412
+ if ( $type == $types->att && !$this->options->get_value('activate_attachments') )
413
+ continue;
414
+
415
+ //Process link
416
+ if ( empty($r) )
417
+ $r = array();
418
+ else
419
+ $r = array($r);
420
+
421
+ //Check if links should be grouped
422
+ if ( $this->options->get_value('group_links') ) {
423
+ $group = ( $this->options->get_value('group_post') ) ? $this->add_prefix($post->ID) : $this->get_prefix();
424
+ $lb .= '[' . $group . ']';
425
  }
426
+ $r[] = $lb;
427
 
428
+ //Load properties for attachments
429
+ if ( !!$pid ) {
430
+ if ( !isset($this->media_attachments[$pid]) ) {
431
+ switch ($type) {
432
+ case $types->img:
433
+ $m_props['source'] = $h;
434
+ break;
435
+
436
+ case $types->att:
437
+ //Source URL
438
+ $m_props['source'] = wp_get_attachment_url($pid);
439
+ break;
440
+ }
441
+
442
+ //Retrieve attachment data
443
+ if ( $this->options->get_value('enabled_desc') ) {
444
+ $m_props['p'] = get_post($pid);
445
+ //Description
446
+ $m_props['desc'] = $m_props['p']->post_content;
447
+ //Clear attachment data
448
+ unset($m_props['p']);
449
+ }
450
+
451
+ //Add attachment properties
452
+ if ( !empty($m_props['source']) )
453
+ $this->media_attachments[$pid] = $m_props;
454
+ }
455
+
456
+ //Check again if attachment ID exists (in case it was just added to array)
457
+ if ( isset($this->media_attachments[$pid]) )
458
+ $r[] = $this->add_prefix('id[' . $pid . ']');
459
  }
460
+
461
+
462
+ //Convert rel attribute to string
463
+ $r = implode(' ', $r);
464
+
465
+ $link_new = '<a ' . $this->util->build_attribute_string($attr) . '>';
466
+ //Insert modified link
467
+ $content = str_replace($link, $link_new, $content);
468
+ unset($h, $r);
469
  }
470
  }
471
  }
472
+ return $content;
473
  }
474
 
475
  /**
478
  function enqueue_files() {
479
  if ( ! $this->is_enabled() )
480
  return;
481
+
482
+ $lib = 'js/' . ( ( WP_DEBUG ) ? 'dev/lib.dev.js' : 'lib.js' );
483
+ wp_enqueue_script($this->add_prefix('lib'), $this->util->get_file_url($lib), array('jquery'), $this->util->get_plugin_version());
484
+ wp_enqueue_style($this->add_prefix('style'), $this->get_theme_style(), array(), $this->util->get_plugin_version());
485
+ }
486
+
487
+ /**
488
+ * Build client (JS) object name
489
+ * @return string Name of JS object
490
+ */
491
+ function get_client_obj() {
492
+ static $obj = null;
493
+ if ( is_null($obj) )
494
+ $obj = strtoupper($this->get_prefix(''));
495
+ return $obj;
496
  }
497
 
498
  /**
505
 
506
  $options = array();
507
  $out = array();
508
+ $out['script_start'] = '(function($){$(document).ready(function(){';
509
+ $out['script_end'] = '})})(jQuery);';
510
  $js_code = array();
 
 
 
 
 
 
 
 
 
511
  //Get options
512
  $options = array(
513
+ 'validateLinks' => $this->options->get_value('validate_links'),
514
+ 'autoPlay' => $this->options->get_value('autostart'),
515
+ 'slideTime' => $this->options->get_value('duration'),
516
+ 'loop' => $this->options->get_value('loop'),
517
+ 'overlayOpacity' => $this->options->get_value('overlay_opacity'),
518
+ 'animate' => $this->options->get_value('animate'),
519
+ 'captionEnabled' => $this->options->get_value('enabled_caption'),
520
+ 'captionSrc' => $this->options->get_value('caption_src'),
521
+ 'descEnabled' => $this->options->get_value('enabled_desc'),
522
+ 'layout' => $this->get_theme_layout(),
523
+ 'altsrc' => $this->add_prefix('src'),
524
+ 'relAttribute' => array($this->get_prefix()),
525
+ 'prefix' => $this->get_prefix()
526
  );
527
+ //Backwards compatibility
528
+ if ( $this->options->get_value('enabled_compat'))
529
+ $options['relAttribute'][] = $this->attr_legacy;
530
+
 
 
 
 
531
  //Load UI Strings
532
  if ( ($strings = $this->build_strings()) && !empty($strings) )
533
+ $options['strings'] = $strings;
534
+ $js_code[] = $this->get_client_obj() . '.initialize(' . json_encode($options) . ');';
535
+ $js_out = $out['script_start'] . implode('', $js_code) . $out['script_end'];
536
+ echo $this->util->build_script_element($js_out, $this->add_prefix('init'));
537
+ }
538
+
539
+ /**
540
+ * Output code in footer
541
+ * > Media attachment URLs
542
+ */
543
+ function client_footer() {
544
+ if ( !$this->is_enabled() )
545
+ return;
546
+ //Media attachments
547
+ if ( !empty($this->media_attachments) ) {
548
+ $atch_out = $this->get_client_obj() . '.media = ' . json_encode($this->media_attachments) . ';';
549
+ echo $this->util->build_script_element($atch_out, $this->add_prefix('media'));
550
+ }
551
  }
552
 
553
  /**
554
  * Build JS object of UI strings when initializing lightbox
555
+ * @return array UI strings
556
  */
557
  function build_strings() {
558
+ $ret = array();
559
+ //Get all UI options
560
  $prefix = 'txt_';
561
+ $opt_strings = array_filter(array_keys($this->options->get_items()), create_function('$opt', 'return ( strpos($opt, "' . $prefix . '") === 0 );'));
562
+ if ( count($opt_strings) ) {
563
+ //Build array of UI options
564
  foreach ( $opt_strings as $key ) {
565
  $name = substr($key, strlen($prefix));
566
+ $ret[$name] = $this->options->get_value($key);
567
  }
 
568
  }
569
  return $ret;
570
  }
603
  $action = 'reset';
604
  if ( isset($_REQUEST['action']) && $this->add_prefix($action) == $_REQUEST['action'] ) {
605
  //Reset settings
606
+ $this->options->reset(true);
607
  $uri = remove_query_arg(array('_wpnonce', 'action'), add_query_arg(array($this->add_prefix('action') => $action), $_SERVER['REQUEST_URI']));
608
  //Redirect user
609
  wp_redirect($uri);
636
  /**
637
  * Adds settings section for Lightbox functionality
638
  * Section is added to Settings > Media Admin menu
639
+ * @todo Move appropriate code to options class
640
  */
641
  function admin_settings() {
642
  $page = $this->options_admin_page;
650
  $section = $this->get_prefix();
651
  //Section
652
  add_settings_section($section, '<span id="' . $this->admin_get_settings_section() . '">' . __('Lightbox Settings') . '</span>', $this->m('admin_section'), $page);
653
+ //Register settings container
654
+ register_setting($page, $this->add_prefix('options'), $this->options->m('validate'));
655
+ }
656
+
657
+ /**
658
+ * Enqueues header files for admin pages
659
+ * @todo Separate and move options CSS to options class
660
+ */
 
 
 
 
 
 
 
 
 
 
 
 
 
661
  function admin_enqueue_files() {
662
+ //Enqueue custom CSS for options page
663
  if ( is_admin() && basename($_SERVER['SCRIPT_NAME']) == $this->options_admin_page ) {
664
+ wp_enqueue_style($this->add_prefix('admin'), $this->util->get_file_url('css/admin.css'), array(), $this->util->get_plugin_version());
665
  }
666
  }
667
 
668
  /**
669
  * Get ID of settings section on admin page
670
  * @return string ID of settings section
671
+ * @todo Eval for moving to options class
672
  */
673
  function admin_get_settings_section() {
674
  return $this->add_prefix('settings');
677
  /**
678
  * Placeholder function for lightbox admin settings
679
  * Required because setting init function requires a callback
680
+ * @todo Evaluate for moving to options class
681
  */
682
+ function admin_section() {
683
+ $this->options->build();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
684
  }
685
 
686
+ /* Custom fields */
 
 
 
 
 
 
 
687
 
688
+ function get_theme_options() {
689
+ //Get themes
690
+ $themes = $this->get_themes();
691
+
692
+ //Pop out default theme
693
+ $theme_default = $themes[$this->theme_default];
694
+ unset($themes[$this->theme_default]);
695
+
696
+ //Sort themes by title
697
+ uasort($themes, create_function('$a,$b', 'return strcmp($a[\'title\'], $b[\'title\']);'));
698
+
699
+ //Insert default theme at top of array
700
+ $themes = array($this->theme_default => $theme_default) + $themes;
701
+
702
+ //Build options
703
+ foreach ( $themes as $name => $props ) {
704
+ $themes[$name] = $props['title'];
705
+ }
706
+ return $themes;
707
  }
708
  }
709
 
710
+ ?>
readme.txt CHANGED
@@ -1,40 +1,42 @@
1
- === Plugin Name ===
2
- Contributors: archetyped
3
- Tags: lightbox, gallery, photography, images
4
- Requires at least: 2.9.2
5
- Tested up to: 3.0
 
6
  Stable tag: trunk
7
 
8
- A simple and customizable Lightbox for Wordpress
9
 
10
  == Description ==
 
11
 
12
- Simple Lightbox is a very simple and customizable lightbox that is easy to add to your Wordpress website
13
-
14
- #### Customization
15
  Options for customizing the lightbox behavior are located in the **Settings > Media** admin menu in the **Lightbox Settings** section (or just click the **Settings** link below the plugin's name when viewing the list of installed plugins)
16
 
17
- * Customizable UI Text
18
- * Enable/Disable Lightbox Functionality (Default: Enabled)
 
 
 
 
19
  * Enable Lightbox depending on Page Type (Home, Pages, Archive, etc.)
20
- * Automatically activate lightbox for links to images on page (no need to add `rel="lightbox"` attribute to link)
21
- * Group automatically-activated links (play as a slideshow)
22
- * Group image links by Post (separate slideshow for each Post on page)
23
- * Automatically Start Slideshow (Default: Enabled)
24
- * Slide Duration (Seconds) (Default: 6)
25
- * Loop through images (Default: Enabled)
26
- * Overlay Opacity (0 - 1) (Default: 0.8)
27
 
28
  #### Usage
29
- * The necessary Javascript and CSS files will be automatically added to the page as long as `wp_head()` is called in the theme
30
- * That's it!
 
 
 
31
 
32
  == Installation ==
33
 
34
- 1. Upload `simple-lightbox` folder to the `/wp-content/plugins/` directory
35
- 1. Activate the plugin through the 'Plugins' menu in WordPress
36
- 1. Verify that your theme uses the `wp_head()` template tag (this is where the necessary files will be added to your theme output)
37
- 1. Let plugin automatically add lightbox functionality for links to images or manually add `rel="lightbox"` to any image links that you want to be displayed in a lightbox
38
 
39
  == Upgrade Notice ==
40
 
@@ -42,7 +44,7 @@ No upgrade notices
42
 
43
  == Frequently Asked Questions ==
44
 
45
- Send your questions to wp@archetyped.com
46
 
47
  == Screenshots ==
48
 
@@ -50,13 +52,93 @@ Send your questions to wp@archetyped.com
50
  2. Customized UI Text
51
 
52
  == Changelog ==
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  = 1.3.1 =
54
- * Updated: Utilities code (internal)
 
55
  = 1.3 =
56
- * Added: Customizable UI label text (close, next, and prev button images can be replaced in `images` directory)
57
- * Added: Group image links by Post (separate slideshow for each post)
58
- * Added: Reset settings link on plugin listings page
59
- * Optimized: Organized settings page
60
 
61
  = 1.2.1 =
62
  * Fixed: Image title given higher precedence than Image alt (more compatible w/WP workflow)
@@ -72,5 +154,6 @@ Send your questions to wp@archetyped.com
72
  * Optimized: Options menu field building
73
  * Optimized: Loading of default values for plugin options
74
  * Optimized: General code optimizations
 
75
  = 1.0 =
76
  * Initial release
1
+ === Simple Lightbox ===
2
+ Contributors: Archetyped
3
+ Donate link: http://archetyped.com/tools/simple-lightbox/#donate
4
+ Tags: lightbox, gallery, photography, images, theme, template, style
5
+ Requires at least: 3.2.1
6
+ Tested up to: 3.2.1
7
  Stable tag: trunk
8
 
9
+ A simple, themeable, and customizable Lightbox for Wordpress
10
 
11
  == Description ==
12
+ Simple Lightbox is a very simple and customizable lightbox that is easy to add to your Wordpress website. It also [supports skins](http://archetyped.com/lab/slb-registering-themes/), so it can be fully integrated with your site's theme.
13
 
14
+ #### Features
 
 
15
  Options for customizing the lightbox behavior are located in the **Settings > Media** admin menu in the **Lightbox Settings** section (or just click the **Settings** link below the plugin's name when viewing the list of installed plugins)
16
 
17
+ * **Supports Image Attachment links**
18
+ * **Display image metadata (caption, description, etc.) in lightbox**
19
+ * Automatically activate links (no manual coding required)
20
+ * Keyboard Navigation
21
+ * Link Validation (optional)
22
+ * User-customizable skin (template) support
23
  * Enable Lightbox depending on Page Type (Home, Pages, Archive, etc.)
24
+ * Group image links (play as a slideshow)
25
+ * Group image links by Post (e.g. separate slideshow for each post on home page)
26
+ * UI/Animation Customization
27
+ * Slideshow Customization
28
+ * Backwards-compatibility with legacy lightbox links
 
 
29
 
30
  #### Usage
31
+ 1. Insert links to images/image attachments into your posts/pages
32
+
33
+ **That's it! The image will be displayed in a lightbox automatically.**
34
+
35
+ * For more usage tips, go to [Simple Lightbox's official page](http://archetyped.com/tools/simple-lightbox/)
36
 
37
  == Installation ==
38
 
39
+ 1. Install and activate via admin dashboard
 
 
 
40
 
41
  == Upgrade Notice ==
42
 
44
 
45
  == Frequently Asked Questions ==
46
 
47
+ Post your questions and comments on [Simple Lightbox's official page](http://archetyped.com/tools/simple-lightbox/)
48
 
49
  == Screenshots ==
50
 
52
  2. Customized UI Text
53
 
54
  == Changelog ==
55
+
56
+ = 1.5.6 =
57
+ * Add: Display image description in lightbox (with HTML support)
58
+ * Add: Support for W3 Total Cache plugin
59
+ * Add: Initial support for NextGEN galleries
60
+ * Update: **Important:** [System Requirements](http://wordpress.org/about/requirements/) aligned with WP 3.2.1
61
+ * Optimize: Improved support for small images in default template
62
+ * Optimize: Support for non-English text in user options
63
+ * Optimize: Improved IE compatibility
64
+ * Optimize: Improved data handling
65
+ * Optimize: Skin loading performance
66
+ * Optimize: Skin CSS Cleanup
67
+ * Optimize: Caption support for galleries
68
+ * Optimize: Options code cleanup (Juga Sweep)
69
+ * Fix: User-defined UI text not used (Ivan gets Even (cooler))
70
+ * Fix: Options reset after update (KRazy Donna)
71
+
72
+ = 1.5.5.1 =
73
+ * Fix: Disabled links not being disabled (Disabling Sascha)
74
+
75
+ = 1.5.5 =
76
+ * Add: Distinct link activation (will not affect other lightboxes)
77
+ * Add: Backwards compatibility with legacy lightbox links (optional)
78
+ * Add: Support for WordPress 3.2
79
+ * Add: Support for links added after page load (e.g. via AJAX, etc.)
80
+ * Add: Admin option to enable/disable attachment links
81
+ * Add: Support for image attachment links
82
+ * Update: Options management overhaul
83
+ * Update: Additional WordPress 3.2 support (Gallery)
84
+ * Update: Cache-management for enqueued files
85
+ * Update: Improved UI consistency
86
+ * Update: Improved compatibility for older versions of PHP
87
+ * Update: Internal optimizations
88
+ * Update: Improved URL handling
89
+ * Fix: Improved options migration from old versions (Hutchison Migration)
90
+ * Fix: XHTML Validation (Hajo Validation)
91
+
92
+ = 1.5.4 =
93
+ * Add: Optional Link validation
94
+ * Add: Keyboard Navigation
95
+ * Add: Option to enable/disable image caption
96
+ * Add: `rel` attribute supported again
97
+ * Add: Use `slb_off` in link's `rel` attribute to disable automatic activation for link
98
+ * Fix: HTTPS compatibility (J&uuml;rgen Protocol)
99
+ * Fix: Enabling SLB on Pages issue
100
+ * Fix: Zmanu is_single
101
+ * Fix: Image order is sometimes incorrect
102
+ * Optimize: Filter double clicks
103
+ * Optimize: Separate options to enable/disable SLB on Posts and Pages
104
+ * Optimize: Better grouping support
105
+
106
+ = 1.5.3 =
107
+ * Fix: Caption may not display under certain circumstances (Caption Erin)
108
+ * Fix: Images not grouped when "separate by post" option is activated (Logical Ross)
109
+ * Update: Lightbox will not be activated for links that already have `rel` attribute set
110
+
111
+ = 1.5.2 =
112
+ * Fix: Slideshow loops out of control (Mirage of Wallentin)
113
+ * Fix: Lightbox fails when group by posts disabled (Lange Find)
114
+ * Add: Option to use the image's URI as caption when link title not set (Under UI options)
115
+
116
+ = 1.5.1 =
117
+ * Add: WP Gallery support
118
+ * Fix: Navigation hidden when only one image
119
+ * Fix: Use user-defined UI text
120
+
121
+ = 1.5 =
122
+ * Add: Theme support
123
+ * Optimize: Javascript cleanup and file size reductions
124
+ * Optimize: CSS cleanup
125
+
126
+ = 1.4 =
127
+ * Update: Integrated with jQuery
128
+ * Optimize: Javascript filesize 9x smaller
129
+ * Add: Close lightbox by clicking to left/right outside of image (an oft-requested feature)
130
+
131
+ = 1.3.2 =
132
+ * Add: Option to enable/disable lightbox resizing animation (thanks Maria!)
133
+
134
  = 1.3.1 =
135
+ * Update: Utilities code (internal)
136
+
137
  = 1.3 =
138
+ * Add: Customizable UI label text (close, next, and prev button images can be replaced in `images` directory)
139
+ * Add: Group image links by Post (separate slideshow for each post)
140
+ * Add: Reset settings link on plugin listings page
141
+ * Optimize: Organized settings page
142
 
143
  = 1.2.1 =
144
  * Fixed: Image title given higher precedence than Image alt (more compatible w/WP workflow)
154
  * Optimized: Options menu field building
155
  * Optimized: Loading of default values for plugin options
156
  * Optimized: General code optimizations
157
+
158
  = 1.0 =
159
  * Initial release
templates/default/layout.html ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div id="slb_container">
2
+ <div id="slb_content">
3
+ {slbContent}
4
+ <div id="slb_nav_hover">
5
+ {navPrev}
6
+ {navNext}
7
+ </div>
8
+ <div id="slb_loading">
9
+ {slbLoading}
10
+ </div>
11
+ </div>
12
+ </div>
13
+ <div id="slb_details">
14
+ <div id="slb_data">
15
+ <div id="slb_data_content">
16
+ {dataCaption}
17
+ <span id="slb_data_desc">
18
+ {dataDescription}
19
+ </span>
20
+ {dataNumber}
21
+ <span id="slb_nav">
22
+ {navPrev}
23
+ {navNext}
24
+ {navSlideControl}
25
+ </span>
26
+ </div>
27
+ <div id="slb_close">
28
+ {slbClose}
29
+ </div>
30
+ </div>
31
+ </div>