diff options
Diffstat (limited to 'docroot/classes')
-rwxr-xr-x | docroot/classes/application.js | 227 | ||||
-rwxr-xr-x | docroot/classes/bezel.class.js | 154 | ||||
-rwxr-xr-x | docroot/classes/card.class.js | 378 | ||||
-rwxr-xr-x | docroot/classes/chip.class.js | 269 | ||||
-rwxr-xr-x | docroot/classes/cookie.class.js | 70 | ||||
-rwxr-xr-x | docroot/classes/decoder.module.js | 205 | ||||
-rwxr-xr-x | docroot/classes/history.class.js | 208 | ||||
-rwxr-xr-x | docroot/classes/layout.class.js | 51 | ||||
-rwxr-xr-x | docroot/classes/layouts/layout.custom.class.js | 219 | ||||
-rwxr-xr-x | docroot/classes/layouts/layout.error.class.js | 33 | ||||
-rwxr-xr-x | docroot/classes/layouts/layout.primary.class.js | 411 | ||||
-rwxr-xr-x | docroot/classes/layouts/layout.special.class.js | 43 | ||||
-rwxr-xr-x | docroot/classes/overlay.class.js | 118 | ||||
-rwxr-xr-x | docroot/classes/roundcorners.class.js | 60 | ||||
-rwxr-xr-x | docroot/classes/sketchbook.class.js | 288 | ||||
-rwxr-xr-x | docroot/classes/sme.namespace.js | 140 | ||||
-rwxr-xr-x | docroot/classes/table.class.js | 315 | ||||
-rwxr-xr-x | docroot/classes/utility.js | 199 |
18 files changed, 3388 insertions, 0 deletions
diff --git a/docroot/classes/application.js b/docroot/classes/application.js new file mode 100755 index 0000000..d8776d8 --- /dev/null +++ b/docroot/classes/application.js | |||
@@ -0,0 +1,227 @@ | |||
1 | /* | ||
2 | * Material Experience - Main Application Code | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | * | ||
8 | * Core application code that is responsible for starting up the application | ||
9 | * and initializing the core objects. | ||
10 | */ | ||
11 | |||
12 | /* | ||
13 | * Register global actions for AJAX responders. | ||
14 | */ | ||
15 | Ajax.Responders.register( | ||
16 | { | ||
17 | /* | ||
18 | * When an AJAX connection is created show the bezel that says | ||
19 | * "loading data". | ||
20 | */ | ||
21 | onCreate: function() | ||
22 | { | ||
23 | if (!SME.AJAXBezel) | ||
24 | { | ||
25 | SME.AJAXBezel = new Bezel({ displayTime: 0, destroy: false }).show(Strings.loadingAnim); | ||
26 | } | ||
27 | else | ||
28 | { | ||
29 | SME.AJAXBezel.show(Strings.loadingAnim); | ||
30 | } | ||
31 | }, | ||
32 | |||
33 | /* | ||
34 | * Each time a requester completes we check to see if it was the last | ||
35 | * one, if it is then we take down the loading bezel. | ||
36 | */ | ||
37 | onComplete: function() | ||
38 | { | ||
39 | if (Ajax.activeRequestCount == 0) | ||
40 | { | ||
41 | SME.AJAXBezel.hide(); | ||
42 | } | ||
43 | }, | ||
44 | |||
45 | /* | ||
46 | * When something goes wrong with loading data we die. | ||
47 | */ | ||
48 | onException: function(transport, exception) | ||
49 | { | ||
50 | if (SME.debug) | ||
51 | { | ||
52 | console.error(exception); | ||
53 | } | ||
54 | |||
55 | SME.AJAXBezel.hide(); | ||
56 | new Bezel({ displayTime: 0 }).show(Strings.ajaxError); | ||
57 | } | ||
58 | }); | ||
59 | |||
60 | /* | ||
61 | * Load the data for the card tables. | ||
62 | */ | ||
63 | function loadTables() | ||
64 | { | ||
65 | new Ajax.Request(SME.url.tableList, | ||
66 | { | ||
67 | method: "get", | ||
68 | |||
69 | onSuccess: function(transport) | ||
70 | { | ||
71 | transport.responseText.evalJSON().each(function(data) | ||
72 | { | ||
73 | var windowDims = window.getDimensions(); | ||
74 | |||
75 | var table = new CardTable( | ||
76 | { | ||
77 | color: data.color, | ||
78 | name: data.name, | ||
79 | id: data.tid, | ||
80 | decorate: data.decorate | ||
81 | }); | ||
82 | |||
83 | // Push the table onto the global table cache (see the SME namespace | ||
84 | // for more information about the global table cache) | ||
85 | SME.tables.push(table); | ||
86 | |||
87 | // Subtracting the max chip width and height ensures that cards don't | ||
88 | // fall too far off the tables | ||
89 | table.loadChipData(SME.url.cardTables, | ||
90 | { | ||
91 | table: data.tid, | ||
92 | w: windowDims.width - (SME.sizes.chipMax.width / 2), | ||
93 | h: windowDims.height - (SME.sizes.chipMax.height / 2) | ||
94 | }, false); | ||
95 | }); | ||
96 | |||
97 | // Initialize the sketchbook | ||
98 | SME.sketchbook = new Sketchbook(); | ||
99 | |||
100 | // By default show the home table. When the history manager loads for the | ||
101 | // first time (after this step) it will load the right table from the URL | ||
102 | // if applicable. This just ensures that a table is always displayed. | ||
103 | CardTable.showTable("home"); | ||
104 | |||
105 | // Show the tool box in the upper right | ||
106 | showToolBox(); | ||
107 | } | ||
108 | }); | ||
109 | |||
110 | // Start up the history manager | ||
111 | SME.history = new HistoryManager().pollEvents(); | ||
112 | } | ||
113 | |||
114 | /* | ||
115 | * Show an intro card. This function will gracefully pass if there are no | ||
116 | * intro cards to be shown. | ||
117 | */ | ||
118 | function showIntro() | ||
119 | { | ||
120 | new Ajax.Request(SME.url.introCards, | ||
121 | { | ||
122 | method: "get", | ||
123 | |||
124 | onSuccess: function(transport) | ||
125 | { | ||
126 | var data = transport.responseText.cleanJSON(); | ||
127 | |||
128 | // If there is no intro card then just pass | ||
129 | if (data == "" || data == "\n") | ||
130 | { | ||
131 | return loadTables(); | ||
132 | } | ||
133 | else | ||
134 | { | ||
135 | data = data.evalJSON(); | ||
136 | |||
137 | // By default just show the first card in the | ||
138 | // feed | ||
139 | var myCard = data[0]; | ||
140 | |||
141 | // If more than one card then pick one at random | ||
142 | // to display (per client requirements). | ||
143 | if (data.length > 1) | ||
144 | { | ||
145 | myCard = data[Math.floor(1 + (data.length - 1) * Math.random())]; | ||
146 | } | ||
147 | } | ||
148 | |||
149 | var card = new Card( | ||
150 | { | ||
151 | color: SME.colors.grey, | ||
152 | title: '', | ||
153 | addExtraButtons: false, | ||
154 | contID: myCard, | ||
155 | |||
156 | onFadeComplete: function() | ||
157 | { | ||
158 | loadTables(); | ||
159 | } | ||
160 | }).show(); | ||
161 | } | ||
162 | }); | ||
163 | } | ||
164 | |||
165 | /* | ||
166 | * Show the toolbox in the upper right side of the screen. | ||
167 | */ | ||
168 | function showToolBox() | ||
169 | { | ||
170 | new Effect.Appear($$("div#header div.history")[0]); | ||
171 | |||
172 | // On mouseover of the history link show the dropdown | ||
173 | $$("div.history a.history")[0].observe("mouseover", function() | ||
174 | { | ||
175 | SME.history.getDropDown(); | ||
176 | }); | ||
177 | |||
178 | // Show the login screen when the login link is clicked | ||
179 | $("loginLink").observe("click", Sketchbook.showLoginScreen); | ||
180 | |||
181 | // Check the login when they first hit the page, saves people logging | ||
182 | // in again | ||
183 | Sketchbook.checkLogin(); | ||
184 | } | ||
185 | |||
186 | /* | ||
187 | * Main program function, this starts up the interface and does various | ||
188 | * little fixups of interface elements. | ||
189 | */ | ||
190 | function main() | ||
191 | { | ||
192 | if (SME.debug) | ||
193 | { | ||
194 | new Bezel({ displayTime: 5 }).show("Full Debug Mode is Enabled"); | ||
195 | } | ||
196 | |||
197 | // Check the resolution at load and when the screen size changes | ||
198 | window.checkResolution(); | ||
199 | Event.observe(window, "resize", window.checkResolution); | ||
200 | |||
201 | // Show the intro card or load the tables | ||
202 | if (SME.skipIntro || window.location.hash.length > 1) | ||
203 | { | ||
204 | loadTables(); | ||
205 | } | ||
206 | else | ||
207 | { | ||
208 | showIntro(); | ||
209 | } | ||
210 | |||
211 | // Per Bryan this should be a single year if 2007 otherwise it should | ||
212 | // be a date range starting on the year that the site was released. | ||
213 | if (new Date().getFullYear() > 2007) | ||
214 | { | ||
215 | $("copyright").innerHTML = $("copyright").innerHTML.replace(/####/, "2007-" + | ||
216 | new Date().getFullYear()); | ||
217 | } | ||
218 | else | ||
219 | { | ||
220 | $("copyright").innerHTML = $("copyright").innerHTML.replace(/####/, "2007"); | ||
221 | } | ||
222 | } | ||
223 | |||
224 | /* | ||
225 | * Start the program up when the window loads. | ||
226 | */ | ||
227 | Event.observe(window, "load", main); \ No newline at end of file | ||
diff --git a/docroot/classes/bezel.class.js b/docroot/classes/bezel.class.js new file mode 100755 index 0000000..d29ed4f --- /dev/null +++ b/docroot/classes/bezel.class.js | |||
@@ -0,0 +1,154 @@ | |||
1 | /* | ||
2 | * Material Experience - Bezel Class | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | * | ||
8 | * Notification bezel class mimics the notification bezel in BBEdit | ||
9 | * (and to a lesser extent, OS X). | ||
10 | */ | ||
11 | |||
12 | var Bezel = Class.create(); | ||
13 | Object.extend(Bezel.prototype, | ||
14 | { | ||
15 | /* | ||
16 | * Creates the bezel HTML elements and adds them to the document body. | ||
17 | */ | ||
18 | initialize: function() | ||
19 | { | ||
20 | this.options = Object.extend( | ||
21 | { | ||
22 | background: "black", // Background color of the bezel | ||
23 | opacity: 0.8, // % Opacity of the bezel | ||
24 | fontSize: "2em", // Font Size | ||
25 | fadeTime: 2, // Fade Time | ||
26 | destroy: true, // Destroy bezel on fade out | ||
27 | displayTime: 1, // How long to display the bezel - 0 is "sticky" | ||
28 | onShow: Prototype.emptyFunction, // After show callback | ||
29 | afterHide: Prototype.emptyFunction // After hide callback | ||
30 | }, arguments[0] || {}); | ||
31 | |||
32 | this.visible = false; | ||
33 | |||
34 | var Rounder = new RoundedCorners(this.options.background); | ||
35 | |||
36 | this.bezel = Element.extend(document.createElement("div")); | ||
37 | this.message = Element.extend(document.createElement("div")); | ||
38 | |||
39 | this.bezel.appendChild(Rounder.get(Rounder.directions.top)); | ||
40 | this.bezel.appendChild(this.message); | ||
41 | this.bezel.appendChild(Rounder.get(Rounder.directions.bottom)); | ||
42 | |||
43 | document.body.appendChild(this.bezel); | ||
44 | |||
45 | this.bezel.setStyle( | ||
46 | { | ||
47 | opacity: this.options.opacity, | ||
48 | zIndex: 9999999, | ||
49 | position: "absolute", | ||
50 | display: "none", | ||
51 | width: "auto", | ||
52 | cursor: "pointer" | ||
53 | }); | ||
54 | |||
55 | this.message.setStyle( | ||
56 | { | ||
57 | background: this.options.background, | ||
58 | fontSize: this.options.fontSize, | ||
59 | color: "white", | ||
60 | padding: "0px 1em", | ||
61 | textAlign: "center" | ||
62 | }); | ||
63 | |||
64 | // IE does not properly size the bezel so we must constrain it | ||
65 | if (Prototype.Browser.IE) | ||
66 | { | ||
67 | this.bezel.setStyle({ width: "50%" }); | ||
68 | } | ||
69 | |||
70 | this.bezel.onclick = function() | ||
71 | { | ||
72 | this.hide(); | ||
73 | }.bind(this); | ||
74 | }, | ||
75 | |||
76 | /* | ||
77 | * Displays the notification bezel with the requested message. | ||
78 | */ | ||
79 | show: function(message) | ||
80 | { | ||
81 | // Sets the bezel message | ||
82 | this.message.innerHTML = message; | ||
83 | this.visible = true; | ||
84 | |||
85 | // Center the bezel | ||
86 | var mysize = this.bezel.getDimensions(); | ||
87 | mysize = window.calcCordsToCenter(mysize.height, mysize.width); | ||
88 | |||
89 | // Set the position of the bezel | ||
90 | this.bezel.setStyle( | ||
91 | { | ||
92 | top: mysize.top + "px", | ||
93 | left: mysize.left + "px" | ||
94 | }); | ||
95 | |||
96 | this.bezel.show(); | ||
97 | |||
98 | if (typeof this.options.onShow == "function") | ||
99 | { | ||
100 | this.options.onShow(); | ||
101 | } | ||
102 | |||
103 | if (this.options.displayTime > 0) | ||
104 | { | ||
105 | new PeriodicalExecuter(function(executer) | ||
106 | { | ||
107 | this.hide(); | ||
108 | executer.stop(); | ||
109 | }.bind(this), this.options.displayTime); | ||
110 | } | ||
111 | |||
112 | return this; | ||
113 | }, | ||
114 | |||
115 | /* | ||
116 | * Removes the bezel from the DOM. | ||
117 | */ | ||
118 | destroy: function() | ||
119 | { | ||
120 | try | ||
121 | { | ||
122 | document.body.removeChild(this.bezel); | ||
123 | } | ||
124 | catch (e) | ||
125 | { | ||
126 | // Ignore errors | ||
127 | } | ||
128 | }, | ||
129 | |||
130 | /* | ||
131 | * Fades out the notification bezel. If this is not a sticky bezel | ||
132 | * (displayTime > 0) then this function will be called automatically | ||
133 | * by the show routine. | ||
134 | */ | ||
135 | hide: function() | ||
136 | { | ||
137 | new Effect.Fade(this.bezel, | ||
138 | { | ||
139 | duration: this.options.fadeTime, | ||
140 | |||
141 | afterFinish: function() | ||
142 | { | ||
143 | this.visible = false; | ||
144 | |||
145 | if (this.options.destroy) | ||
146 | { | ||
147 | this.destroy(); | ||
148 | } | ||
149 | |||
150 | this.options.afterHide(this); | ||
151 | }.bind(this) | ||
152 | }); | ||
153 | } | ||
154 | }); \ No newline at end of file | ||
diff --git a/docroot/classes/card.class.js b/docroot/classes/card.class.js new file mode 100755 index 0000000..e135259 --- /dev/null +++ b/docroot/classes/card.class.js | |||
@@ -0,0 +1,378 @@ | |||
1 | /* | ||
2 | * Material Experience - Card Class | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | */ | ||
8 | |||
9 | var Card = Class.create(); | ||
10 | Object.extend(Card.prototype, | ||
11 | { | ||
12 | /* | ||
13 | * Creates the card and appends it to the document. This does | ||
14 | * not set the content. | ||
15 | */ | ||
16 | initialize: function() | ||
17 | { | ||
18 | this.options = Object.extend( | ||
19 | { | ||
20 | title: 'New Card', // Card Title | ||
21 | id: 'mycard', // Card ID | ||
22 | preview: false, // This is a preview card | ||
23 | addExtraButtons: true, // Add the extra buttons to non-system cards | ||
24 | contID: null, // Content ID of the card's contents | ||
25 | color: SME.colors.blue, // Card Color | ||
26 | width: SME.sizes.card.width, // Card Width | ||
27 | height: SME.sizes.card.height, // Card Height | ||
28 | onClose: Prototype.emptyFunction, // Card Close Callback | ||
29 | onFadeComplete: Prototype.emptyFunction, // Card Fade Completion Callback | ||
30 | onFadeOutFinish: Prototype.emptyFunction // Card Fade Out Completion Callback | ||
31 | }, arguments[0] || {}); | ||
32 | |||
33 | // Keep track of our card chip | ||
34 | this.chip = this.options.chip; | ||
35 | |||
36 | // Buttons on the card frame | ||
37 | this.buttons = []; | ||
38 | |||
39 | // Create the document overlay but don't yet show it | ||
40 | this.overlay = new Overlay( | ||
41 | { | ||
42 | onClick: function() | ||
43 | { | ||
44 | this.hide(); | ||
45 | }.bind(this) | ||
46 | }); | ||
47 | |||
48 | this._createCard(); | ||
49 | |||
50 | if (this.options.addExtraButtons) | ||
51 | { | ||
52 | this._addExtraButtons(); | ||
53 | } | ||
54 | |||
55 | // Always add a close button | ||
56 | this.addCommandButton(Strings.closeCard,'images/close.gif', function() | ||
57 | { | ||
58 | this.hide(); | ||
59 | }.bind(this)); | ||
60 | |||
61 | this.setTitle(this.options.title); | ||
62 | }, | ||
63 | |||
64 | /* | ||
65 | * Create the card DOM nodes. | ||
66 | */ | ||
67 | _createCard: function() | ||
68 | { | ||
69 | // Get the rounded corner generator and the coordinates of the page center | ||
70 | var Rounder = new RoundedCorners(this.options.color); | ||
71 | var cardPos = window.calcCordsToCenter(this.options.height, this.options.width); | ||
72 | var slicebox = Rounder.get(Rounder.directions.top); | ||
73 | |||
74 | // Intro card will flash across the screen while card builds but | ||
75 | // before fading up if display is not set to none | ||
76 | this.card = Builder.node('div', | ||
77 | { | ||
78 | id: this.options.id, | ||
79 | style: 'display: none;', | ||
80 | className: 'card' | ||
81 | }); | ||
82 | |||
83 | // Create the card header with title and button box | ||
84 | slicebox.appendChild(this.title = Builder.node('span', { className: 'cardTitle' })); | ||
85 | slicebox.appendChild(this.buttonbox = Builder.node('div', { className: 'buttonbox' }, | ||
86 | this.buttonLabel = Builder.node('span', { className: 'commandlabel' }) | ||
87 | )); | ||
88 | |||
89 | // Append the header and the card content area | ||
90 | this.card.appendChild(slicebox); | ||
91 | this.card.appendChild(Builder.node('div', { style: 'background: white; opacity: 100%;' }, | ||
92 | this.cframe = Builder.node('div') | ||
93 | )); | ||
94 | |||
95 | // Append the bottom of the card frame and add the card to the document | ||
96 | this.card.appendChild(Rounder.get(Rounder.directions.bottom)); | ||
97 | document.body.appendChild(this.card); | ||
98 | |||
99 | // Card frame styles | ||
100 | Element.extend(this.cframe).setStyle( | ||
101 | { | ||
102 | width: this.options.width - 11 + 'px', | ||
103 | height: this.options.height - 40 + 'px', | ||
104 | borderLeft: '5px solid ' + this.options.color, | ||
105 | borderRight: '5px solid ' + this.options.color, | ||
106 | borderTop: '10px solid ' + this.options.color, | ||
107 | position: 'relative', | ||
108 | overflow: 'hidden' | ||
109 | }); | ||
110 | |||
111 | // Card styles | ||
112 | Element.extend(this.card).setStyle( | ||
113 | { | ||
114 | height: this.options.height + 'px', | ||
115 | width: this.options.width + 'px', | ||
116 | top: cardPos.top + 'px', | ||
117 | left: cardPos.left + 'px', | ||
118 | zIndex: 9001, | ||
119 | opacity: '100%', | ||
120 | display: 'none', | ||
121 | position: 'absolute' | ||
122 | }); | ||
123 | |||
124 | // Make the card draggable | ||
125 | this.drag = new Draggable(this.card, | ||
126 | { | ||
127 | handle: 'round', | ||
128 | zindex: 9001, | ||
129 | starteffect: null, | ||
130 | endeffect: null | ||
131 | }); | ||
132 | }, | ||
133 | |||
134 | /* | ||
135 | * Add a command button to the top right of the card. | ||
136 | */ | ||
137 | addCommandButton: function(title, icon, action) | ||
138 | { | ||
139 | var newButton = Builder.node('img', { className: 'commandbtn', src: icon }); | ||
140 | |||
141 | // Show the label on mouseover | ||
142 | Event.observe(newButton, 'mouseover', function() | ||
143 | { | ||
144 | this.buttonLabel.innerHTML = title; | ||
145 | }.bindAsEventListener(this)); | ||
146 | |||
147 | // Hide the label on mouse out | ||
148 | Event.observe(newButton, 'mouseout', function() | ||
149 | { | ||
150 | this.buttonLabel.innerHTML = ''; | ||
151 | }.bindAsEventListener(this)); | ||
152 | |||
153 | // Take the action specified when the button is clicked | ||
154 | Event.observe(newButton, 'click', function(event) | ||
155 | { | ||
156 | action(event); | ||
157 | }); | ||
158 | |||
159 | // Add the button to the button box and cache it. | ||
160 | this.buttons[title] = this.buttonbox.appendChild(newButton); | ||
161 | }, | ||
162 | |||
163 | /* | ||
164 | * Removes a command button from the card. | ||
165 | */ | ||
166 | removeCommandButton: function(button) | ||
167 | { | ||
168 | this.buttonLabel.innerHTML = ''; | ||
169 | this.buttonbox.removeChild(this.buttons[button]); | ||
170 | }, | ||
171 | |||
172 | /* | ||
173 | * Sets the title of the card. | ||
174 | */ | ||
175 | setTitle: function(title) | ||
176 | { | ||
177 | this.options.title = title; | ||
178 | this.title.innerHTML = title; | ||
179 | }, | ||
180 | |||
181 | /* | ||
182 | * Disables the drag on the card and removes the card | ||
183 | * from the DOM. | ||
184 | */ | ||
185 | destroy: function() | ||
186 | { | ||
187 | // Kill the drag to prevent probable memory leaks in IE | ||
188 | this.drag.destroy(); | ||
189 | |||
190 | // Remove the card from the DOM | ||
191 | document.body.removeChild(this.card); | ||
192 | |||
193 | // Destroy the overlay | ||
194 | this.overlay.destroy(); | ||
195 | }, | ||
196 | |||
197 | /* | ||
198 | * Display the card. | ||
199 | */ | ||
200 | show: function() | ||
201 | { | ||
202 | // Track the currently active card for the history manager | ||
203 | // FLAWED LOGIC: We can have multiple cards visible at the same | ||
204 | // time. | ||
205 | SME.currentCard = this; | ||
206 | |||
207 | // Get card positioning data | ||
208 | var cardPos = window.calcCordsToCenter(this.options.height, this.options.width); | ||
209 | var windSize = window.getDimensions(); | ||
210 | |||
211 | // Position the card | ||
212 | this.card.setStyle( | ||
213 | { | ||
214 | top: cardPos.top + 'px', | ||
215 | left: cardPos.left + 'px' | ||
216 | }); | ||
217 | |||
218 | this._autoSetLayout(); | ||
219 | |||
220 | // Show the overlay first so it will fade in with the card | ||
221 | this.overlay.show(); | ||
222 | |||
223 | // Fade the card in and run its fadeComplete function. | ||
224 | new Effect.Appear(this.card, | ||
225 | { | ||
226 | afterFinish: function() | ||
227 | { | ||
228 | this.options.onFadeComplete(); | ||
229 | }.bind(this) | ||
230 | }); | ||
231 | }, | ||
232 | |||
233 | /* | ||
234 | * Automatically set the layout based on the content of a JSON feed | ||
235 | * if it is supplied. | ||
236 | */ | ||
237 | _autoSetLayout: function() | ||
238 | { | ||
239 | // Fetch data only if this is not an intro card and a content | ||
240 | // id has been specified. | ||
241 | if (!this.options.contID) | ||
242 | { | ||
243 | return; | ||
244 | } | ||
245 | |||
246 | // Pull the correct data if this is a preview or a real | ||
247 | // card. | ||
248 | if (this.options.preview) | ||
249 | { | ||
250 | var theUrl = SME.url.cardPreview.evaluate({card: this.options.contID}) | ||
251 | } | ||
252 | else | ||
253 | { | ||
254 | var theUrl = SME.url.cards.evaluate({card: this.options.contID}); | ||
255 | } | ||
256 | |||
257 | new Ajax.Request(theUrl, | ||
258 | { | ||
259 | method: 'get', | ||
260 | |||
261 | onSuccess: function(transport) | ||
262 | { | ||
263 | try | ||
264 | { | ||
265 | var data = transport.responseText.cleanJSON().evalJSON(); | ||
266 | this.setLayout(SME.engineMapping[data.template], data); | ||
267 | } | ||
268 | catch (exception) | ||
269 | { | ||
270 | if (SME.debug) | ||
271 | { | ||
272 | console.error(exception); | ||
273 | } | ||
274 | |||
275 | this.setLayout(Card.Layout.Errors, ''); | ||
276 | } | ||
277 | }.bind(this) | ||
278 | }); | ||
279 | }, | ||
280 | |||
281 | /* | ||
282 | * Add extra buttons to a card. I moved this to its own private | ||
283 | * function because some cards, like system cards, don't need all | ||
284 | * those extra buttons. | ||
285 | */ | ||
286 | _addExtraButtons: function() | ||
287 | { | ||
288 | this.addCommandButton(Strings.addToSketchbook,'images/plus.gif', function() | ||
289 | { | ||
290 | SME.sketchbook.addChip(this.options.contID); | ||
291 | }.bind(this)); | ||
292 | |||
293 | this.addCommandButton(Strings.sendToFriend,'images/email.gif', function() | ||
294 | { | ||
295 | if ($('cardFlash')) | ||
296 | { | ||
297 | this.flashP = $('cardFlash').up(); | ||
298 | this.flash = $('cardFlash').remove(); | ||
299 | } | ||
300 | |||
301 | var card = new Card( | ||
302 | { | ||
303 | color: SME.colors.grey, | ||
304 | title: Strings.sendToFriend, | ||
305 | addExtraButtons: false, | ||
306 | |||
307 | onFadeComplete: function() | ||
308 | { | ||
309 | if (this.flash) | ||
310 | { | ||
311 | this.flashP.appendChild(this.flash); | ||
312 | this.flash = null; | ||
313 | } | ||
314 | }.bind(this) | ||
315 | }); | ||
316 | |||
317 | card.setLayout(Card.Layout.Special, | ||
318 | { | ||
319 | url: SME.url.sendToFriend.evaluate( | ||
320 | { | ||
321 | durl: 'card' + encodeURI(this.options.contID), | ||
322 | title: encodeURI(this.options.title) | ||
323 | }) | ||
324 | }); | ||
325 | |||
326 | card.show(); | ||
327 | }.bind(this)); | ||
328 | |||
329 | this.addCommandButton(Strings.printCard, 'images/print.gif', function() | ||
330 | { | ||
331 | this.layoutEngine.print(); | ||
332 | }.bind(this)); | ||
333 | |||
334 | // Card history ON the card was poorly designed and implemented | ||
335 | // (yeah, I know) so I'm just removing it for now till I get | ||
336 | // some time to re-write it. | ||
337 | // | ||
338 | // TODO: Re-write this | ||
339 | this.addCommandButton(Strings.myHistory,'images/history.gif', function() | ||
340 | { | ||
341 | var center = window.calcCordsToCenter(SME.sizes.card.height, SME.sizes.card.width); | ||
342 | SME.history.getDropDown(center.left + 30, center.top + 13); | ||
343 | }); | ||
344 | }, | ||
345 | |||
346 | /* | ||
347 | * Hides the current card but does not remove it from the DOM. | ||
348 | */ | ||
349 | hide: function() | ||
350 | { | ||
351 | // There is no current card so unset it | ||
352 | // FLAWED LOGIC: We can have multiple cards. | ||
353 | SME.currentCard = null; | ||
354 | |||
355 | // Fade out and subsequently destroy the card | ||
356 | new Effect.Fade(this.card, | ||
357 | { | ||
358 | afterFinish: function() | ||
359 | { | ||
360 | this.options.onFadeOutFinish(); | ||
361 | this.destroy(); | ||
362 | }.bind(this) | ||
363 | }); | ||
364 | |||
365 | // FLAWED LOGIC: Multiple cards, see above. | ||
366 | SME.history.clearHash(); | ||
367 | this.overlay.hide(); | ||
368 | this.options.onClose(); | ||
369 | }, | ||
370 | |||
371 | /* | ||
372 | * Sets the layout of the card using a layout engine. | ||
373 | */ | ||
374 | setLayout: function(layoutE, data) | ||
375 | { | ||
376 | this.layoutEngine = new layoutE(this.cframe, data, this).layout(); | ||
377 | } | ||
378 | }); | ||
diff --git a/docroot/classes/chip.class.js b/docroot/classes/chip.class.js new file mode 100755 index 0000000..61b7bdb --- /dev/null +++ b/docroot/classes/chip.class.js | |||
@@ -0,0 +1,269 @@ | |||
1 | /* | ||
2 | * Material Experience - Card Chip Class | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | */ | ||
8 | |||
9 | var CardChip = Class.create(); | ||
10 | Object.extend(CardChip.prototype, | ||
11 | { | ||
12 | /* | ||
13 | * Initializes the chip class. | ||
14 | */ | ||
15 | initialize: function() | ||
16 | { | ||
17 | this.options = Object.extend( | ||
18 | { | ||
19 | x: 0, // X coordinate of the chip | ||
20 | y: 0, // Y coordinate of the chip | ||
21 | locked: false, // Require login to view card | ||
22 | category: null, // Chip target category | ||
23 | contID: null, // Chip target content ID | ||
24 | title: null, // Chip target title | ||
25 | image: null, // Chip image | ||
26 | className: 'chip', // Chip CSS Class Name | ||
27 | animate: true // Animate chip onto table | ||
28 | }, arguments[0] || {}); | ||
29 | |||
30 | this.dragged = false; | ||
31 | |||
32 | this._createChip(); | ||
33 | |||
34 | return this; | ||
35 | }, | ||
36 | |||
37 | /* | ||
38 | * Create the chip DOM nodes and attach events and drags as appropriate. | ||
39 | */ | ||
40 | _createChip: function() | ||
41 | { | ||
42 | this.chip = Builder.node('img', | ||
43 | { | ||
44 | src: this.options.image, | ||
45 | className: this.options.className, | ||
46 | style: 'top: ' + this.options.y + 'px; left: ' + this.options.x + 'px', | ||
47 | title: this.options.title + ((this.options.locked) ? ' (protected)' : ''), | ||
48 | alt: this.options.title + ((this.options.locked) ? ' (protected)' : '') | ||
49 | }); | ||
50 | |||
51 | // Each DOM node should have a link back to this class so we can | ||
52 | // later look at a DOM node which holds no real data about the | ||
53 | // object and come back here to get the data we need. | ||
54 | this.chip.classLink = this; | ||
55 | |||
56 | new Draggable(this.chip, | ||
57 | { | ||
58 | starteffect: null, | ||
59 | endeffect: null, | ||
60 | |||
61 | change: function() | ||
62 | { | ||
63 | // Set the dragged flag (see onClick for more information) | ||
64 | this.dragged = true; | ||
65 | }.bind(this), | ||
66 | |||
67 | onEnd: function(e) | ||
68 | { | ||
69 | // Ensures that cards stay on top of the stack after they are | ||
70 | // dropped. This is accomplished by determining the maximum | ||
71 | // z-index of the cards and applying max + 1 to the current | ||
72 | // chip. | ||
73 | // | ||
74 | // Note that we are modifying a variable used internally by | ||
75 | // Scriptaculous that is NOT part of the public API. The | ||
76 | // originalZ variable is used by Scriptaculous to record the | ||
77 | // z-index that it should return the draggable to, by | ||
78 | // incrementing this we effectively move the card up in the | ||
79 | // stack. | ||
80 | // | ||
81 | // Note that scriptaculous adds a z-index of 1000 when the drag | ||
82 | // starts and does not remove it till AFTER this function executes | ||
83 | // so we have to take off that extra 1000 to get the real z-index. | ||
84 | e.originalZ = $$('div#' + (SME.currentTable || 'home') + ' img.chip').max(function(x) | ||
85 | { | ||
86 | var z = parseInt(x.style.zIndex) || 0; | ||
87 | return (z >= 1000) ? z - 1000 : z; | ||
88 | }) + 1; | ||
89 | |||
90 | // Track the X and Y coordinates of the chip | ||
91 | e.element.classLink.options.x = e.element.style.left.split('px')[0]; | ||
92 | e.element.classLink.options.y = e.element.style.top.split('px')[0]; | ||
93 | } | ||
94 | }); | ||
95 | |||
96 | Event.observe(this.chip, 'click', function() | ||
97 | { | ||
98 | this.onClick(); | ||
99 | }.bindAsEventListener(this)); | ||
100 | |||
101 | if (this.options.animate) | ||
102 | { | ||
103 | this._generateAnimation(); | ||
104 | } | ||
105 | }, | ||
106 | |||
107 | /* | ||
108 | * Handle clicks on the chip. | ||
109 | */ | ||
110 | onClick: function() | ||
111 | { | ||
112 | // If we recently dragged this card then do nothing. This | ||
113 | // facilitates single click DND as well as single click | ||
114 | // activation. | ||
115 | if (this.dragged == true) | ||
116 | { | ||
117 | this.dragged = false; | ||
118 | return; | ||
119 | } | ||
120 | |||
121 | // If this is a locked chip and we aren't logged in then show | ||
122 | // a login screen | ||
123 | if (this.options.locked && !Sketchbook.loggedIn) | ||
124 | { | ||
125 | return Sketchbook.showLoginScreen(); | ||
126 | } | ||
127 | |||
128 | // Disable the puff in IE because the PNG fix breaks it | ||
129 | if (!Prototype.Browser.IE) | ||
130 | { | ||
131 | new Effect.Puff(this.chip, | ||
132 | { | ||
133 | afterFinish: function() | ||
134 | { | ||
135 | new Effect.Appear(this.chip); | ||
136 | }.bind(this) | ||
137 | }); | ||
138 | } | ||
139 | |||
140 | SME.history.registerEvent('card', this.options.title, this.options.contID); | ||
141 | |||
142 | var t = new Card( | ||
143 | { | ||
144 | color: CardTable.tableColor(this.options.category), | ||
145 | contID: this.options.contID, | ||
146 | title: this.options.title, | ||
147 | chip: this | ||
148 | }); | ||
149 | |||
150 | t.show(); | ||
151 | }, | ||
152 | |||
153 | /* | ||
154 | * Actually move the chip onto the table. This function expects the | ||
155 | * original and new coordinates to be pre-generated and stored by | ||
156 | * another function. | ||
157 | */ | ||
158 | animate: function() | ||
159 | { | ||
160 | new Effect.Morph(this.chip, | ||
161 | { | ||
162 | style: | ||
163 | { | ||
164 | top: this.newY + 'px', | ||
165 | left: this.newX + 'px' | ||
166 | } | ||
167 | }); | ||
168 | }, | ||
169 | |||
170 | /* | ||
171 | * Return data about the chip in an object that matches of the format | ||
172 | * of the card table JSON feed. | ||
173 | */ | ||
174 | _serialize: function() | ||
175 | { | ||
176 | return { | ||
177 | cid : this.options.contID, | ||
178 | title : this.options.title, | ||
179 | category : this.options.category, | ||
180 | locked : this.options.locked, | ||
181 | x : this.options.x, | ||
182 | y : this.options.y, | ||
183 | chip : this.options.image | ||
184 | }; | ||
185 | }, | ||
186 | |||
187 | /* | ||
188 | * Generate random coordinates for and place the chip off the edge of | ||
189 | * the table to be animated in later on by a different function. | ||
190 | */ | ||
191 | _generateAnimation: function() | ||
192 | { | ||
193 | var myRand = Math.floor(1 + (5 - 1) * Math.random()); | ||
194 | var windSize = window.getDimensions(); | ||
195 | |||
196 | this.newX = this.options.x; | ||
197 | this.newY = this.options.y; | ||
198 | |||
199 | this.chip.setStyle({ position: 'absolute' }); | ||
200 | |||
201 | // Determine from which direction the cards will animate | ||
202 | switch (myRand) | ||
203 | { | ||
204 | case 1: // Top | ||
205 | this.chip.setStyle( | ||
206 | { | ||
207 | top: -(SME.sizes.chipMax.height) + 'px', | ||
208 | left: (windSize.width / 2) + 'px' | ||
209 | }); | ||
210 | break; | ||
211 | |||
212 | case 2: // Right | ||
213 | this.chip.setStyle( | ||
214 | { | ||
215 | top: (windSize.height / 2) + 'px', | ||
216 | left: (SME.sizes.chipMax.width) + windSize.width + 'px' | ||
217 | }); | ||
218 | break; | ||
219 | |||
220 | case 3: // Bottom | ||
221 | this.chip.setStyle( | ||
222 | { | ||
223 | top: (SME.sizes.chipMax.height) + windSize.height + 'px', | ||
224 | left: (windSize.width / 2) + 'px' | ||
225 | }); | ||
226 | break; | ||
227 | |||
228 | default: // Left | ||
229 | this.chip.setStyle( | ||
230 | { | ||
231 | top: (windSize.height / 2) + 'px', | ||
232 | left: -(SME.sizes.chipMax.width + 40) + 'px' | ||
233 | }); | ||
234 | break; | ||
235 | } | ||
236 | }, | ||
237 | |||
238 | /* | ||
239 | * Add a chip DOM node to a table. | ||
240 | */ | ||
241 | addTo: function(table) | ||
242 | { | ||
243 | table.appendChild(this.chip); | ||
244 | }, | ||
245 | |||
246 | /* | ||
247 | * Remove the chip DOM node from the document. | ||
248 | */ | ||
249 | destroy: function() | ||
250 | { | ||
251 | document.removeChild(this.chip); | ||
252 | }, | ||
253 | |||
254 | /* | ||
255 | * Show the chip. | ||
256 | */ | ||
257 | show: function() | ||
258 | { | ||
259 | this.chip.show(); | ||
260 | }, | ||
261 | |||
262 | /* | ||
263 | * Hide the chip. | ||
264 | */ | ||
265 | hide: function() | ||
266 | { | ||
267 | this.chip.hide(); | ||
268 | } | ||
269 | }); \ No newline at end of file | ||
diff --git a/docroot/classes/cookie.class.js b/docroot/classes/cookie.class.js new file mode 100755 index 0000000..adf28f2 --- /dev/null +++ b/docroot/classes/cookie.class.js | |||
@@ -0,0 +1,70 @@ | |||
1 | /* | ||
2 | * Material Experience - Cookie Handling Class | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) on 10/2/07 | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 10/3/07 | ||
7 | * | ||
8 | * Class to handle cookie CRUD. Code adapted from: | ||
9 | * http://www.quirksmode.org/js/cookies.html | ||
10 | */ | ||
11 | |||
12 | var Cookie = Object.extend(Class.create(), | ||
13 | { | ||
14 | /* | ||
15 | * Creates a cookie. | ||
16 | */ | ||
17 | create: function(name, value, days) | ||
18 | { | ||
19 | if (days) | ||
20 | { | ||
21 | var date = new Date().setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); | ||
22 | var expires = "; expires=" + date.toGMTString(); | ||
23 | } | ||
24 | |||
25 | document.cookie = name + "=" + value + (expires ? expires : '') + "; path=/"; | ||
26 | }, | ||
27 | |||
28 | /* | ||
29 | * Setting the expiration date of the cookie to -1 effectively deletes | ||
30 | * it. | ||
31 | */ | ||
32 | erase: function(name) | ||
33 | { | ||
34 | createCookie(name,"",-1); | ||
35 | }, | ||
36 | |||
37 | /* | ||
38 | * Reads a cookie and returns the value. | ||
39 | */ | ||
40 | read: function(name) | ||
41 | { | ||
42 | var nameEQ = name + "="; | ||
43 | var ca = document.cookie.split(';'); | ||
44 | |||
45 | for ( var i = 0; i < ca.length; i++ ) | ||
46 | { | ||
47 | var c = ca[i]; | ||
48 | |||
49 | while (c.charAt(0) == ' ') | ||
50 | { | ||
51 | c = c.substring(1,c.length); | ||
52 | } | ||
53 | |||
54 | if (c.indexOf(nameEQ) == 0) | ||
55 | { | ||
56 | return c.substring(nameEQ.length,c.length); | ||
57 | } | ||
58 | } | ||
59 | |||
60 | return null; | ||
61 | } | ||
62 | }); | ||
63 | |||
64 | /* | ||
65 | * Shorthand for Cookie.read | ||
66 | */ | ||
67 | function $C(name) | ||
68 | { | ||
69 | return Cookie.read(name); | ||
70 | } \ No newline at end of file | ||
diff --git a/docroot/classes/decoder.module.js b/docroot/classes/decoder.module.js new file mode 100755 index 0000000..ae33059 --- /dev/null +++ b/docroot/classes/decoder.module.js | |||
@@ -0,0 +1,205 @@ | |||
1 | /* | ||
2 | * Material Experience - Encoder/Decoder Module | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
6 | * | ||
7 | * Encoder/Decoder module for common encoding schemes like URL and | ||
8 | * base64. Code from: http://ostermiller.org/calc/encode.html | ||
9 | */ | ||
10 | var END_OF_INPUT = -1; | ||
11 | |||
12 | var base64Chars = new Array( | ||
13 | 'A','B','C','D','E','F','G','H', | ||
14 | 'I','J','K','L','M','N','O','P', | ||
15 | 'Q','R','S','T','U','V','W','X', | ||
16 | 'Y','Z','a','b','c','d','e','f', | ||
17 | 'g','h','i','j','k','l','m','n', | ||
18 | 'o','p','q','r','s','t','u','v', | ||
19 | 'w','x','y','z','0','1','2','3', | ||
20 | '4','5','6','7','8','9','+','/' | ||
21 | ); | ||
22 | |||
23 | var reverseBase64Chars = new Array(); | ||
24 | var base64Str; | ||
25 | var base64Count; | ||
26 | |||
27 | for (var i=0; i < base64Chars.length; i++) | ||
28 | { | ||
29 | reverseBase64Chars[base64Chars[i]] = i; | ||
30 | } | ||
31 | |||
32 | // -------------------------------------------------------------------------- \\ | ||
33 | |||
34 | function urlDecode(str) | ||
35 | { | ||
36 | return unescape(str.replace(new RegExp('\\+','g'),' ')); | ||
37 | } | ||
38 | |||
39 | function urlEncode(str) | ||
40 | { | ||
41 | str = escape(str); | ||
42 | str = str.replace(new RegExp('\\+','g'),'%2B'); | ||
43 | |||
44 | return str.replace(new RegExp('%20','g'),'+'); | ||
45 | } | ||
46 | |||
47 | // -------------------------------------------------------------------------- \\ | ||
48 | |||
49 | function setBase64Str(str) | ||
50 | { | ||
51 | base64Str = str; | ||
52 | base64Count = 0; | ||
53 | } | ||
54 | |||
55 | function readBase64() | ||
56 | { | ||
57 | if (!base64Str) | ||
58 | { | ||
59 | return END_OF_INPUT; | ||
60 | } | ||
61 | |||
62 | if (base64Count >= base64Str.length) | ||
63 | { | ||
64 | return END_OF_INPUT; | ||
65 | } | ||
66 | |||
67 | var c = base64Str.charCodeAt(base64Count) & 0xff; | ||
68 | |||
69 | base64Count++; | ||
70 | |||
71 | return c; | ||
72 | } | ||
73 | |||
74 | function encodeBase64(str) | ||
75 | { | ||
76 | setBase64Str(str); | ||
77 | |||
78 | var result = ''; | ||
79 | var inBuffer = new Array(3); | ||
80 | var lineCount = 0; | ||
81 | var done = false; | ||
82 | |||
83 | while (!done && (inBuffer[0] = readBase64()) != END_OF_INPUT) | ||
84 | { | ||
85 | inBuffer[1] = readBase64(); | ||
86 | inBuffer[2] = readBase64(); | ||
87 | result += (base64Chars[ inBuffer[0] >> 2 ]); | ||
88 | |||
89 | if (inBuffer[1] != END_OF_INPUT) | ||
90 | { | ||
91 | result += (base64Chars [(( inBuffer[0] << 4 ) & 0x30) | (inBuffer[1] >> 4) ]); | ||
92 | |||
93 | if (inBuffer[2] != END_OF_INPUT) | ||
94 | { | ||
95 | result += (base64Chars [((inBuffer[1] << 2) & 0x3c) | (inBuffer[2] >> 6) ]); | ||
96 | result += (base64Chars [inBuffer[2] & 0x3F]); | ||
97 | } | ||
98 | else | ||
99 | { | ||
100 | result += (base64Chars [((inBuffer[1] << 2) & 0x3c)]); | ||
101 | result += ('='); | ||
102 | done = true; | ||
103 | } | ||
104 | } | ||
105 | else | ||
106 | { | ||
107 | result += (base64Chars [(( inBuffer[0] << 4 ) & 0x30)]); | ||
108 | result += ('='); | ||
109 | result += ('='); | ||
110 | done = true; | ||
111 | } | ||
112 | |||
113 | lineCount += 4; | ||
114 | |||
115 | if (lineCount >= 76) | ||
116 | { | ||
117 | result += ('\n'); | ||
118 | lineCount = 0; | ||
119 | } | ||
120 | } | ||
121 | |||
122 | return result; | ||
123 | } | ||
124 | |||
125 | function readReverseBase64() | ||
126 | { | ||
127 | if (!base64Str) | ||
128 | { | ||
129 | return END_OF_INPUT; | ||
130 | } | ||
131 | |||
132 | while (true) | ||
133 | { | ||
134 | if (base64Count >= base64Str.length) | ||
135 | { | ||
136 | return END_OF_INPUT; | ||
137 | } | ||
138 | |||
139 | var nextCharacter = base64Str.charAt(base64Count); | ||
140 | base64Count++; | ||
141 | |||
142 | if (reverseBase64Chars[nextCharacter]) | ||
143 | { | ||
144 | return reverseBase64Chars[nextCharacter]; | ||
145 | } | ||
146 | |||
147 | if (nextCharacter == 'A') | ||
148 | { | ||
149 | return 0; | ||
150 | } | ||
151 | } | ||
152 | |||
153 | return END_OF_INPUT; | ||
154 | } | ||
155 | |||
156 | function ntos(n) | ||
157 | { | ||
158 | n = n.toString(16); | ||
159 | |||
160 | if (n.length == 1) | ||
161 | { | ||
162 | n = "0" + n; | ||
163 | } | ||
164 | |||
165 | n = "%" + n; | ||
166 | |||
167 | return unescape(n); | ||
168 | } | ||
169 | |||
170 | function decodeBase64(str) | ||
171 | { | ||
172 | setBase64Str(str); | ||
173 | |||
174 | var result = ""; | ||
175 | var inBuffer = new Array(4); | ||
176 | var done = false; | ||
177 | |||
178 | while (!done && (inBuffer[0] = readReverseBase64()) != END_OF_INPUT | ||
179 | && (inBuffer[1] = readReverseBase64()) != END_OF_INPUT) | ||
180 | { | ||
181 | inBuffer[2] = readReverseBase64(); | ||
182 | inBuffer[3] = readReverseBase64(); | ||
183 | result += ntos((((inBuffer[0] << 2) & 0xff)| inBuffer[1] >> 4)); | ||
184 | |||
185 | if (inBuffer[2] != END_OF_INPUT) | ||
186 | { | ||
187 | result += ntos((((inBuffer[1] << 4) & 0xff)| inBuffer[2] >> 2)); | ||
188 | |||
189 | if (inBuffer[3] != END_OF_INPUT) | ||
190 | { | ||
191 | result += ntos((((inBuffer[2] << 6) & 0xff) | inBuffer[3])); | ||
192 | } | ||
193 | else | ||
194 | { | ||
195 | done = true; | ||
196 | } | ||
197 | } | ||
198 | else | ||
199 | { | ||
200 | done = true; | ||
201 | } | ||
202 | } | ||
203 | |||
204 | return result; | ||
205 | } \ No newline at end of file | ||
diff --git a/docroot/classes/history.class.js b/docroot/classes/history.class.js new file mode 100755 index 0000000..12005c3 --- /dev/null +++ b/docroot/classes/history.class.js | |||
@@ -0,0 +1,208 @@ | |||
1 | /* | ||
2 | * Material Experience - History Manager Class | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | */ | ||
8 | |||
9 | var HistoryManager = Class.create(); | ||
10 | Object.extend(HistoryManager.prototype, | ||
11 | { | ||
12 | /* | ||
13 | * Sets up the history storage array and initializes the last | ||
14 | * event to nothing. | ||
15 | */ | ||
16 | initialize: function() | ||
17 | { | ||
18 | this.historyStore = $H(); | ||
19 | this.lastEvent = null; | ||
20 | }, | ||
21 | |||
22 | /* | ||
23 | * Clears the window hash. Generally called when a card closes. | ||
24 | */ | ||
25 | clearHash: function() | ||
26 | { | ||
27 | window.location.hash = '#'; | ||
28 | }, | ||
29 | |||
30 | /* | ||
31 | * Register an event with the history manager. Also updates the | ||
32 | * window hash to reflect the event. This is the only way to | ||
33 | * register an event with the history manager. | ||
34 | */ | ||
35 | registerEvent: function(type, title, url) | ||
36 | { | ||
37 | window.location.hash = '#' + type + url; | ||
38 | |||
39 | // If you don't set this explicitly IE will show the hash as | ||
40 | // the document title. | ||
41 | document.title = Strings.appTitle; | ||
42 | |||
43 | this.lastEvent = '#' + type + url; | ||
44 | this.historyStore[type + url] = title; | ||
45 | |||
46 | return this; | ||
47 | }, | ||
48 | |||
49 | /* | ||
50 | * Stops polling the URL for new events. | ||
51 | */ | ||
52 | stopPolling: function() | ||
53 | { | ||
54 | this.poller.stop(); | ||
55 | |||
56 | return this; | ||
57 | }, | ||
58 | |||
59 | /* | ||
60 | * Starts polling the URL for new events and will execute the event | ||
61 | * handler if an event occurs. | ||
62 | */ | ||
63 | pollEvents: function() | ||
64 | { | ||
65 | this.poller = new PeriodicalExecuter(function() | ||
66 | { | ||
67 | // Make sure there is a valid hash and it is NOT the card | ||
68 | // that we just loaded (prevent double-carding) | ||
69 | if ( | ||
70 | this.lastEvent != window.location.hash && | ||
71 | window.location.hash != '#' && | ||
72 | window.location.hash != '' | ||
73 | ) { | ||
74 | this.lastEvent = window.location.hash; | ||
75 | this._handleEvents(); | ||
76 | } | ||
77 | }.bind(this), 0.1); | ||
78 | |||
79 | return this; | ||
80 | }, | ||
81 | |||
82 | /* | ||
83 | * Handles an event, this is called internally by the pollEvents function | ||
84 | * when a new event occurs and should never be called directly. | ||
85 | */ | ||
86 | _handleEvents: function() | ||
87 | { | ||
88 | // Breaks down the event type and parameters from the hash | ||
89 | var evtTypes = /^(card|table|preview)/; | ||
90 | var hash = window.location.hash; | ||
91 | |||
92 | // When IE does the split it only returns one array element, | ||
93 | // the parameter, so we have to do a little magic to match | ||
94 | // the action too. All other browsers seem to work correctly. | ||
95 | var fullEvt = hash.substr(1, hash.length).split(evtTypes).without(''); | ||
96 | var eventType = (fullEvt.length == 1) ? hash.substr(1, hash.length).match(evtTypes)[0] : fullEvt[0]; | ||
97 | var eventParam = (fullEvt.length == 1) ? fullEvt : fullEvt[1]; | ||
98 | |||
99 | // Hide the current card if there is one | ||
100 | if (SME.currentCard && SME.currentCard.hide) | ||
101 | { | ||
102 | SME.currentCard.hide(); | ||
103 | } | ||
104 | |||
105 | switch (eventType) | ||
106 | { | ||
107 | case 'table': | ||
108 | CardTable.showTable(eventParam); | ||
109 | return; | ||
110 | break; | ||
111 | |||
112 | case 'card': | ||
113 | theURL = SME.url.cards.evaluate({card: eventParam}); | ||
114 | break; | ||
115 | |||
116 | case 'preview': | ||
117 | theURL = SME.url.cardPreview.evaluate({card: eventParam}); | ||
118 | this.stopPolling(); | ||
119 | break; | ||
120 | |||
121 | default: | ||
122 | return; | ||
123 | break; | ||
124 | } | ||
125 | |||
126 | new Ajax.Request(theURL, | ||
127 | { | ||
128 | method: 'get', | ||
129 | |||
130 | onSuccess: function(transport) | ||
131 | { | ||
132 | var data = transport.responseText.cleanJSON().evalJSON(); | ||
133 | |||
134 | var card = new Card( | ||
135 | { | ||
136 | color: CardTable.tableColor(data.type), | ||
137 | contID: eventParam, | ||
138 | title: data.title, | ||
139 | preview: (eventType == 'preview') ? true : false | ||
140 | }); | ||
141 | |||
142 | this.registerEvent(eventType, data.title, eventParam); | ||
143 | card.show(); | ||
144 | }.bind(this) | ||
145 | }); | ||
146 | }, | ||
147 | |||
148 | |||
149 | /* | ||
150 | * Creates and populates the history dropdown based on the | ||
151 | * events stored in the history storage array. Also handles | ||
152 | * showing, hiding and positioning that menu. | ||
153 | */ | ||
154 | getDropDown: function(x, y) | ||
155 | { | ||
156 | var dropdown = $('history'); | ||
157 | |||
158 | // If no x and y then position the menu for the my history | ||
159 | // dropdown from the tools menu. | ||
160 | x = x ? x : 20; | ||
161 | y = y ? y : 26; | ||
162 | |||
163 | // Remove and re-append the menu from the DOM, this prevents | ||
164 | // IE z-index bugs. | ||
165 | document.body.appendChild(dropdown.remove()); | ||
166 | dropdown.innerHTML = ''; | ||
167 | |||
168 | // Populate the menu | ||
169 | this.historyStore.each(function(item) | ||
170 | { | ||
171 | dropdown.appendChild(Builder.node('li', {}, Builder.node('a', { href: '#' + item.key }, item.value))); | ||
172 | }); | ||
173 | |||
174 | // Initially show the menu | ||
175 | dropdown.addClassName('hovered'); | ||
176 | |||
177 | // Must explicitly set left to nothing otherwise positioning | ||
178 | // is wrong. | ||
179 | dropdown.setStyle( | ||
180 | { | ||
181 | right: x + 'px', | ||
182 | top: y + 'px', | ||
183 | left: '', | ||
184 | zIndex: 9999 | ||
185 | }); | ||
186 | |||
187 | // Hide the menu when the user... | ||
188 | $H({ | ||
189 | '#history a' : 'click', // Clicks on a link | ||
190 | 'div.table' : 'mouseover', // Mouses-off onto a table | ||
191 | 'p' : 'mouseover', // Mouses-off onto a paragraph | ||
192 | 'iframe' : 'mouseover', // Mouses-off onto an iframe | ||
193 | 'img' : 'mouseover' // Mouses-off onto an image | ||
194 | }).each(function(aitem) | ||
195 | { | ||
196 | $$(aitem.key).each(function(item) | ||
197 | { | ||
198 | item.observe(aitem.value, function() | ||
199 | { | ||
200 | if ($('history').hasClassName('hovered')) | ||
201 | { | ||
202 | $('history').removeClassName('hovered'); | ||
203 | } | ||
204 | }); | ||
205 | }); | ||
206 | }); | ||
207 | } | ||
208 | }); \ No newline at end of file | ||
diff --git a/docroot/classes/layout.class.js b/docroot/classes/layout.class.js new file mode 100755 index 0000000..a088fbf --- /dev/null +++ b/docroot/classes/layout.class.js | |||
@@ -0,0 +1,51 @@ | |||
1 | /* | ||
2 | * Material Experience - Card Layout Engines Class | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | */ | ||
8 | |||
9 | // Card.Layout.Errors = Class.create(); | ||
10 | // Object.extend(Object.extend(Card.Layout.Errors.prototype, Card.Layout.prototype), | ||
11 | |||
12 | Card.Layout = Class.create(); | ||
13 | Object.extend(Card.Layout.prototype, | ||
14 | { | ||
15 | /* | ||
16 | * Initialize the layout class | ||
17 | */ | ||
18 | initialize: function(cframe, data, card) | ||
19 | { | ||
20 | this.cframe = cframe; | ||
21 | this.data = data; | ||
22 | this.card = card; | ||
23 | this.color = this.card.options.color; | ||
24 | this.hasResources = false; | ||
25 | }, | ||
26 | |||
27 | /* | ||
28 | * Main function to layout the card. | ||
29 | */ | ||
30 | layout: function() | ||
31 | { | ||
32 | throw "Not implemented here."; | ||
33 | }, | ||
34 | |||
35 | /* | ||
36 | * Throw the card into a popup window for printing. | ||
37 | */ | ||
38 | print: function() | ||
39 | { | ||
40 | throw "Not implemented here."; | ||
41 | }, | ||
42 | |||
43 | /* | ||
44 | * Debug the card layout. Generally this should be used to spot out | ||
45 | * data issues. But it could also be used for other debugging purposes. | ||
46 | */ | ||
47 | _debug: function() | ||
48 | { | ||
49 | throw "Not implemented here."; | ||
50 | } | ||
51 | }); \ No newline at end of file | ||
diff --git a/docroot/classes/layouts/layout.custom.class.js b/docroot/classes/layouts/layout.custom.class.js new file mode 100755 index 0000000..d4aa463 --- /dev/null +++ b/docroot/classes/layouts/layout.custom.class.js | |||
@@ -0,0 +1,219 @@ | |||
1 | /* | ||
2 | * Material Experience - Card Layout Engines Class | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | */ | ||
8 | |||
9 | Card.Layout.Custom = Class.create(); | ||
10 | Object.extend(Object.extend(Card.Layout.Custom.prototype, Card.Layout.prototype), | ||
11 | { | ||
12 | /* | ||
13 | * Initialize the layout class | ||
14 | */ | ||
15 | initialize: function(cframe, data, card) | ||
16 | { | ||
17 | this.cframe = cframe; | ||
18 | this.data = data; | ||
19 | this.card = card; | ||
20 | this.color = this.card.options.color; | ||
21 | this.hasResources = false; | ||
22 | this.inited = false; | ||
23 | }, | ||
24 | |||
25 | /* | ||
26 | * Create custom scroll bars for an HTML element. | ||
27 | */ | ||
28 | scrollify: function(div, isiframe) | ||
29 | { | ||
30 | var div2 = null; | ||
31 | |||
32 | // Inherit the color from a link | ||
33 | var color = $$('ul#navigation li a')[0].getStyle('background-color'); | ||
34 | |||
35 | if (isiframe) | ||
36 | { | ||
37 | div2 = div.contentWindow.document.documentElement; | ||
38 | } | ||
39 | else | ||
40 | { | ||
41 | div2 = div; | ||
42 | } | ||
43 | |||
44 | // Create the scroll container and draggable widget | ||
45 | this.cframe.appendChild( | ||
46 | this.scrollCont = Builder.node('div', | ||
47 | { | ||
48 | style: 'border-left: 1px solid ' + color + ';' + | ||
49 | 'width: 1px; position: absolute; ' + | ||
50 | 'height:' + div.offsetHeight + 'px; ' + | ||
51 | 'top: 10px;' + 'left: ' + (div.offsetLeft + div.offsetWidth + 12) + 'px;' | ||
52 | }, | ||
53 | |||
54 | this.scrollBar = Builder.node('img', | ||
55 | { | ||
56 | src: 'http://materialexperience.santoprene.com/images/pill.gif', | ||
57 | style: 'display: block; margin-left: -3px; cursor: move; ' + | ||
58 | 'background: ' + color + '; padding: 0px;' | ||
59 | }) | ||
60 | )); | ||
61 | |||
62 | // Create the scroller | ||
63 | this.scroller = new Control.Slider(this.scrollBar, this.scrollCont, | ||
64 | { | ||
65 | axis: 'vertical', | ||
66 | range: $R(0, div2.scrollHeight), | ||
67 | |||
68 | onSlide: function(value) | ||
69 | { | ||
70 | div2.scrollTop = Math.floor(value); | ||
71 | }.bind(this) | ||
72 | }); | ||
73 | |||
74 | new PeriodicalExecuter(function() | ||
75 | { | ||
76 | // Hide the scroller if there isn't a need for it | ||
77 | if (div2.scrollHeight > 480) | ||
78 | { | ||
79 | this.scrollCont.show(); | ||
80 | } | ||
81 | else | ||
82 | { | ||
83 | this.scrollCont.hide(); | ||
84 | return; | ||
85 | } | ||
86 | |||
87 | // Update the scroller range when the contents change | ||
88 | this.scroller.range = $R(0, div2.scrollHeight); | ||
89 | |||
90 | // Move the scroller when the scrolled element changes | ||
91 | // (e.g. linking down into the page). | ||
92 | if (this.scroller.value != div2.scrollTop) | ||
93 | { | ||
94 | this.scroller.setValue(div2.scrollTop); | ||
95 | } | ||
96 | }.bind(this), 1); | ||
97 | }, | ||
98 | |||
99 | initIframe: function(url) | ||
100 | { | ||
101 | $('wideCol').innerHTML = '<iframe ' + | ||
102 | 'src = "' + url + '" ' + | ||
103 | 'frameborder = "0" ' + | ||
104 | 'scrolling = "auto" ' + | ||
105 | 'width = "660" ' + | ||
106 | 'height = "460" ' + | ||
107 | 'id = "wide_content" ' + | ||
108 | 'name = "wide_content" ' + | ||
109 | '></iframe>'; | ||
110 | }, | ||
111 | |||
112 | /* | ||
113 | * Main function to layout the card. | ||
114 | */ | ||
115 | layout: function() | ||
116 | { | ||
117 | var htmlstr = '<div id="narrowCol" style="float: left; margin: 10px 0px 0px 20px; width: 200px; height: 460px; overflow: hidden;"><ul id="navigation">'; | ||
118 | |||
119 | this.data.contentNarrow.each(function(item) | ||
120 | { | ||
121 | htmlstr += '<li><a target="wide_content" href="' + item.url + '">' + item.title + '</a>'; | ||
122 | |||
123 | if (item.subitems) | ||
124 | { | ||
125 | var parentUrl = item.url; | ||
126 | |||
127 | htmlstr += '<ul>'; | ||
128 | |||
129 | item.subitems.each(function(subitem) | ||
130 | { | ||
131 | htmlstr += '<li><a target="wide_content" href="' + parentUrl + '#' + subitem.url + '">' + subitem.title + '</a></li>'; | ||
132 | }.bind(this)); | ||
133 | |||
134 | htmlstr += '</ul></li>'; | ||
135 | } | ||
136 | else | ||
137 | { | ||
138 | htmlstr += '</li>'; | ||
139 | } | ||
140 | }.bind(this)); | ||
141 | |||
142 | htmlstr += '</ul></div><div id="wideCol" style="width: 660px; height: 460px; float: right; margin: 10px 10px 0px 0px;"></div>'; | ||
143 | this.cframe.innerHTML = htmlstr; | ||
144 | |||
145 | if (this.data.intro) | ||
146 | { | ||
147 | $('wideCol').innerHTML = this.data.intro; | ||
148 | } | ||
149 | |||
150 | // this.scrollify($('wide_content'), true); | ||
151 | this.scrollify($('narrowCol')); | ||
152 | |||
153 | // Hide everything first | ||
154 | $$('ul#navigation li ul').each(function(item) | ||
155 | { | ||
156 | item.setStyle({ display: 'none' }); | ||
157 | }); | ||
158 | |||
159 | var curr = null; | ||
160 | // Then attach the onclick events | ||
161 | $$('ul#navigation li').each(function(item) | ||
162 | { | ||
163 | Event.observe(item, 'click', function(event) | ||
164 | { | ||
165 | // Hide the currently opened item if it is clicked again | ||
166 | // per BS. | ||
167 | if (curr && item == curr) | ||
168 | { | ||
169 | curr.down('ul').hide(); | ||
170 | item.setStyle({ borderLeft: '0px' }); | ||
171 | curr = null; | ||
172 | return; | ||
173 | } | ||
174 | |||
175 | curr = item; | ||
176 | |||
177 | // Hide everything | ||
178 | $$('ul#navigation li ul').each(function(item) | ||
179 | { | ||
180 | item.setStyle({ display: 'none' }); | ||
181 | }); | ||
182 | |||
183 | // Clear out the borders | ||
184 | $$('ul#navigation li').each(function(item) | ||
185 | { | ||
186 | item.setStyle({ borderLeft: '' }); | ||
187 | }); | ||
188 | |||
189 | // Figure out what color we SHOULD be, this will change | ||
190 | // depending on the card color we are on so just rely | ||
191 | // on the designer to communicate through the CSS, | ||
192 | // probably not wise relying on a designer for code | ||
193 | // but alas... | ||
194 | var color = $$('ul#navigation li a')[0].getStyle('background-color'); | ||
195 | |||
196 | this.initIframe(item.href); | ||
197 | |||
198 | // Catch and execute or just leave it lie | ||
199 | try | ||
200 | { | ||
201 | if (item.down('ul').style.display != 'none') | ||
202 | { | ||
203 | item.down('ul').hide(); | ||
204 | Event.stop(event); | ||
205 | return; | ||
206 | } | ||
207 | |||
208 | item.down('ul').show(); | ||
209 | item.setStyle({ borderLeft: '3px solid ' + color }); | ||
210 | //Event.stop(event); | ||
211 | } | ||
212 | catch (e) | ||
213 | { | ||
214 | // Just let the link do its thing | ||
215 | } | ||
216 | }.bind(this)); | ||
217 | }.bind(this)); | ||
218 | } | ||
219 | }); \ No newline at end of file | ||
diff --git a/docroot/classes/layouts/layout.error.class.js b/docroot/classes/layouts/layout.error.class.js new file mode 100755 index 0000000..2fedfcf --- /dev/null +++ b/docroot/classes/layouts/layout.error.class.js | |||
@@ -0,0 +1,33 @@ | |||
1 | /* | ||
2 | * Material Experience - Error Card Layout Engine | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | */ | ||
8 | |||
9 | Card.Layout.Errors = Class.create(); | ||
10 | Object.extend(Object.extend(Card.Layout.Errors.prototype, Card.Layout.prototype), | ||
11 | { | ||
12 | /* | ||
13 | * Initialize the layout class | ||
14 | */ | ||
15 | initialize: function(cframe, data, card) | ||
16 | { | ||
17 | this.cframe = cframe; | ||
18 | this.data = data; | ||
19 | this.card = card; | ||
20 | this.color = this.card.options.color; | ||
21 | this.hasResources = false; | ||
22 | }, | ||
23 | |||
24 | /* | ||
25 | * Main function to layout the card. | ||
26 | */ | ||
27 | layout: function() | ||
28 | { | ||
29 | this.cframe.innerHTML = ''; | ||
30 | this.cframe.appendChild(Builder.node('h1', Strings.cardErrorTitle)); | ||
31 | this.cframe.appendChild(Builder.node('p', Strings.cardErrorText)); | ||
32 | } | ||
33 | }); \ No newline at end of file | ||
diff --git a/docroot/classes/layouts/layout.primary.class.js b/docroot/classes/layouts/layout.primary.class.js new file mode 100755 index 0000000..87bc910 --- /dev/null +++ b/docroot/classes/layouts/layout.primary.class.js | |||
@@ -0,0 +1,411 @@ | |||
1 | /* | ||
2 | * Material Experience - Primary Card Layout Class | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | * | ||
8 | * WARNING: Here be dragons, OK so not as many as before but good luck you poor | ||
9 | * sap. | ||
10 | */ | ||
11 | |||
12 | Card.Layout.Primary = Class.create(); | ||
13 | Object.extend(Object.extend(Card.Layout.Primary.prototype, Card.Layout.prototype), | ||
14 | { | ||
15 | /* | ||
16 | * Initialize the layout class | ||
17 | */ | ||
18 | initialize: function(cframe, data, card) | ||
19 | { | ||
20 | this.cframe = cframe; | ||
21 | this.data = data; | ||
22 | this.card = card; | ||
23 | this.color = this.card.options.color; | ||
24 | this.hasResources = false; | ||
25 | |||
26 | var direction = data.template.replace(/narrow-/,''); | ||
27 | this.direction = direction ? direction : 'left'; | ||
28 | this.oppositeDirection = (direction == 'left') ? 'right' : 'left'; | ||
29 | }, | ||
30 | |||
31 | /* | ||
32 | * Set the column content. This can be flash, an image or an iframe. | ||
33 | */ | ||
34 | setColumn: function(url, direction, contTitle) | ||
35 | { | ||
36 | var content = Builder.node('div', { style: 'float: ' + this.oppositeDirection }); | ||
37 | var myCol = direction + 'Col'; | ||
38 | |||
39 | if (/swf$/.test(url)) // Flash | ||
40 | { | ||
41 | content.innerHTML = new SWFObject(url, 'cardFlash', 580, 479, 9, '#FFFFFF').getSWFHTML(); | ||
42 | } | ||
43 | else if (/(png|gif|jpg|jpeg)$/.test(url)) // Image | ||
44 | { | ||
45 | content = Builder.node('img', | ||
46 | { | ||
47 | src: url, | ||
48 | alt: contTitle, | ||
49 | title: contTitle, | ||
50 | style: 'float: ' + this.oppositeDirection | ||
51 | }); | ||
52 | } | ||
53 | else // iFrame | ||
54 | { | ||
55 | // Can't use DOM methods because IE is allergic | ||
56 | content.innerHTML = '<iframe ' + | ||
57 | 'src = "' + url + '" ' + | ||
58 | 'frameborder = "0" ' + | ||
59 | 'scrolling = "auto" ' + | ||
60 | 'width = "580" ' + | ||
61 | 'height = "480" ' + | ||
62 | 'id = "wide_content" ' + | ||
63 | 'name = "wide_content" ' + | ||
64 | '></iframe>'; | ||
65 | } | ||
66 | |||
67 | // We need to replace the content of the column if it exists to | ||
68 | // facilitate changing content later on (i.e. thumbnails) | ||
69 | if (typeof this[myCol] != 'undefined') | ||
70 | { | ||
71 | this.cframe.replaceChild(content, this[myCol]); | ||
72 | this[myCol] = content; | ||
73 | } | ||
74 | else | ||
75 | { | ||
76 | this[myCol] = this.cframe.appendChild(content); | ||
77 | } | ||
78 | |||
79 | }, | ||
80 | |||
81 | /* | ||
82 | * Build the resource (more information) list. | ||
83 | */ | ||
84 | getResourceList: function() | ||
85 | { | ||
86 | // Only run if we need to | ||
87 | if (!this.data.resources) | ||
88 | { | ||
89 | return; | ||
90 | } | ||
91 | |||
92 | var numResources = 0; | ||
93 | var iter = 0; | ||
94 | var limit = 5; | ||
95 | this.hasResources = true; | ||
96 | |||
97 | this.resourceContainer = this.contentArea.appendChild( | ||
98 | Builder.node('div', | ||
99 | [ | ||
100 | Builder.node('h2', { style: 'color: ' + this.card.options.color }, Strings.moreInfo), | ||
101 | this.resources = Builder.node('ul', { className: 'resourceList' }) | ||
102 | ] | ||
103 | )); | ||
104 | |||
105 | this.data.resources.each(function(item) | ||
106 | { | ||
107 | // Limit the number of allowable resources | ||
108 | if (typeof item == 'undefined' || ++iter > limit) | ||
109 | { | ||
110 | return; | ||
111 | } | ||
112 | |||
113 | numResources++; | ||
114 | |||
115 | this.resources.appendChild(Builder.node('li', | ||
116 | Builder.node('a', | ||
117 | { | ||
118 | href: item.link + '?entrypoint=DESIGNER', | ||
119 | target: '_new', | ||
120 | style: 'color: ' + this.color | ||
121 | }, item.title) | ||
122 | )); | ||
123 | }.bind(this)); | ||
124 | }, | ||
125 | |||
126 | /* | ||
127 | * Set the contents of the narrow column. | ||
128 | */ | ||
129 | setHTML: function(content) | ||
130 | { | ||
131 | this.htmlDiv.innerHTML = content; | ||
132 | }, | ||
133 | |||
134 | /* | ||
135 | * Setup the thumbnails. | ||
136 | */ | ||
137 | getThumbnails: function() | ||
138 | { | ||
139 | // Only run if we need to | ||
140 | if (this.data.media.size() <= 1) | ||
141 | { | ||
142 | return; | ||
143 | } | ||
144 | |||
145 | var iter = 0; | ||
146 | var limit = 5; // Includes main media file | ||
147 | var lData = this.data.media; | ||
148 | |||
149 | this.thumbStrip = this.contentArea.appendChild(Builder.node('div', { className: 'thumbStrip' })); | ||
150 | |||
151 | // Add the main media file to the data array | ||
152 | lData.push( | ||
153 | { | ||
154 | thumb: this.data.contentWide[0].thumb, | ||
155 | url: this.data.contentWide[0].url | ||
156 | }); | ||
157 | |||
158 | this.data.media.each(function(item) | ||
159 | { | ||
160 | // Limit the number of thumbnails | ||
161 | if (typeof item == 'undefined' || ++iter > limit) | ||
162 | { | ||
163 | return; | ||
164 | } | ||
165 | |||
166 | var mediaPiece = this.thumbStrip.appendChild( | ||
167 | Builder.node('img', | ||
168 | { | ||
169 | src: item.thumb, | ||
170 | className: 'mediaThumb' | ||
171 | }) | ||
172 | ); | ||
173 | |||
174 | Event.observe(mediaPiece, 'click', function() | ||
175 | { | ||
176 | this.setColumn(item.url, this.direction, ''); | ||
177 | }.bindAsEventListener(this)); | ||
178 | }.bind(this)); | ||
179 | }, | ||
180 | |||
181 | /* | ||
182 | * Clean up the data. | ||
183 | */ | ||
184 | _cleanData: function() | ||
185 | { | ||
186 | var lData = this.data.contentNarrow[0].htmlContent; | ||
187 | this.data.contentNarrow[0].htmlContent = lData.replace(/<a/gi, '<a style="color: '+this.color+'"'); | ||
188 | }, | ||
189 | |||
190 | /* | ||
191 | * Calculate the heights of various interface elements for use in | ||
192 | * laying out the page. | ||
193 | */ | ||
194 | _calculateHeights: function() | ||
195 | { | ||
196 | // This data structure will hold the heights of all the elements | ||
197 | // in the narrow column for use in dynamically calculating the | ||
198 | // layout of the column. | ||
199 | this.heightTable = | ||
200 | { | ||
201 | headline : this.headLine ? this.headLine.getHeight() : 0, | ||
202 | content : this.htmlDiv ? this.htmlDiv.getHeight() : 0, | ||
203 | thumbnails : this.thumbStrip ? this.thumbStrip.getHeight() : 0, | ||
204 | resources : this.resourceContainer ? this.resourceContainer.getHeight() : 0, | ||
205 | paddingFactor : !Prototype.Browser.IE ? 40 : 0 | ||
206 | }; | ||
207 | |||
208 | // If there is no headline IE comes up with a ridiculous height for some | ||
209 | // reason so we correct for that here. | ||
210 | if (Prototype.Browser.IE && this.data.contentNarrow[0].title.length == 0) | ||
211 | { | ||
212 | this.heightTable.headline = 0; | ||
213 | } | ||
214 | |||
215 | // A variety of exceptions/tweaks for IE6 found by testing every combination | ||
216 | // of layouts and finding the variance. There isn't much of another way to | ||
217 | // do it. | ||
218 | if (Prototype.Browser.IE) | ||
219 | { | ||
220 | this.heightTable.thumbnail += 2; | ||
221 | this.heightTable.content += 24; | ||
222 | } | ||
223 | |||
224 | if (Prototype.Browser.IE && this.resourceContainer) | ||
225 | { | ||
226 | this.heightTable.resources -= 30; | ||
227 | } | ||
228 | |||
229 | if (Prototype.Browser.IE && this.htmlDiv && this.thumbStrip && !this.resourceContainer) | ||
230 | { | ||
231 | this.heightTable.thumbnails += 10; | ||
232 | } | ||
233 | |||
234 | |||
235 | if (Prototype.Browser.IE && this.resourceContainer && !this.thumbStrip) | ||
236 | { | ||
237 | this.heightTable.resources += 70; | ||
238 | } | ||
239 | |||
240 | // Calculate the height available for content | ||
241 | this.heightTable.avaliableForContent = SME.sizes.innerCard.height - ( | ||
242 | this.heightTable.headline + | ||
243 | this.heightTable.thumbnails + | ||
244 | this.heightTable.resources + | ||
245 | this.heightTable.paddingFactor | ||
246 | ); | ||
247 | }, | ||
248 | |||
249 | /* | ||
250 | * Setup the scroll bar on the wide content area. | ||
251 | */ | ||
252 | _setupScroller: function() | ||
253 | { | ||
254 | // We only want to show the scroll bar if there is a need for it | ||
255 | if (this.heightTable.content < this.heightTable.avaliableForContent) | ||
256 | { | ||
257 | return; | ||
258 | } | ||
259 | |||
260 | this.contentArea.appendChild( | ||
261 | this.scrollCont = Builder.node('div', | ||
262 | { | ||
263 | style: 'border-left: 1px solid ' + this.card.options.color + ';' + | ||
264 | 'width: 1px; right: 0px; position: absolute; ' + | ||
265 | 'height:' + this.heightTable.avaliableForContent + 'px; ' + | ||
266 | 'top: ' + (this.heightTable.headline + 10) + 'px;' | ||
267 | }, | ||
268 | |||
269 | this.scrollBar = Builder.node('img', | ||
270 | { | ||
271 | src: 'images/pill.gif', | ||
272 | style: 'display: block; margin-left: -3px; cursor: move; ' + | ||
273 | 'background: ' + this.card.options.color + ';' | ||
274 | }) | ||
275 | )); | ||
276 | |||
277 | new Control.Slider(this.scrollBar, this.scrollCont, | ||
278 | { | ||
279 | axis: 'vertical', | ||
280 | range: $R(0, this.heightTable.content), | ||
281 | |||
282 | onSlide: function(value) | ||
283 | { | ||
284 | this.htmlDiv.scrollTop = value; | ||
285 | }.bind(this) | ||
286 | }); | ||
287 | |||
288 | }, | ||
289 | |||
290 | /* | ||
291 | * Main function to layout the card. | ||
292 | */ | ||
293 | layout: function() | ||
294 | { | ||
295 | this._cleanData(); | ||
296 | |||
297 | this.setColumn(this.data.contentWide[0].url, this.direction, this.data.contentWide[0].title); | ||
298 | |||
299 | this.contentArea = this.cframe.appendChild( | ||
300 | Builder.node('div', { className: 'narrowCol' }, | ||
301 | this.headLine = Builder.node('h1', { style: 'color: ' + this.color }, this.data.contentNarrow[0].title) | ||
302 | ) | ||
303 | ); | ||
304 | |||
305 | // We've gotta set the width here otherwise the height | ||
306 | // calculations will be incorrect | ||
307 | this.htmlDiv = Builder.node('div', { style: 'width: 98%; overflow: hidden; width: 300px;' }); | ||
308 | |||
309 | // Must set this early on otherwise there is no way to determine | ||
310 | // the actual height of the content div | ||
311 | this.setHTML(this.data.contentNarrow[0].htmlContent); | ||
312 | this.contentArea.appendChild(this.htmlDiv); | ||
313 | |||
314 | this.getResourceList(); | ||
315 | this.getThumbnails(); | ||
316 | |||
317 | // IE doesn't let prototype mess with the default object | ||
318 | // prototypes so we have to manually extend them. Sigh... | ||
319 | [ | ||
320 | this.headLine, | ||
321 | this.htmlDiv, | ||
322 | this.thumbStrip, | ||
323 | this.resourceContainer, | ||
324 | this.contentArea | ||
325 | ].each(Element.extend); | ||
326 | |||
327 | // Set these styles up here or we get a -21px bug in our | ||
328 | // calculation code | ||
329 | this.contentArea.setStyle( | ||
330 | { | ||
331 | width: '310px', | ||
332 | position: 'absolute', | ||
333 | top: '0px' | ||
334 | }); | ||
335 | |||
336 | this._calculateHeights(); | ||
337 | this._setupScroller(); | ||
338 | |||
339 | // We set this last, after all the height calculations are | ||
340 | // completed. That makes it a lot easier to work with. | ||
341 | this.htmlDiv.setStyle({ height: this.heightTable.avaliableForContent + 'px' }); | ||
342 | |||
343 | // Webkit doesn't pad things correctly | ||
344 | if (this.direction == 'left') | ||
345 | { | ||
346 | this.contentArea.setStyle({ left: '10px' }); | ||
347 | } | ||
348 | else | ||
349 | { | ||
350 | this.contentArea.setStyle({ right: '10px' }); | ||
351 | } | ||
352 | |||
353 | // If this is an intro card do the intro card stuff. | ||
354 | if (this.data.template == 'intro') | ||
355 | { | ||
356 | this._doIntro(); | ||
357 | } | ||
358 | |||
359 | return this; | ||
360 | }, | ||
361 | |||
362 | /* | ||
363 | * Setup an intro card. Because intro cards aren't really that much | ||
364 | * different from a basic card and because class inheritance sucks in | ||
365 | * Prototype > 1.6 I'm just going to add this into here. | ||
366 | */ | ||
367 | _doIntro: function() | ||
368 | { | ||
369 | this.contentArea.appendChild(Builder.node('div', { className: 'skipbox' }, | ||
370 | this.skipLink = Builder.node('a', { href: '#tablehome', className: 'skiplink' }, | ||
371 | [ | ||
372 | Strings.skipButton, | ||
373 | Builder.node('img', { src: 'images/arrow_right_grey.gif', className: 'skipimg' }) | ||
374 | ]) | ||
375 | )); | ||
376 | }, | ||
377 | |||
378 | /* | ||
379 | * Throw the card into a popup window for printing. | ||
380 | */ | ||
381 | print: function() | ||
382 | { | ||
383 | var wind = window.open('', '', 'width=' + SME.sizes.innerCard.width + ',height=' + (SME.sizes.innerCard.height + 60)); | ||
384 | |||
385 | with (wind) | ||
386 | { | ||
387 | with (document) | ||
388 | { | ||
389 | write('<html><head><style type="text/css">@import url(application.css); .narrowCol{top: 30px !important;}</style></head><body>'); | ||
390 | write('<img src="images/logo.jpg" style="display: block; margin: 5px 0px 30px 5px;"/>'); | ||
391 | write(this.cframe.innerHTML); | ||
392 | write('</body></html>'); | ||
393 | close(); | ||
394 | } | ||
395 | |||
396 | print(); | ||
397 | close(); | ||
398 | } | ||
399 | |||
400 | return true; | ||
401 | }, | ||
402 | |||
403 | /* | ||
404 | * Debug the card layout. Generally this should be used to spot out | ||
405 | * data issues. But it could also be used for other debugging purposes. | ||
406 | */ | ||
407 | _debug: function() | ||
408 | { | ||
409 | |||
410 | } | ||
411 | }); \ No newline at end of file | ||
diff --git a/docroot/classes/layouts/layout.special.class.js b/docroot/classes/layouts/layout.special.class.js new file mode 100755 index 0000000..b285390 --- /dev/null +++ b/docroot/classes/layouts/layout.special.class.js | |||
@@ -0,0 +1,43 @@ | |||
1 | /* | ||
2 | * Material Experience - Special Case Layout Engine | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | */ | ||
8 | |||
9 | Card.Layout.Special = Class.create(); | ||
10 | Object.extend(Object.extend(Card.Layout.Special.prototype, Card.Layout.prototype), | ||
11 | { | ||
12 | /* | ||
13 | * Initialize the layout class | ||
14 | */ | ||
15 | initialize: function(cframe, data, card) | ||
16 | { | ||
17 | this.cframe = cframe; | ||
18 | this.data = data; | ||
19 | this.card = card; | ||
20 | this.color = this.card.options.color; | ||
21 | this.hasResources = false; | ||
22 | |||
23 | // Check if the url is provided otherwise pull it out of the | ||
24 | // wide content field. | ||
25 | if (!this.data.url) | ||
26 | { | ||
27 | this.data.url = this.data.contentWide[0].url; | ||
28 | } | ||
29 | }, | ||
30 | |||
31 | /* | ||
32 | * Main function to layout the card. | ||
33 | */ | ||
34 | layout: function() | ||
35 | { | ||
36 | this.cframe.innerHTML = '<iframe ' + | ||
37 | 'src = "' + this.data.url + '" ' + | ||
38 | 'frameborder = "0" ' + | ||
39 | 'width = "' + (SME.sizes.card.width - 11) + '" ' + | ||
40 | 'height = "' + (SME.sizes.card.height - 40) + '"' + | ||
41 | '></iframe>'; | ||
42 | } | ||
43 | }); \ No newline at end of file | ||
diff --git a/docroot/classes/overlay.class.js b/docroot/classes/overlay.class.js new file mode 100755 index 0000000..9a6e110 --- /dev/null +++ b/docroot/classes/overlay.class.js | |||
@@ -0,0 +1,118 @@ | |||
1 | /* | ||
2 | * Material Experience - Document Overlay Class | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | */ | ||
8 | |||
9 | var Overlay = Class.create(); | ||
10 | Object.extend(Overlay.prototype, | ||
11 | { | ||
12 | /* | ||
13 | * Initializes the overlay class | ||
14 | */ | ||
15 | initialize: function() | ||
16 | { | ||
17 | this.options = Object.extend( | ||
18 | { | ||
19 | zIndex: 9000, // z-index of the overlay | ||
20 | background: 'white', // Overlay color | ||
21 | opacity: 0.5, // Overlay opacity | ||
22 | fadeInSpeed: 0.2, // Fade in speed | ||
23 | fadeOutSpeed: 1, // Fade out speed | ||
24 | onClick: this.hide, // Callback for overlay click | ||
25 | onShow: Prototype.emptyFunction, // Callback when the overlay is shown | ||
26 | onHide: Prototype.emptyFunction // Callback when the overlay is hidden | ||
27 | }, $H(arguments[0]) || {}); | ||
28 | |||
29 | this._createOverlay(); | ||
30 | }, | ||
31 | |||
32 | /* | ||
33 | * Creates the actual DOM elements of the overlay. | ||
34 | */ | ||
35 | _createOverlay: function() | ||
36 | { | ||
37 | this.overlay = Element.extend(document.createElement('div')); | ||
38 | document.body.appendChild(this.overlay); | ||
39 | |||
40 | Event.observe(this.overlay, 'click', this.options.onClick.bindAsEventListener(this)); | ||
41 | |||
42 | this.overlay.setStyle( | ||
43 | { | ||
44 | backgroundColor: this.options.background, | ||
45 | zIndex: this.options.zIndex, | ||
46 | height: '100%', | ||
47 | width: '100%', | ||
48 | position: 'absolute', | ||
49 | display: 'none', | ||
50 | top: 0, | ||
51 | left: 0 | ||
52 | }); | ||
53 | }, | ||
54 | |||
55 | /* | ||
56 | * Displays the overlay. | ||
57 | */ | ||
58 | show: function() | ||
59 | { | ||
60 | this._fixBody(); | ||
61 | |||
62 | this.options.onShow(this); | ||
63 | |||
64 | new Effect.Appear(this.overlay, | ||
65 | { | ||
66 | to: this.options.opacity, | ||
67 | duration: this.options.fadeInSpeed, | ||
68 | limit: 1 | ||
69 | }); | ||
70 | }, | ||
71 | |||
72 | /* | ||
73 | * Hides the overlay. | ||
74 | */ | ||
75 | hide: function() | ||
76 | { | ||
77 | this.options.onHide(this); | ||
78 | |||
79 | new Effect.Fade(this.overlay, | ||
80 | { | ||
81 | duration: this.options.fadeOutSpeed, | ||
82 | limit: 1, | ||
83 | |||
84 | afterFinish: function() | ||
85 | { | ||
86 | this._fixBody(true); | ||
87 | }.bind(this) | ||
88 | }); | ||
89 | }, | ||
90 | |||
91 | /* | ||
92 | * Removes the overlay from the DOM | ||
93 | */ | ||
94 | destroy: function() | ||
95 | { | ||
96 | document.body.removeChild(this.overlay); | ||
97 | }, | ||
98 | |||
99 | /* | ||
100 | * Fix for IE 6, sets the body height and overflow so people can not | ||
101 | * scroll beyond the overlay. Disable overflow on the body and html | ||
102 | * so you can see the whole overlay. | ||
103 | */ | ||
104 | _fixBody: function(reset) | ||
105 | { | ||
106 | var myHeight = reset ? '' : '100%'; | ||
107 | var myOverflow = reset ? '' : 'hidden'; | ||
108 | |||
109 | $A([document.body,document.getElementsByTagName('html')[0]]).each(function(t) | ||
110 | { | ||
111 | Element.extend(t).setStyle( | ||
112 | { | ||
113 | height: myHeight, | ||
114 | overflow: myOverflow | ||
115 | }); | ||
116 | }); | ||
117 | } | ||
118 | }); \ No newline at end of file | ||
diff --git a/docroot/classes/roundcorners.class.js b/docroot/classes/roundcorners.class.js new file mode 100755 index 0000000..c156d30 --- /dev/null +++ b/docroot/classes/roundcorners.class.js | |||
@@ -0,0 +1,60 @@ | |||
1 | /* | ||
2 | * Material Experience - Card Chip Class | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | * | ||
8 | * An HTML based approach to drawing rounded corners using element | ||
9 | * "slices" to create the corners instead of images. This depends | ||
10 | * on a section in the stylesheet to work correctly. | ||
11 | * | ||
12 | * Code adapted from: http://www.html.it/articoli/nifty/index.html | ||
13 | */ | ||
14 | |||
15 | var RoundedCorners = Class.create(); | ||
16 | Object.extend(RoundedCorners.prototype, | ||
17 | { | ||
18 | // Direction table for rounded corners. | ||
19 | directions: | ||
20 | { | ||
21 | top : 'top', | ||
22 | bottom : 'bottom' | ||
23 | }, | ||
24 | |||
25 | /* | ||
26 | * Initialize the object | ||
27 | */ | ||
28 | initialize: function(color) | ||
29 | { | ||
30 | this.color = color; | ||
31 | }, | ||
32 | |||
33 | /* | ||
34 | * Creates a rounded corner container. | ||
35 | */ | ||
36 | get: function(direction) | ||
37 | { | ||
38 | var slicebox = document.createElement('b'); | ||
39 | slicebox.className = 'round'; | ||
40 | |||
41 | if (direction == this.directions.top) | ||
42 | { | ||
43 | var myEnum = $A($R(1,10)); | ||
44 | } | ||
45 | else | ||
46 | { | ||
47 | var myEnum = $A($R(1,10)).reverse(); | ||
48 | } | ||
49 | |||
50 | myEnum.each(function(item) | ||
51 | { | ||
52 | var corner = document.createElement('b'); | ||
53 | corner.className = 'rcSlice_' + item; | ||
54 | corner.style.backgroundColor = this.color; | ||
55 | slicebox.appendChild(corner); | ||
56 | }.bind(this)); | ||
57 | |||
58 | return slicebox; | ||
59 | } | ||
60 | }); \ No newline at end of file | ||
diff --git a/docroot/classes/sketchbook.class.js b/docroot/classes/sketchbook.class.js new file mode 100755 index 0000000..d62101e --- /dev/null +++ b/docroot/classes/sketchbook.class.js | |||
@@ -0,0 +1,288 @@ | |||
1 | /* | ||
2 | * Material Experience - Sketchbook Class | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | */ | ||
8 | |||
9 | var Sketchbook = Class.create(); | ||
10 | |||
11 | Object.extend(Sketchbook.prototype, | ||
12 | { | ||
13 | /* | ||
14 | * Initializes a new sketchbook and table. | ||
15 | */ | ||
16 | initialize: function() | ||
17 | { | ||
18 | // Sketchbook chip data store | ||
19 | this.dataStore = []; | ||
20 | |||
21 | this.table = new CardTable( | ||
22 | { | ||
23 | color: SME.colors.grey, | ||
24 | name: Strings.sketchbook, | ||
25 | id: 'sketchbook', | ||
26 | |||
27 | onRender: function(table) | ||
28 | { | ||
29 | table.table.appendChild(Builder.node('img', | ||
30 | { | ||
31 | id: 'sbTrash', | ||
32 | src: 'images/trash_can.jpg' | ||
33 | })); | ||
34 | |||
35 | Droppables.add($('sbTrash'), | ||
36 | { | ||
37 | accept: 'chip', | ||
38 | |||
39 | onDrop: function(chip) | ||
40 | { | ||
41 | this.removeChip(chip); | ||
42 | }.bind(this) | ||
43 | }); | ||
44 | }.bind(this) | ||
45 | }); | ||
46 | |||
47 | this.hide(); | ||
48 | |||
49 | if (Sketchbook.loggedIn) | ||
50 | { | ||
51 | this.loadData(); | ||
52 | } | ||
53 | |||
54 | // Be sure to save everything when the window closes | ||
55 | Event.observe(window, 'unload', this.saveData.bindAsEventListener(this)); | ||
56 | }, | ||
57 | |||
58 | /* | ||
59 | * Loads sketchbook data from the server and calls a function to merge | ||
60 | * that data into the currently loaded sketchbook. | ||
61 | */ | ||
62 | loadData: function() | ||
63 | { | ||
64 | new Ajax.Request(SME.url.sketchBookIN, | ||
65 | { | ||
66 | method: 'get', | ||
67 | |||
68 | onSuccess: function(transport) | ||
69 | { | ||
70 | this.dataStore = transport.responseText.cleanJSON().evalJSON(); | ||
71 | this._mergeSketchbooks(); | ||
72 | }.bind(this) | ||
73 | }); | ||
74 | }, | ||
75 | |||
76 | /* | ||
77 | * Merges the currently active (presumably guest) sketchbook with the | ||
78 | * data loaded from the server. Prevents the user from losing data when | ||
79 | * they login after adding chips to a guest sketchbook. | ||
80 | */ | ||
81 | _mergeSketchbooks: function() | ||
82 | { | ||
83 | this.dataStore.each(function(chip) | ||
84 | { | ||
85 | if (!this.table.hasChip(chip.cid)) | ||
86 | { | ||
87 | this.table.addChip(chip, { animate: false }); | ||
88 | } | ||
89 | }.bind(this)); | ||
90 | }, | ||
91 | |||
92 | /* | ||
93 | * Add a chip to the sketchbook. This function does duplicate detection | ||
94 | * using methods provided by the CardTable class to avoid adding a chip | ||
95 | * more than once. The function also updates the sketchbook's data | ||
96 | * store. | ||
97 | */ | ||
98 | addChip: function(contID) | ||
99 | { | ||
100 | if (this.table.hasChip(contID)) | ||
101 | { | ||
102 | new Bezel().show(Strings.alreadyAddedSB); | ||
103 | return; | ||
104 | } | ||
105 | |||
106 | new Ajax.Request(SME.url.chipResolver.evaluate({card: contID}), | ||
107 | { | ||
108 | method: 'get', | ||
109 | |||
110 | onSuccess: function(transport) | ||
111 | { | ||
112 | var data = transport.responseText.cleanJSON().evalJSON(); | ||
113 | var windSize = window.getDimensions(); | ||
114 | |||
115 | this.table.addChip(data, | ||
116 | { | ||
117 | x: Math.floor(1 + (windSize.width - 1) * Math.random()), | ||
118 | y: Math.floor(1 + ((windSize.height - 120) - 1) * Math.random()), | ||
119 | animate: false | ||
120 | }); | ||
121 | |||
122 | // Per BS: Would like different messages if logged in or not | ||
123 | if (Sketchbook.loggedIn) | ||
124 | { | ||
125 | new Bezel().show(Strings.addedSketchbook); | ||
126 | } | ||
127 | else | ||
128 | { | ||
129 | new Bezel({ fadeTime: 5 }).show(Strings.addedGuestSB); | ||
130 | } | ||
131 | |||
132 | this.saveData(); | ||
133 | }.bind(this) | ||
134 | }); | ||
135 | }, | ||
136 | |||
137 | /* | ||
138 | * Remove a chip from the table by DOM node. | ||
139 | */ | ||
140 | removeChip: function(chip) | ||
141 | { | ||
142 | this.table.removeChip(chip.classLink.options.contID); | ||
143 | this.saveData(); | ||
144 | }, | ||
145 | |||
146 | /* | ||
147 | * Serialize the datastore and post it back to the server for safe | ||
148 | * keeping. | ||
149 | */ | ||
150 | saveData: function() | ||
151 | { | ||
152 | new Ajax.Request(SME.url.sketchBook, | ||
153 | { | ||
154 | method: 'post', | ||
155 | asynchronous: false, | ||
156 | parameters: { 'sketchbook_data': this.table.getChipData().toJSON() } | ||
157 | }); | ||
158 | }, | ||
159 | |||
160 | /* | ||
161 | * Show the sketchbook table or if not logged in show a login screen. | ||
162 | */ | ||
163 | show: function() | ||
164 | { | ||
165 | if (!Sketchbook.loggedIn) | ||
166 | { | ||
167 | Sketchbook.showLoginScreen(); | ||
168 | } | ||
169 | else | ||
170 | { | ||
171 | this.table.show(); | ||
172 | } | ||
173 | }, | ||
174 | |||
175 | /* | ||
176 | * Hide the sketchbook table. | ||
177 | */ | ||
178 | hide: function() | ||
179 | { | ||
180 | this.table.hide(); | ||
181 | } | ||
182 | }); | ||
183 | |||
184 | Object.extend(Sketchbook, | ||
185 | { | ||
186 | // Flag to determine if the user is logged in or not. | ||
187 | loggedIn: false, | ||
188 | |||
189 | // Username of the logged in user. | ||
190 | username: null, | ||
191 | |||
192 | // Show the login screen in a special case card. | ||
193 | showLoginScreen: function() | ||
194 | { | ||
195 | var t = new Card( | ||
196 | { | ||
197 | color: SME.colors.grey, | ||
198 | title: Strings.pleaseLogin, | ||
199 | addExtraButtons: false, | ||
200 | |||
201 | onClose: function() | ||
202 | { | ||
203 | Sketchbook.checkLogin(); | ||
204 | } | ||
205 | }); | ||
206 | |||
207 | t.setLayout(Card.Layout.Special, { url: SME.url.loginScreen }); | ||
208 | t.show(); | ||
209 | }, | ||
210 | |||
211 | /* | ||
212 | * Once the user has successfully logged in do some actions to setup | ||
213 | * the user interface. | ||
214 | */ | ||
215 | doLoggedIn: function() | ||
216 | { | ||
217 | // Assume that if your calling this function the login succeeded | ||
218 | Sketchbook.loggedIn = true; | ||
219 | |||
220 | $$('div.history')[0].innerHTML = 'Hello ' + Sketchbook.username + '! | ' + | ||
221 | '<a href="http://santoprene.com/cgi-bin/protected/register/logout_designer.pl">' + Strings.logout + '</a> | ' + | ||
222 | '<a href="#" class="manage">' + Strings.manageAccount + '</a> | ' + | ||
223 | '<a href="#" class="history">' + Strings.myHistory + '</a>'; | ||
224 | |||
225 | // On mouseover of the history link show the dropdown. | ||
226 | // We need to re-attach it here because we're changing the | ||
227 | // links (above) and that causes the DOM to lose the original | ||
228 | // event. | ||
229 | $$('div.history a.history')[0].observe('mouseover', function() | ||
230 | { | ||
231 | SME.history.getDropDown(); | ||
232 | }); | ||
233 | |||
234 | $$('a.manage')[0].observe('click', function(event) | ||
235 | { | ||
236 | var t = new Card( | ||
237 | { | ||
238 | color: SME.colors.grey, | ||
239 | addExtraButtons: false, | ||
240 | title: Strings.manageAccount | ||
241 | }); | ||
242 | |||
243 | t.setLayout(Card.Layout.Special, { url: SME.url.manageAccount }); | ||
244 | t.show(); | ||
245 | |||
246 | Event.stop(event); | ||
247 | }); | ||
248 | }, | ||
249 | |||
250 | /* | ||
251 | * Check that the user actually logged in. | ||
252 | */ | ||
253 | checkLogin: function() | ||
254 | { | ||
255 | // If the site cookie doesn't exist no point even going on | ||
256 | if (!$C('Site')) | ||
257 | { | ||
258 | return; | ||
259 | } | ||
260 | |||
261 | /* | ||
262 | * Site cookie is in a weird format, basically sub-cookies are | ||
263 | * separated by & signs. The username and password of the | ||
264 | * currently logged in user is store as username:password in | ||
265 | * the sub-cookie called cookie. Oh yeah and the | ||
266 | * username/password pair is base64 encoded. | ||
267 | */ | ||
268 | var username = decodeBase64($C('Site').split('Cookie&')[1]).split(':')[0]; | ||
269 | Sketchbook.username = username; | ||
270 | |||
271 | /* | ||
272 | * Per Andy: the username and password won't be in the cookie | ||
273 | * (even though the cookie itself is set) if the login failed. | ||
274 | * So lets assume that if we find a username in the appropriate | ||
275 | * place in the cookie we can go ahead and log the user in. | ||
276 | */ | ||
277 | if (username) | ||
278 | { | ||
279 | Sketchbook.loggedIn = true; | ||
280 | Sketchbook.doLoggedIn(); | ||
281 | return true; | ||
282 | } | ||
283 | else | ||
284 | { | ||
285 | return false; | ||
286 | } | ||
287 | } | ||
288 | }); \ No newline at end of file | ||
diff --git a/docroot/classes/sme.namespace.js b/docroot/classes/sme.namespace.js new file mode 100755 index 0000000..05ad125 --- /dev/null +++ b/docroot/classes/sme.namespace.js | |||
@@ -0,0 +1,140 @@ | |||
1 | /* | ||
2 | * Material Experience - Santoprene Material Experience Namespace | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | * | ||
8 | * The Santoprene Materials Experience Namespace holds all non-generic data | ||
9 | * and methods governing the working of this application. | ||
10 | * | ||
11 | * There may be a lot of things set here that are used at runtime but I | ||
12 | * explicitly define them in the SME namespace so other developers can see | ||
13 | * exactly what is contained here. There are also some dynamically defined | ||
14 | * variables based on the current querystring. | ||
15 | */ | ||
16 | |||
17 | var SME = | ||
18 | { | ||
19 | /* | ||
20 | * Release version of the site. This has nothing to do with the SVN | ||
21 | * version of the site. This is not used right now but should be | ||
22 | * incremented with each production release of the site. | ||
23 | */ | ||
24 | releaseVersion: '1.2.2', | ||
25 | |||
26 | /* | ||
27 | * The color table exists to provide aliases to the colors used in the | ||
28 | * interface. This will make it much easier to change at a later date | ||
29 | * should that ever be required. The color table should be used only | ||
30 | * for default values. | ||
31 | */ | ||
32 | colors: | ||
33 | { | ||
34 | blue : 'rgb(100, 129, 145)', | ||
35 | brown : 'rgb(100, 065, 040)', | ||
36 | green : 'rgb(100, 150, 040)', | ||
37 | grey : 'rgb(100, 100, 100)', | ||
38 | orange : 'rgb(225, 125, 010)', | ||
39 | red : 'rgb(150, 035, 020)' | ||
40 | }, | ||
41 | |||
42 | /* | ||
43 | * The sizes table governs the size of the user interface elements. | ||
44 | * This exists for much the same reason as the color table, to provide | ||
45 | * an easy way to change sizes later. Note that the rest of the styles | ||
46 | * in the interface should be set relatively so changing these | ||
47 | * parameters should not destroy the interface. | ||
48 | */ | ||
49 | sizes: | ||
50 | { | ||
51 | card : { height: 520, width: 925 }, // Card Size | ||
52 | innerCard : { height: 481, width: 914 }, // Inside Size of Card | ||
53 | chipMax : { height: 180, width: 143 }, // Maximum Chip Size | ||
54 | windMin : { height: 589, width: 1020 } // Minimum Window Size | ||
55 | }, | ||
56 | |||
57 | // URLs Used by the Application | ||
58 | url: | ||
59 | { | ||
60 | tableList : 'data/card_tables.js', | ||
61 | perisitChips : 'data/persist_chip.js', | ||
62 | loginScreen : 'http://' + window.location.hostname + '/cgi-bin/login.pl', | ||
63 | sketchBook : 'http://' + window.location.hostname + '/cgi-bin/sketchbook.pl', | ||
64 | sketchBookIN : 'http://' + window.location.hostname + '/cgi-bin/sketchbook.pl?interactive=false', | ||
65 | manageAccount: 'http://www.santoprene.com/cgi-bin/protected/register/account.pl?template=designer_', | ||
66 | introCards : 'http://' + window.location.hostname + '/cgi-bin/sop_proxy.pl?site=http://www.santoprene.com/cms/designer_json/224fa06db73ece8a/index.html', | ||
67 | cards : new Template('http://' + window.location.hostname + '/cgi-bin/sop_proxy.pl?site=http://www.santoprene.com/cms/designer_card/#{card}/index.html'), | ||
68 | sendToFriend : new Template('http://www.santoprene.com/cgi-bin/send_page/send.pl?tmpl=designer&title=#{title}&durl=#{durl}'), | ||
69 | cardTables : new Template('http://' + window.location.hostname + '/cgi-bin/card_table.pl?table=#{table}&h=#{h}&w=#{w}'), | ||
70 | chipResolver : new Template('http://' + window.location.hostname + '/cgi-bin/sketchbook_resolver.pl?card=#{card}'), | ||
71 | cardPreview : new Template('http://' + window.location.hostname + '/cgi-bin/sop_preview_proxy.pl?site=http://admin.santoprene.com/cgi-bin/content/display_content.pl?form_id=48&content_id=#{card}&site_id=1'), | ||
72 | |||
73 | // Debugging and Development URLS (removed by build system) | ||
74 | /*;;;*/ chipResolver : new Template('http://' + window.location.hostname + '/cgi-bin/sop_proxy.pl?site=http://materialexperience.santoprene.com/cgi-bin/sketchbook_resolver.pl?card=#{card}'), | ||
75 | /*;;;*/ cardTables : new Template('http://' + window.location.hostname + '/cgi-bin/sop_proxy.pl?site=http://materialexperience.santoprene.com/cgi-bin/card_table.pl?table=#{table}&h=#{h}&w=#{w}'), | ||
76 | // END Debugging and Development URLS | ||
77 | |||
78 | // Dummy item terminates the list since the items above are | ||
79 | // removed by the build system and the trailing comma would | ||
80 | // break in IE. Don't Remove this! | ||
81 | dontRemove: null | ||
82 | }, | ||
83 | |||
84 | /* | ||
85 | * Compose layout name to layout engine mapping. Allows us to add more | ||
86 | * layout engines later a lot simpler than the previous method allowed. | ||
87 | */ | ||
88 | engineMapping : | ||
89 | { | ||
90 | 'narrow-left' : Card.Layout.Primary, | ||
91 | 'narrow-right' : Card.Layout.Primary, | ||
92 | 'special' : Card.Layout.Special, | ||
93 | 'intro' : Card.Layout.Primary, | ||
94 | 'custom-menu' : Card.Layout.Custom | ||
95 | }, | ||
96 | |||
97 | /* | ||
98 | * When the page is called with a query string of debug then you will | ||
99 | * be able to open a firebug console and view debugging messages. Also | ||
100 | * checks to make sure console.log() is actually defined to avoid | ||
101 | * throwing errors on browsers w/out firebug. | ||
102 | */ | ||
103 | debug: /debug=.*(true)/.test(window.location.search) && !!(console.log), | ||
104 | |||
105 | /* | ||
106 | * Skip the intro card because it wastes time during development. | ||
107 | */ | ||
108 | skipIntro: /.*intro=false.*/.test(window.location.search), | ||
109 | |||
110 | /* | ||
111 | * | ||
112 | * RUNTIME VARIABLES SECTION | ||
113 | * | ||
114 | * These are variables set at runtime but declared explicitly here | ||
115 | * so other developers can have a clear picture of what is in this | ||
116 | * namespace. | ||
117 | * | ||
118 | * If you set a variable in this namespace at runtime please declare | ||
119 | * it explicitly here. | ||
120 | * | ||
121 | */ | ||
122 | |||
123 | // Array of all loaded tables. | ||
124 | tables: [], | ||
125 | |||
126 | // AJAX information bezel. | ||
127 | AJAXBezel: null, | ||
128 | |||
129 | // Currently active card table. | ||
130 | currentTable: null, | ||
131 | |||
132 | // Sketchbook. | ||
133 | sketchbook: null, | ||
134 | |||
135 | // History manager. | ||
136 | history: null, | ||
137 | |||
138 | // Card currently being displayed. | ||
139 | currentCard: null | ||
140 | }; | ||
diff --git a/docroot/classes/table.class.js b/docroot/classes/table.class.js new file mode 100755 index 0000000..1784c5c --- /dev/null +++ b/docroot/classes/table.class.js | |||
@@ -0,0 +1,315 @@ | |||
1 | /* | ||
2 | * Material Experience - Card Table Class | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | */ | ||
8 | |||
9 | var CardTable = Class.create(); | ||
10 | |||
11 | Object.extend(CardTable.prototype, | ||
12 | { | ||
13 | /* | ||
14 | * Create the table object. | ||
15 | */ | ||
16 | initialize: function() | ||
17 | { | ||
18 | this.options = Object.extend( | ||
19 | { | ||
20 | name: 'New Table', // Table Name | ||
21 | color: SME.colors.blue, // Table Color | ||
22 | id: null, // Table ID | ||
23 | decorate: true, // Draw table decorator | ||
24 | onRender: Prototype.emptyFunction // Callback function when the table renders | ||
25 | }, arguments[0] || {}); | ||
26 | |||
27 | // Cache of the chips on the table | ||
28 | this.chips = []; | ||
29 | |||
30 | // Cache of table JSON data | ||
31 | this.data = null; | ||
32 | |||
33 | this._createTable(); | ||
34 | |||
35 | // Initially resizes the window to the correct height, and keep | ||
36 | // resizing it as the window changes | ||
37 | this.resize(); | ||
38 | Event.observe(window, 'resize', function() | ||
39 | { | ||
40 | this.resize(); | ||
41 | }.bindAsEventListener(this)); | ||
42 | |||
43 | if (this.options.decorate) | ||
44 | { | ||
45 | this._createAppDecorator(); | ||
46 | } | ||
47 | }, | ||
48 | |||
49 | /* | ||
50 | * Create the table DOM object. | ||
51 | */ | ||
52 | _createTable: function() | ||
53 | { | ||
54 | this.table = document.createElement('div'); | ||
55 | |||
56 | if (this.options.id) | ||
57 | { | ||
58 | this.table.id = this.options.id; | ||
59 | } | ||
60 | |||
61 | this.table.className = 'table'; | ||
62 | document.body.appendChild(this.table); | ||
63 | |||
64 | this.options.onRender(this); | ||
65 | }, | ||
66 | |||
67 | /* | ||
68 | * Load the chip data from the server and cache it in the object. | ||
69 | */ | ||
70 | loadChipData: function(feedURL, urlParams, showAfterLoad) | ||
71 | { | ||
72 | new Ajax.Request(feedURL.evaluate(urlParams), | ||
73 | { | ||
74 | method: 'get', | ||
75 | |||
76 | onSuccess: function(transport) | ||
77 | { | ||
78 | this.data = transport.responseText.evalJSON(); | ||
79 | |||
80 | if (showAfterLoad || this.options.id == SME.currentTable) | ||
81 | this.showChips(); | ||
82 | }.bind(this) | ||
83 | }); | ||
84 | }, | ||
85 | |||
86 | /* | ||
87 | * Creates the decorator icon show in the footer of the user interface | ||
88 | * which just links to a table hash and relies on the history manager | ||
89 | * to load the appropriate table. | ||
90 | */ | ||
91 | _createAppDecorator: function() | ||
92 | { | ||
93 | var link = Builder.node('a', | ||
94 | { | ||
95 | href: '#table' + this.options.id, | ||
96 | style: 'color: ' + this.options.color, | ||
97 | alt: this.options.name + ' Table', | ||
98 | title: this.options.name + ' Table' | ||
99 | }, | ||
100 | [ | ||
101 | Builder.node('img', | ||
102 | { | ||
103 | className: 'appDecorator', | ||
104 | src: 'images/pill.gif', | ||
105 | style: 'background: ' + this.options.color | ||
106 | }), | ||
107 | |||
108 | this.options.name.toUpperCase() | ||
109 | ]); | ||
110 | |||
111 | $('footer').appendChild(link); | ||
112 | }, | ||
113 | |||
114 | /* | ||
115 | * Show chips on the table once data is loaded. This function creates | ||
116 | * and caches chip objects on the table. | ||
117 | */ | ||
118 | showChips: function() | ||
119 | { | ||
120 | // Only load the chips once | ||
121 | if (this.chipsDone || !this.data) | ||
122 | { | ||
123 | return; | ||
124 | } | ||
125 | else | ||
126 | { | ||
127 | this.chipsDone = true; | ||
128 | } | ||
129 | |||
130 | this.bez = new Bezel( | ||
131 | { | ||
132 | onShow: function() | ||
133 | { | ||
134 | this.data.each(function(chip) | ||
135 | { | ||
136 | this.addChip(chip); | ||
137 | }.bind(this)); | ||
138 | |||
139 | this.chips.each(function(chip) | ||
140 | { | ||
141 | chip.animate(); | ||
142 | }); | ||
143 | }.bind(this) | ||
144 | }).show(Strings.loadingNoAnim); | ||
145 | }, | ||
146 | |||
147 | /* | ||
148 | * Remove a chip from the table. | ||
149 | */ | ||
150 | removeChip: function(chipContID) | ||
151 | { | ||
152 | this.chips.each(function(chip) | ||
153 | { | ||
154 | if (chip.options.contID == chipContID) | ||
155 | { | ||
156 | chip.hide(); | ||
157 | this.chips = this.chips.without(chip); | ||
158 | } | ||
159 | }.bind(this)) | ||
160 | }, | ||
161 | |||
162 | /* | ||
163 | * Add a chip to the table. | ||
164 | */ | ||
165 | addChip: function(data) | ||
166 | { | ||
167 | var cords = Object.extend( | ||
168 | { | ||
169 | x: data.x, | ||
170 | y: data.y, | ||
171 | animate: true | ||
172 | }, arguments[1] || {}); | ||
173 | |||
174 | var chip = new CardChip( | ||
175 | { | ||
176 | x: cords.x, | ||
177 | y: cords.y, | ||
178 | category: data.category, | ||
179 | contID: data.cid, | ||
180 | title: data.title, | ||
181 | image: data.chip, | ||
182 | locked: data.locked || false, | ||
183 | animate: cords.animate | ||
184 | }); | ||
185 | |||
186 | chip.addTo(this.table); | ||
187 | this.chips.push(chip); | ||
188 | }, | ||
189 | |||
190 | /* | ||
191 | * Clear out the chip cache. This function is not to be used within | ||
192 | * the code. It exists to be called manually on the command line for | ||
193 | * debugging purposes. | ||
194 | */ | ||
195 | _blowChips: function() | ||
196 | { | ||
197 | this.chips = []; | ||
198 | }, | ||
199 | |||
200 | /* | ||
201 | * Serialize all the chip data on the table to an object, presumably | ||
202 | * to be converted to JSON later. The object should match the output | ||
203 | * of card_table.pl | ||
204 | */ | ||
205 | getChipData: function() | ||
206 | { | ||
207 | var data = []; | ||
208 | |||
209 | this.chips.each(function(c) | ||
210 | { | ||
211 | data.push(c._serialize()); | ||
212 | }); | ||
213 | |||
214 | return data; | ||
215 | }, | ||
216 | |||
217 | /* | ||
218 | * Tests if the table contains a chip with the particular content ID. | ||
219 | */ | ||
220 | hasChip: function(chipID) | ||
221 | { | ||
222 | var chip = this.chips.find(function(item) | ||
223 | { | ||
224 | if (item.options.contID == chipID) | ||
225 | return true; | ||
226 | |||
227 | return false; | ||
228 | }); | ||
229 | |||
230 | return chip ? true : false; | ||
231 | }, | ||
232 | |||
233 | /* | ||
234 | * Changes the size of the table when the browser window is resized. | ||
235 | */ | ||
236 | resize: function() | ||
237 | { | ||
238 | this.table.style.height = ($('footer').offsetTop - | ||
239 | $('footer').offsetHeight) + 'px'; | ||
240 | }, | ||
241 | |||
242 | /* | ||
243 | * Shows the table. | ||
244 | */ | ||
245 | show: function() | ||
246 | { | ||
247 | this.showChips(); | ||
248 | SME.currentTable = this.options.id; | ||
249 | this.table.style.display = 'block'; | ||
250 | }, | ||
251 | |||
252 | /* | ||
253 | * Hides the table. | ||
254 | */ | ||
255 | hide: function() | ||
256 | { | ||
257 | this.table.style.display = 'none'; | ||
258 | } | ||
259 | }); | ||
260 | |||
261 | Object.extend(CardTable, | ||
262 | { | ||
263 | /* | ||
264 | * Show a table and hide all others. Not the special exception for the | ||
265 | * sketchbook. | ||
266 | */ | ||
267 | showTable: function(table) | ||
268 | { | ||
269 | SME.tables.each(function(item) | ||
270 | { | ||
271 | if (item.options.id == table) | ||
272 | { | ||
273 | item.show(); | ||
274 | } | ||
275 | else | ||
276 | { | ||
277 | item.hide(); | ||
278 | } | ||
279 | }); | ||
280 | |||
281 | if (table == 'sketchbook') | ||
282 | { | ||
283 | SME.sketchbook.show(); | ||
284 | } | ||
285 | else | ||
286 | { | ||
287 | SME.sketchbook.hide(); | ||
288 | } | ||
289 | }, | ||
290 | |||
291 | /* | ||
292 | * Return the color as a string for a table based on table ID. Note | ||
293 | * the special exception for special case cards which are not associated | ||
294 | * with a particular table. | ||
295 | */ | ||
296 | tableColor: function(table) | ||
297 | { | ||
298 | if (table == 'special') | ||
299 | { | ||
300 | return SME.colors.grey; | ||
301 | } | ||
302 | |||
303 | var color = SME.tables.find(function(item) | ||
304 | { | ||
305 | if (item.options.id == table) | ||
306 | { | ||
307 | return true; | ||
308 | } | ||
309 | |||
310 | return false; | ||
311 | }).options.color; | ||
312 | |||
313 | return color; | ||
314 | } | ||
315 | }); \ No newline at end of file | ||
diff --git a/docroot/classes/utility.js b/docroot/classes/utility.js new file mode 100755 index 0000000..218a059 --- /dev/null +++ b/docroot/classes/utility.js | |||
@@ -0,0 +1,199 @@ | |||
1 | /* | ||
2 | * Material Experience - Utility Functions | ||
3 | * | ||
4 | * EYEMG - Interactive Media Group | ||
5 | * Created by Mike Crute (mcrute@eyemg.com) | ||
6 | * Updated by Mike Crute (mcrute@eyemg.com) on 9/26/07 | ||
7 | * | ||
8 | * Various utility functions that do not natively exist in any of | ||
9 | * the libraries we use. | ||
10 | * | ||
11 | * This is also the zoo for monkey patches. Be careful. | ||
12 | */ | ||
13 | |||
14 | Object.extend(window, | ||
15 | { | ||
16 | /* | ||
17 | * Calculates the top and left offsets to center an element within the | ||
18 | * page. Every browser seems to approach this in a different fashion but | ||
19 | * this function works well on FX, OP, WK, and IE. | ||
20 | */ | ||
21 | calcCordsToCenter: function(height, width) | ||
22 | { | ||
23 | var windowSize = window.getDimensions(); | ||
24 | |||
25 | return { | ||
26 | top: Math.round(((windowSize.height - height) / 2)), | ||
27 | left: Math.round(((windowSize.width - width) / 2)) | ||
28 | }; | ||
29 | }, | ||
30 | |||
31 | /* | ||
32 | * Gets the dimensions of the current window accounting for variations | ||
33 | * in browsers. Supports IE in either quirks or strict mode. | ||
34 | */ | ||
35 | getDimensions: function() | ||
36 | { | ||
37 | var windowSize = { | ||
38 | height: document.documentElement.clientHeight, | ||
39 | width: document.documentElement.clientWidth | ||
40 | }; | ||
41 | |||
42 | if (Prototype.Browser.Opera) | ||
43 | { | ||
44 | windowSize = { | ||
45 | height: document.body.clientHeight, | ||
46 | width: document.body.clientWidth | ||
47 | }; | ||
48 | } | ||
49 | |||
50 | if (Prototype.Browser.WebKit) | ||
51 | { | ||
52 | windowSize = { | ||
53 | height: window.innerHeight, | ||
54 | width: window.innerWidth | ||
55 | }; | ||
56 | } | ||
57 | |||
58 | return windowSize; | ||
59 | }, | ||
60 | |||
61 | /* | ||
62 | * Verify the screen size is great than a set minimum or warn the user. | ||
63 | * Probably superfluous but why not. | ||
64 | */ | ||
65 | checkResolution: function() | ||
66 | { | ||
67 | var size = window.getDimensions(); | ||
68 | |||
69 | if ( | ||
70 | size.height < SME.sizes.windMin.height || | ||
71 | size.width < SME.sizes.windMin.width | ||
72 | ) | ||
73 | { | ||
74 | new Bezel({ displayTime: 2 }).show(Strings.windowSize); | ||
75 | Event.stopObserving(window, 'resize', window.checkResolution); | ||
76 | } | ||
77 | } | ||
78 | }); | ||
79 | |||
80 | Object.extend(String.prototype, | ||
81 | { | ||
82 | /* | ||
83 | * Cleans up sloppy Compose JSON output by removing comments, newlines, | ||
84 | * and tabs. | ||
85 | */ | ||
86 | cleanJSON: function() | ||
87 | { | ||
88 | var data = this; | ||
89 | data = data.replace(/^\/\/.*/gm,''); | ||
90 | data = data.replace(/(\n|\t|\r)/g,''); | ||
91 | |||
92 | return data; | ||
93 | } | ||
94 | }); | ||
95 | |||
96 | Object.extend(Ajax.Request.prototype, | ||
97 | { | ||
98 | /* | ||
99 | * Forces IE to enqueue XHR requests otherwise it gets bogged down and | ||
100 | * freezes the interface. | ||
101 | */ | ||
102 | initialize: function(url, options) | ||
103 | { | ||
104 | this.transport = Ajax.getTransport(); | ||
105 | this.setOptions(options); | ||
106 | this.url = url; | ||
107 | |||
108 | if (Prototype.Browser.IE) | ||
109 | { | ||
110 | Ajax.requestQueue.push(this); | ||
111 | Ajax._runQueue(); | ||
112 | } | ||
113 | else | ||
114 | { | ||
115 | this.request(url); | ||
116 | } | ||
117 | }, | ||
118 | |||
119 | /* | ||
120 | * Monkey patch prototype so it stops auto-evaluating the returned JSON | ||
121 | * data. Our JSON data is simply not clean enough for that and it throws | ||
122 | * errors all over the place. | ||
123 | * | ||
124 | * I've made as few modifications from the original as possible to ease | ||
125 | * in porting forward changes made in Prototype. | ||
126 | */ | ||
127 | respondToReadyState: function(readyState) | ||
128 | { | ||
129 | var state = Ajax.Request.Events[readyState]; | ||
130 | var transport = this.transport; | ||
131 | |||
132 | if (state == 'Complete') | ||
133 | { | ||
134 | try | ||
135 | { | ||
136 | this._complete = true; | ||
137 | (this.options['on' + this.transport.status] | ||
138 | || this.options['on' + (this.success() ? 'Success' : 'Failure')] | ||
139 | || Prototype.emptyFunction)(transport); | ||
140 | } | ||
141 | catch (e) | ||
142 | { | ||
143 | this.dispatchException(e); | ||
144 | } | ||
145 | } | ||
146 | |||
147 | try | ||
148 | { | ||
149 | (this.options['on' + state] || Prototype.emptyFunction)(transport); | ||
150 | Ajax.Responders.dispatch('on' + state, this, transport); | ||
151 | } | ||
152 | catch (e) | ||
153 | { | ||
154 | this.dispatchException(e); | ||
155 | } | ||
156 | |||
157 | if (state == 'Complete') | ||
158 | { | ||
159 | // avoid memory leak in MSIE: clean up | ||
160 | this.transport.onreadystatechange = Prototype.emptyFunction; | ||
161 | } | ||
162 | } | ||
163 | }); | ||
164 | |||
165 | /* | ||
166 | * Implement AJAX request queueing. IE does not handle AJAX request concurrency | ||
167 | * very well and will freeze the entire browser interface if more than 2 calls | ||
168 | * are queued at a single time. We implement a global request queue and a queue | ||
169 | * runner that handles concurrent AJAX requests. The code is pretty straight | ||
170 | * forward but it also depends on modifications made to Ajax.Request.prototype. | ||
171 | */ | ||
172 | Object.extend(Ajax, | ||
173 | { | ||
174 | // Global AJAX Request Queue | ||
175 | requestQueue: [], | ||
176 | |||
177 | |||
178 | /* | ||
179 | * Process events in the request queue. | ||
180 | */ | ||
181 | _runQueue: function() | ||
182 | { | ||
183 | if (this.activeRequestCount > 1) | ||
184 | { | ||
185 | // Too many requests so we will try again later | ||
186 | new PeriodicalExecuter(function(executer) | ||
187 | { | ||
188 | this._runQueue(); | ||
189 | executer.stop(); | ||
190 | }.bind(this), 0.1); | ||
191 | |||
192 | return; | ||
193 | } | ||
194 | |||
195 | // Run the first request in the queue | ||
196 | var currentRequest = this.requestQueue.shift(); | ||
197 | currentRequest.request(currentRequest.url); | ||
198 | } | ||
199 | }); \ No newline at end of file | ||