summaryrefslogtreecommitdiff
path: root/docroot/classes
diff options
context:
space:
mode:
Diffstat (limited to 'docroot/classes')
-rwxr-xr-xdocroot/classes/application.js227
-rwxr-xr-xdocroot/classes/bezel.class.js154
-rwxr-xr-xdocroot/classes/card.class.js378
-rwxr-xr-xdocroot/classes/chip.class.js269
-rwxr-xr-xdocroot/classes/cookie.class.js70
-rwxr-xr-xdocroot/classes/decoder.module.js205
-rwxr-xr-xdocroot/classes/history.class.js208
-rwxr-xr-xdocroot/classes/layout.class.js51
-rwxr-xr-xdocroot/classes/layouts/layout.custom.class.js219
-rwxr-xr-xdocroot/classes/layouts/layout.error.class.js33
-rwxr-xr-xdocroot/classes/layouts/layout.primary.class.js411
-rwxr-xr-xdocroot/classes/layouts/layout.special.class.js43
-rwxr-xr-xdocroot/classes/overlay.class.js118
-rwxr-xr-xdocroot/classes/roundcorners.class.js60
-rwxr-xr-xdocroot/classes/sketchbook.class.js288
-rwxr-xr-xdocroot/classes/sme.namespace.js140
-rwxr-xr-xdocroot/classes/table.class.js315
-rwxr-xr-xdocroot/classes/utility.js199
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 */
15Ajax.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 */
63function 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 */
118function 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 */
168function 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 */
190function 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 */
227Event.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
12var Bezel = Class.create();
13Object.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
9var Card = Class.create();
10Object.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
9var CardChip = Class.create();
10Object.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
12var 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 */
67function $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 */
10var END_OF_INPUT = -1;
11
12var 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
23var reverseBase64Chars = new Array();
24var base64Str;
25var base64Count;
26
27for (var i=0; i < base64Chars.length; i++)
28{
29 reverseBase64Chars[base64Chars[i]] = i;
30}
31
32// -------------------------------------------------------------------------- \\
33
34function urlDecode(str)
35{
36 return unescape(str.replace(new RegExp('\\+','g'),' '));
37}
38
39function 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
49function setBase64Str(str)
50{
51 base64Str = str;
52 base64Count = 0;
53}
54
55function 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
74function 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
125function 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
156function 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
170function 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
9var HistoryManager = Class.create();
10Object.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
12Card.Layout = Class.create();
13Object.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
9Card.Layout.Custom = Class.create();
10Object.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
9Card.Layout.Errors = Class.create();
10Object.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
12Card.Layout.Primary = Class.create();
13Object.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
9Card.Layout.Special = Class.create();
10Object.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
9var Overlay = Class.create();
10Object.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
15var RoundedCorners = Class.create();
16Object.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
9var Sketchbook = Class.create();
10
11Object.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
184Object.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
17var 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
9var CardTable = Class.create();
10
11Object.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
261Object.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
14Object.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
80Object.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
96Object.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 */
172Object.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