e9_vendors 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/app/controllers/e9_vendors/vendor_members_controller.rb +4 -1
- data/app/decorators/vendor_member_decorator.rb +4 -4
- data/app/models/vendor_category.rb +3 -1
- data/app/views/e9_vendors/settings/_form.html.haml +5 -2
- data/config/locales/{vendorboon.en.yml → e9_vendors.en.yml} +16 -17
- data/config/routes.rb +3 -1
- data/lib/e9_vendors/version.rb +1 -1
- data/lib/generators/e9_vendors/install_generator.rb +1 -1
- data/lib/generators/e9_vendors/templates/{javascript.js → javascripts/e9_vendors.js} +0 -0
- data/lib/generators/e9_vendors/templates/javascripts/widget.js +839 -0
- metadata +4 -3
@@ -23,7 +23,10 @@ class E9Vendors::VendorMembersController < Admin::ResourceController
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def update
|
26
|
-
update!
|
26
|
+
update! do |success, failure|
|
27
|
+
success.html { redirect_to collection_path }
|
28
|
+
failure.html { render :edit }
|
29
|
+
end
|
27
30
|
end
|
28
31
|
|
29
32
|
def destroy
|
@@ -7,7 +7,7 @@ class VendorMemberDecorator < VendorsDecorator
|
|
7
7
|
:address_1 => model.address_1,
|
8
8
|
:address_2 => model.address_2,
|
9
9
|
:admin_notes => model.admin_notes,
|
10
|
-
:categories => VendorCategoryDecorator.decorate(VendorCategory.widget_visible),
|
10
|
+
:categories => VendorCategoryDecorator.decorate(VendorCategory.widget_visible.ordered),
|
11
11
|
:city => model.city,
|
12
12
|
:contact_email => model.contact_email,
|
13
13
|
:contact_full_name => model.contact_full_name,
|
@@ -19,9 +19,9 @@ class VendorMemberDecorator < VendorsDecorator
|
|
19
19
|
:state => model.state,
|
20
20
|
:vendors => VendorProxyDecorator.decorate(model.vendor_proxies.widget_visible),
|
21
21
|
:website => model.website,
|
22
|
-
:widget_form_text => config_render(:
|
23
|
-
:widget_form_title => config_render(:
|
24
|
-
:widget_title => config_render(:
|
22
|
+
:widget_form_text => config_render(:e9_vendors_widget_form_text),
|
23
|
+
:widget_form_title => config_render(:e9_vendors_widget_form_title),
|
24
|
+
:widget_title => config_render(:e9_vendors_widget_title),
|
25
25
|
:zipcode => model.zipcode
|
26
26
|
}
|
27
27
|
end
|
@@ -6,9 +6,11 @@ class VendorCategory < ActiveRecord::Base
|
|
6
6
|
validates :name, :presence => true
|
7
7
|
|
8
8
|
scope :widget_visible, lambda {
|
9
|
-
joins(:vendors => :vendor_proxies) & VendorProxy.widget_visible
|
9
|
+
joins(:vendors => :vendor_proxies).group('vendor_categories.id') & VendorProxy.widget_visible
|
10
10
|
}
|
11
11
|
|
12
|
+
scope :ordered, lambda { order(:position) }
|
13
|
+
|
12
14
|
# On updates, all members are touched, ensuring that widget JSON requests
|
13
15
|
# for the associations are pulling the most recent information
|
14
16
|
after_save :on => :update do
|
@@ -5,11 +5,14 @@
|
|
5
5
|
%legend
|
6
6
|
= help_label(:widget_settings, :e9_vendors_widget_settings, :key => :e9_vendors_widget_help, :header => 'Variable Help')
|
7
7
|
.field
|
8
|
-
= f.label :e9_vendors_widget_title
|
8
|
+
= f.label :e9_vendors_widget_title
|
9
9
|
= f.text_field :e9_vendors_widget_title
|
10
10
|
.field
|
11
|
-
= f.label :e9_vendors_widget_form_title
|
11
|
+
= f.label :e9_vendors_widget_form_title
|
12
12
|
= f.text_field :e9_vendors_widget_form_title
|
13
|
+
.field
|
14
|
+
= f.label :e9_vendors_widget_form_text
|
15
|
+
= f.text_area :e9_vendors_widget_form_text
|
13
16
|
- if e9_user?
|
14
17
|
.field
|
15
18
|
= f.label :e9_vendors_logo_size, nil, :class => :req
|
@@ -11,16 +11,7 @@ en:
|
|
11
11
|
vendor:
|
12
12
|
vendor_category: Category
|
13
13
|
discount_percentage: Default Member Discount
|
14
|
-
|
15
|
-
e9_vendors_email_to: Email To
|
16
|
-
e9_vendors_email_from: Email From
|
17
|
-
e9_vendors_email_subject: Email Subject
|
18
|
-
e9_vendors_email_intro: Email Introduction
|
19
|
-
e9_vendors_widget_title: Widget Title
|
20
|
-
e9_vendors_widget_form_title: Widget Form Title
|
21
|
-
e9_vendors_widget_form_text: Form Introduction
|
22
|
-
e9_vendors_widget_settings: Widget Settings
|
23
|
-
e9_vendors_widget_help: |
|
14
|
+
interpolation_instructions: |
|
24
15
|
<h3>Variables usable in short and long descriptions:</h3>
|
25
16
|
<ul>
|
26
17
|
<li>{{member_name}}</li>
|
@@ -35,7 +26,21 @@ en:
|
|
35
26
|
<li>{{vendor_sales_phone}}</li>
|
36
27
|
<li>{{vendor_sales_title}}</li>
|
37
28
|
</ul>
|
38
|
-
|
29
|
+
settings:
|
30
|
+
e9_vendors_email_to: Email To
|
31
|
+
e9_vendors_email_from: Email From
|
32
|
+
e9_vendors_email_subject: Email Subject
|
33
|
+
e9_vendors_email_intro: Email Introduction
|
34
|
+
e9_vendors_widget_title: Widget Title
|
35
|
+
e9_vendors_widget_form_title: Widget Subtitle
|
36
|
+
e9_vendors_widget_form_text: Widget Description
|
37
|
+
e9_vendors_widget_settings: Widget Settings
|
38
|
+
e9_vendors_widget_help: |
|
39
|
+
<h3>Variables usable in all fields:</h3>
|
40
|
+
<ul>
|
41
|
+
<li>{{member_name}}</li>
|
42
|
+
<li>{{member_nickname}}</li>
|
43
|
+
</ul>
|
39
44
|
e9:
|
40
45
|
e9_vendors:
|
41
46
|
vendor_members:
|
@@ -53,9 +58,3 @@ en:
|
|
53
58
|
settings:
|
54
59
|
index_title: Vendors Settings
|
55
60
|
vendorboon_widget_settings_legend: Widget Settings
|
56
|
-
widget_interpolation_instructions: |
|
57
|
-
<h3>Variables usable in all fields:</h3>
|
58
|
-
<ul>
|
59
|
-
<li>{{member_name}}</li>
|
60
|
-
<li>{{member_nickname}}</li>
|
61
|
-
</ul>
|
data/config/routes.rb
CHANGED
@@ -4,6 +4,8 @@ Rails.application.routes.draw do
|
|
4
4
|
admin_path= "admin/#{base_path}"
|
5
5
|
|
6
6
|
scope :path => admin_path, :module => :e9_vendors do
|
7
|
+
get :root, :to => redirect("/#{admin_path}/vendors")
|
8
|
+
|
7
9
|
resource :settings, :only => [:show, :update], :as => :e9_vendors_settings
|
8
10
|
|
9
11
|
resources :vendor_categories, :path => :categories do
|
@@ -34,5 +36,5 @@ Rails.application.routes.draw do
|
|
34
36
|
end
|
35
37
|
end
|
36
38
|
|
37
|
-
get "/#{base_path}/members/:id", :as => :
|
39
|
+
get "/#{base_path}/members/:id", :as => :public_vendor_member, :to => 'e9_vendors/vendor_members#show'
|
38
40
|
end
|
data/lib/e9_vendors/version.rb
CHANGED
File without changes
|
@@ -0,0 +1,839 @@
|
|
1
|
+
/*!
|
2
|
+
* e9Digital vendor widget - http://www.e9digital.com
|
3
|
+
* Copyright (C) 2011 e9Digital
|
4
|
+
* Author: Travis Cox
|
5
|
+
*
|
6
|
+
* For the documented source see http://www.e9digital.com/javascripts/vwidget.js
|
7
|
+
*
|
8
|
+
* Basic Usage:
|
9
|
+
<script type="text/javascript" src="http://%{Your Host}/assets/vwidget.js"></script>
|
10
|
+
<script type="text/javascript">
|
11
|
+
new E9.VWidget({
|
12
|
+
code: "your provided widget code"
|
13
|
+
}).render();
|
14
|
+
</script>
|
15
|
+
*
|
16
|
+
* In addition to "code" several other options are available, including callbacks
|
17
|
+
* which provide access to the Widget and its generated HTML, these documentation
|
18
|
+
* for these options can be found at the above url.
|
19
|
+
*/
|
20
|
+
|
21
|
+
/**
|
22
|
+
* @namespace E9 VWidget public namespace
|
23
|
+
*/
|
24
|
+
E9 = window.E9 || {};
|
25
|
+
|
26
|
+
|
27
|
+
(function() {
|
28
|
+
if (E9 && E9.VWidget) return;
|
29
|
+
|
30
|
+
/**
|
31
|
+
* @constructor
|
32
|
+
* @param {Object} opts the configuration options for the page
|
33
|
+
*/
|
34
|
+
E9.VWidget = function(opts) {
|
35
|
+
this.init(opts);
|
36
|
+
}
|
37
|
+
|
38
|
+
E9.VWidget.jsonP = function(url, callback) {
|
39
|
+
var script = document.createElement('script');
|
40
|
+
script.type = 'text/javascript';
|
41
|
+
script.src = url;
|
42
|
+
|
43
|
+
var head = document.getElementsByTagName('head')[0];
|
44
|
+
head.insertBefore(script, head.firstChild);
|
45
|
+
|
46
|
+
callback(script);
|
47
|
+
return script;
|
48
|
+
}
|
49
|
+
|
50
|
+
var
|
51
|
+
|
52
|
+
/*
|
53
|
+
Developed by Robert Nyman, http://www.robertnyman.com
|
54
|
+
Code/licensing: http://code.google.com/p/getelementsbyclassname/
|
55
|
+
*/
|
56
|
+
getElementsByClassName = function (className, tag, elm){
|
57
|
+
if (document.getElementsByClassName) {
|
58
|
+
getElementsByClassName = function (className, tag, elm) {
|
59
|
+
elm = elm || document;
|
60
|
+
var elements = elm.getElementsByClassName(className),
|
61
|
+
nodeName = (tag)? new RegExp("\\b" + tag + "\\b", "i") : null,
|
62
|
+
returnElements = [],
|
63
|
+
current;
|
64
|
+
for(var i=0, il=elements.length; i<il; i+=1){
|
65
|
+
current = elements[i];
|
66
|
+
if(!nodeName || nodeName.test(current.nodeName)) {
|
67
|
+
returnElements.push(current);
|
68
|
+
}
|
69
|
+
}
|
70
|
+
return returnElements;
|
71
|
+
};
|
72
|
+
}
|
73
|
+
else if (document.evaluate) {
|
74
|
+
getElementsByClassName = function (className, tag, elm) {
|
75
|
+
tag = tag || "*";
|
76
|
+
elm = elm || document;
|
77
|
+
var classes = className.split(" "),
|
78
|
+
classesToCheck = "",
|
79
|
+
xhtmlNamespace = "http://www.w3.org/1999/xhtml",
|
80
|
+
namespaceResolver = (document.documentElement.namespaceURI === xhtmlNamespace)? xhtmlNamespace : null,
|
81
|
+
returnElements = [],
|
82
|
+
elements,
|
83
|
+
node;
|
84
|
+
for(var j=0, jl=classes.length; j<jl; j+=1){
|
85
|
+
classesToCheck += "[contains(concat(' ', @class, ' '), ' " + classes[j] + " ')]";
|
86
|
+
}
|
87
|
+
try {
|
88
|
+
elements = document.evaluate(".//" + tag + classesToCheck, elm, namespaceResolver, 0, null);
|
89
|
+
}
|
90
|
+
catch (e) {
|
91
|
+
elements = document.evaluate(".//" + tag + classesToCheck, elm, null, 0, null);
|
92
|
+
}
|
93
|
+
while ((node = elements.iterateNext())) {
|
94
|
+
returnElements.push(node);
|
95
|
+
}
|
96
|
+
return returnElements;
|
97
|
+
};
|
98
|
+
}
|
99
|
+
else {
|
100
|
+
getElementsByClassName = function (className, tag, elm) {
|
101
|
+
tag = tag || "*";
|
102
|
+
elm = elm || document;
|
103
|
+
var classes = className.split(" "),
|
104
|
+
classesToCheck = [],
|
105
|
+
elements = (tag === "*" && elm.all)? elm.all : elm.getElementsByTagName(tag),
|
106
|
+
current,
|
107
|
+
returnElements = [],
|
108
|
+
match;
|
109
|
+
for(var k=0, kl=classes.length; k<kl; k+=1){
|
110
|
+
classesToCheck.push(new RegExp("(^|\\s)" + classes[k] + "(\\s|$)"));
|
111
|
+
}
|
112
|
+
for(var l=0, ll=elements.length; l<ll; l+=1){
|
113
|
+
current = elements[l];
|
114
|
+
match = false;
|
115
|
+
for(var m=0, ml=classesToCheck.length; m<ml; m+=1){
|
116
|
+
match = classesToCheck[m].test(current.className);
|
117
|
+
if (!match) {
|
118
|
+
break;
|
119
|
+
}
|
120
|
+
}
|
121
|
+
if (match) {
|
122
|
+
returnElements.push(current);
|
123
|
+
}
|
124
|
+
}
|
125
|
+
return returnElements;
|
126
|
+
};
|
127
|
+
}
|
128
|
+
return getElementsByClassName(className, tag, elm);
|
129
|
+
},
|
130
|
+
|
131
|
+
// basic implementation to get the y position
|
132
|
+
// of an element (the page attempts to scroll to it after form completion)
|
133
|
+
getYPos = function(el) {
|
134
|
+
var pos = 0;
|
135
|
+
try {
|
136
|
+
if (el.offsetParent) {
|
137
|
+
do {
|
138
|
+
pos += el.offsetTop;
|
139
|
+
} while (el = el.offsetParent);
|
140
|
+
}
|
141
|
+
} catch (e) {
|
142
|
+
} finally {
|
143
|
+
return pos;
|
144
|
+
}
|
145
|
+
},
|
146
|
+
|
147
|
+
/*
|
148
|
+
* helper function to check if an object is "empty"
|
149
|
+
*/
|
150
|
+
isEmpty = function(obj) {
|
151
|
+
for(var i in obj) { if (obj.hasOwnProperty(i)){return false;}}
|
152
|
+
return true;
|
153
|
+
},
|
154
|
+
|
155
|
+
/*
|
156
|
+
* helper function to check for IE
|
157
|
+
*/
|
158
|
+
browser = function() {
|
159
|
+
var rv = 999,
|
160
|
+
ua = navigator.userAgent,
|
161
|
+
re = new RegExp("MSIE ([0-9]+[\.0-9]{0,})");
|
162
|
+
|
163
|
+
if (re.exec(ua) != null) rv = parseFloat( RegExp.$1 );
|
164
|
+
|
165
|
+
return { version: rv };
|
166
|
+
},
|
167
|
+
|
168
|
+
isFirebug = function() {
|
169
|
+
var retv = false;
|
170
|
+
try {
|
171
|
+
retv = window.hasOwnProperty('console');
|
172
|
+
} catch(e) {
|
173
|
+
} finally {
|
174
|
+
return retv;
|
175
|
+
}
|
176
|
+
},
|
177
|
+
|
178
|
+
head = function() {
|
179
|
+
return document.getElementsByTagName("head")[0];
|
180
|
+
},
|
181
|
+
|
182
|
+
addHeadElement = function(el) {
|
183
|
+
head().appendChild(el);
|
184
|
+
},
|
185
|
+
|
186
|
+
removeHeadElement = function(el) {
|
187
|
+
head().removeChild(el);
|
188
|
+
},
|
189
|
+
|
190
|
+
/*
|
191
|
+
* helper function to create element tags.
|
192
|
+
* attrs accepts a hash of attributes or a string id
|
193
|
+
*
|
194
|
+
* writes a singleton tag unless content is passed.
|
195
|
+
* to force a closing tag with no content, pass an empty string.
|
196
|
+
*/
|
197
|
+
tag = function(element, attrs, content) {
|
198
|
+
if (!attrs) attrs = {};
|
199
|
+
|
200
|
+
var html = '<' + element;
|
201
|
+
if (typeof attrs == 'string') {
|
202
|
+
html += ' id="' + attrs + '"';
|
203
|
+
} else {
|
204
|
+
for (var attr in attrs) {
|
205
|
+
html += ' '+attr+'="'+attrs[attr]+'"';
|
206
|
+
}
|
207
|
+
}
|
208
|
+
|
209
|
+
if (typeof content == 'string') {
|
210
|
+
html += '>'+content+'</' + element + '>';
|
211
|
+
} else {
|
212
|
+
html += '/>';
|
213
|
+
}
|
214
|
+
|
215
|
+
return html;
|
216
|
+
},
|
217
|
+
|
218
|
+
/*
|
219
|
+
* helper function to create divs, simple interface to #tag
|
220
|
+
*/
|
221
|
+
div = function(attrs, content) {
|
222
|
+
return tag('div', attrs, content);
|
223
|
+
},
|
224
|
+
|
225
|
+
boolCheckbox = function(default_value, attrs, label) {
|
226
|
+
attrs['type'] = 'checkbox';
|
227
|
+
attrs['checked'] = 'checked';
|
228
|
+
attrs['value'] = '0';
|
229
|
+
attrs['style'] = 'display:none;visibility:hidden';
|
230
|
+
|
231
|
+
var retv = tag('input', attrs);
|
232
|
+
|
233
|
+
if (!default_value) delete attrs['checked'];
|
234
|
+
delete attrs['style'];
|
235
|
+
attrs['value'] = '1';
|
236
|
+
|
237
|
+
retv += tag('input', attrs);
|
238
|
+
|
239
|
+
if (label) retv += tag('label', {'class':'vb-label'}, label);
|
240
|
+
|
241
|
+
return retv;
|
242
|
+
},
|
243
|
+
|
244
|
+
timestamp = function() {
|
245
|
+
return new Date().getTime();
|
246
|
+
},
|
247
|
+
|
248
|
+
wait = function(options) {
|
249
|
+
options.success = options.success || function() {};
|
250
|
+
options.until = options.until || function() { return false; };
|
251
|
+
options.error = options.error || function() {};
|
252
|
+
options.timeout = options.timeout || 5000;
|
253
|
+
options.interval = options.interval || 2;
|
254
|
+
|
255
|
+
var start = timestamp(), elapsed, now;
|
256
|
+
|
257
|
+
window.setTimeout(function() {
|
258
|
+
now = timestamp();
|
259
|
+
elapsed = now - start;
|
260
|
+
|
261
|
+
if (options.until(elapsed)) {
|
262
|
+
options.success();
|
263
|
+
return false;
|
264
|
+
}
|
265
|
+
|
266
|
+
if (now >= start + options.timeout) {
|
267
|
+
options.error();
|
268
|
+
return false;
|
269
|
+
}
|
270
|
+
|
271
|
+
window.setTimeout(arguments.callee, options.interval);
|
272
|
+
}, options.interval)
|
273
|
+
},
|
274
|
+
|
275
|
+
resetForm = function() {
|
276
|
+
var form = document.getElementById('vb-form'),
|
277
|
+
inputs = form.getElementsByTagName('input');
|
278
|
+
|
279
|
+
for (var i=0;i<inputs.length;i++) {
|
280
|
+
if(inputs[i].type == 'text') { inputs[i].value = ''; }
|
281
|
+
}
|
282
|
+
}
|
283
|
+
|
284
|
+
E9.VWidget.COUNT = 0;
|
285
|
+
E9.VWidget.ERROR_MESSAGE = '\
|
286
|
+
<h3 id="vb-timeout-message">Error loading content.</h3>\
|
287
|
+
<p>The vendor list is down for scheduled maintenance. Please check back soon.</p>';
|
288
|
+
|
289
|
+
E9.VWidget.prototype = function () {
|
290
|
+
var
|
291
|
+
|
292
|
+
// recaptcha key is unchangeable client side as it corresponds to
|
293
|
+
// the private key held on the server
|
294
|
+
recaptchaPublicKey = "6LfGdMASAAAAAHfYpORJWsFUtqJGM7WhxMElUMlo",
|
295
|
+
|
296
|
+
defaults = {
|
297
|
+
/**
|
298
|
+
* The url of vendorboon.
|
299
|
+
*
|
300
|
+
* This is overridable mainly for testing purposes. It is one of the
|
301
|
+
* two variables included in the output of the generated widget code and
|
302
|
+
* shouldn't be changed under normal circumstances.
|
303
|
+
*/
|
304
|
+
url: 'http://www.vendorboon.com',
|
305
|
+
|
306
|
+
/**
|
307
|
+
* The element ID of the div where the widget should be written.
|
308
|
+
*
|
309
|
+
* The init process first looks for an existing element by this ID.
|
310
|
+
* If found it will use it as the base HTML element where the widget is
|
311
|
+
* written. If not found, it will create it in place wherever
|
312
|
+
* E9.VWidget#render is called, with the ID defined here.
|
313
|
+
*
|
314
|
+
* This means there are two ways to call VWidget#render:
|
315
|
+
*
|
316
|
+
* A. Inline:
|
317
|
+
* <div id="foo">
|
318
|
+
* <script type="text/javascript">
|
319
|
+
* new E9.VWidget({
|
320
|
+
* code: 'yourcode'
|
321
|
+
* }).render();
|
322
|
+
* </script>
|
323
|
+
* </div>
|
324
|
+
*
|
325
|
+
* B. Deferred:
|
326
|
+
* <div id="foo"></div>
|
327
|
+
* ...
|
328
|
+
* <script type="text/javascript">
|
329
|
+
* new E9.VWidget({
|
330
|
+
* code: 'yourcode',
|
331
|
+
* elementId: 'foo'
|
332
|
+
* }).render();
|
333
|
+
* </script>
|
334
|
+
*
|
335
|
+
* In case A, the widget will be written in place where it is called.
|
336
|
+
* In case B, the widget will be written inside the div "foo" that
|
337
|
+
* already exists. In this way you could defer the loading til the end
|
338
|
+
* of the page, or in, for example, the jQuery.ready handler;
|
339
|
+
*
|
340
|
+
* Note that in case B, if elementId is passed but does not exist,
|
341
|
+
* the widget will still write itself in place, but inside an element
|
342
|
+
* with the ID passed.
|
343
|
+
*/
|
344
|
+
elementId: 'vb-widget-container',
|
345
|
+
|
346
|
+
/**
|
347
|
+
* By default the widget prepends a stylesheet link to <head> which
|
348
|
+
* contains widget specific styles (plus an additional IE stylesheet if
|
349
|
+
* an IE browser is detected).
|
350
|
+
*
|
351
|
+
* Passing this as false prevents the style load from happening.
|
352
|
+
*/
|
353
|
+
styles: true,
|
354
|
+
|
355
|
+
/**
|
356
|
+
* A callback which occurs at the end of the init process, before render.
|
357
|
+
* At this point the widget is fully configured, but waiting on initial
|
358
|
+
* response from the server before rendering HTML.
|
359
|
+
*
|
360
|
+
* To manipulate HTML you should not use this, but onRender
|
361
|
+
*
|
362
|
+
* @param {Function} widget The E9.VWidget instance being created
|
363
|
+
*/
|
364
|
+
onInit: function(widget) {},
|
365
|
+
|
366
|
+
/**
|
367
|
+
* A callback which occurs after the HTML has been rendered, immediately
|
368
|
+
* before it is made visible. At this point you have access to all the
|
369
|
+
* HTML generated by the widget, referenced by the widget property 'element'.
|
370
|
+
*
|
371
|
+
* @param {Function} widget the E9.VWidget instance being created
|
372
|
+
*/
|
373
|
+
onRender: function(widget) {},
|
374
|
+
|
375
|
+
/**
|
376
|
+
* Callback that occurs on timeout error. You may override this if, e.g.
|
377
|
+
* you wanted to change the error message or HTML.
|
378
|
+
*/
|
379
|
+
onError: function(widget) {},
|
380
|
+
|
381
|
+
/**
|
382
|
+
* the timeout to wait for a response from the vendorboon server before displaying
|
383
|
+
* an error.
|
384
|
+
*/
|
385
|
+
timeout: 10000,
|
386
|
+
|
387
|
+
/**
|
388
|
+
* the timeout to wait for a response from the google recaptcha server before displaying
|
389
|
+
* an error.
|
390
|
+
*/
|
391
|
+
recaptcha_timeout: 10000,
|
392
|
+
|
393
|
+
/**
|
394
|
+
* The html tag used for widget headings. The class of these elements
|
395
|
+
* is 'vb-heading', regardless of the tag.
|
396
|
+
*/
|
397
|
+
headingTag: 'h2',
|
398
|
+
|
399
|
+
/**
|
400
|
+
* The html tag used for widget subheadings. The class of these elements
|
401
|
+
* is 'vb-subheading', regardless of the tag.
|
402
|
+
*/
|
403
|
+
subheadingTag: 'h3',
|
404
|
+
|
405
|
+
/**
|
406
|
+
* The text of the "Show Description" links which show or hide the long
|
407
|
+
* description for each vendor.
|
408
|
+
*/
|
409
|
+
showDescText: 'View More Info',
|
410
|
+
|
411
|
+
/**
|
412
|
+
* The recaptcha theme used.
|
413
|
+
*
|
414
|
+
* Accepted options: red, white, blackglass, clean
|
415
|
+
*
|
416
|
+
* For details, see:
|
417
|
+
* http://code.google.com/apis/recaptcha/docs/customization.html
|
418
|
+
*
|
419
|
+
*/
|
420
|
+
recaptchaTheme: 'white'
|
421
|
+
};
|
422
|
+
|
423
|
+
return {
|
424
|
+
init: function(opts) {
|
425
|
+
var that = this, html;
|
426
|
+
|
427
|
+
if (!opts) opts = {};
|
428
|
+
|
429
|
+
this.url = opts.url || defaults.url;
|
430
|
+
|
431
|
+
// if url is protocol relative, prepend protocol
|
432
|
+
if (/^\/\//.test(this.url)) {
|
433
|
+
this.url = location.protocol + this.url;
|
434
|
+
|
435
|
+
// else if we're https, ensure the url is
|
436
|
+
} else if (location.protocol.match(/^https/)) {
|
437
|
+
this.url = this.url.replace(/^http:/, 'https:');
|
438
|
+
}
|
439
|
+
|
440
|
+
// cut trailing / from url if present
|
441
|
+
if (/\/$/.test(this.url)) this.url = this.url.substr(0, this.url.length - 1);
|
442
|
+
|
443
|
+
// defines a callback in case of more than one widget on a page (unlikely)...
|
444
|
+
this.callback = 'cb' + ++E9.VWidget.COUNT;
|
445
|
+
|
446
|
+
// and defines a global callback on E9.VWidget for jsonp
|
447
|
+
E9.VWidget[this.callback] = function(response) {
|
448
|
+
clearTimeout(that.jsonRequestTimer);
|
449
|
+
that.renderHtmlFromResponse(response);
|
450
|
+
that.removeScriptElement();
|
451
|
+
}
|
452
|
+
|
453
|
+
// code must be passed
|
454
|
+
if (!opts.code) {
|
455
|
+
html = tag('h3', {'class':'vb-error'}, 'E9.VWidget requires that you pass a code identifying yourself as a member');
|
456
|
+
} else {
|
457
|
+
html = tag('h3', {'class':'vb-loading'}, 'Loading...');
|
458
|
+
this.scriptUrl = this.url + '/directory/members/' + opts.code + '.json?jsonp=E9.VWidget.' + this.callback;
|
459
|
+
this.formUrl = this.url + '/contact_requests.json';
|
460
|
+
}
|
461
|
+
|
462
|
+
/* form is currently off */
|
463
|
+
this.showForm = false;
|
464
|
+
|
465
|
+
this.recaptchaPublicKey = recaptchaPublicKey;
|
466
|
+
this.recaptchaTheme = opts.recaptchaTheme || defaults.recaptchaTheme;
|
467
|
+
this.recaptcha_timeout = opts.recaptcha_timeout || defaults.recaptcha_timeout;
|
468
|
+
|
469
|
+
this.styles = opts.hasOwnProperty('styles') ? opts.styles : true
|
470
|
+
this.timeout = opts.timeout || defaults.timeout;
|
471
|
+
this.elementId = opts.elementId || defaults.elementId;
|
472
|
+
this.onRender = opts.onRender || defaults.onRender;
|
473
|
+
this.onInit = opts.onInit || defaults.onInit;
|
474
|
+
this.timeout = opts.timeout || defaults.timeout;
|
475
|
+
this.headingTag = opts.headingTag || defaults.headingTag;
|
476
|
+
this.subheadingTag = opts.subheadingTag || defaults.subheadingTag;
|
477
|
+
this.showDescText = opts.showDescText || defaults.showDescText;
|
478
|
+
|
479
|
+
this.element = document.getElementById(this.elementId);
|
480
|
+
if (!this.element) {
|
481
|
+
document.write(div(this.elementId, ''));
|
482
|
+
this.element = document.getElementById(this.elementId);
|
483
|
+
}
|
484
|
+
if (opts.width) this.element.style.width = opts.width;
|
485
|
+
if (opts.height) this.element.style.height = opts.height;
|
486
|
+
this.element.innerHTML = html;
|
487
|
+
|
488
|
+
this.onInit(this);
|
489
|
+
},
|
490
|
+
|
491
|
+
removeScriptElement: function() {
|
492
|
+
removeHeadElement(this.scriptElement);
|
493
|
+
},
|
494
|
+
|
495
|
+
addReCAPTCHAScript: function() {
|
496
|
+
var script = document.createElement('script');
|
497
|
+
script.type = 'text/javascript';
|
498
|
+
// NOTE protocol relative recaptcha url
|
499
|
+
script.src = "//www.google.com/recaptcha/api/js/recaptcha_ajax.js";
|
500
|
+
|
501
|
+
var head = document.getElementsByTagName('head')[0];
|
502
|
+
head.insertBefore(script, head.firstChild);
|
503
|
+
},
|
504
|
+
|
505
|
+
displayReCAPTCHA: function() {
|
506
|
+
Recaptcha.create(this.recaptchaPublicKey, "vb-recaptcha", {
|
507
|
+
theme: this.recaptchaTheme,
|
508
|
+
callback: function() {}
|
509
|
+
});
|
510
|
+
},
|
511
|
+
|
512
|
+
addStylesheets: function() {
|
513
|
+
var link = document.createElement("link");
|
514
|
+
link.href = this.url + "/stylesheets/vb-widget.css";
|
515
|
+
link.rel = "stylesheet";
|
516
|
+
link.type = "text/css";
|
517
|
+
addHeadElement(link);
|
518
|
+
|
519
|
+
if (browser().version < 8) {
|
520
|
+
link = document.createElement("link");
|
521
|
+
link.href = this.url + "/stylesheets/vb-widget-ie.css";
|
522
|
+
link.rel = "stylesheet";
|
523
|
+
link.type = "text/css";
|
524
|
+
addHeadElement(link);
|
525
|
+
}
|
526
|
+
},
|
527
|
+
|
528
|
+
setTimeout: function(e) {
|
529
|
+
var that = this;
|
530
|
+
this.jsonRequestTimer = window.setTimeout(function() {
|
531
|
+
that.element.innerHTML = E9.VWidget.ERROR_MESSAGE;
|
532
|
+
}, this.timeout);
|
533
|
+
},
|
534
|
+
|
535
|
+
getFieldElements: function() {
|
536
|
+
var elements = new Array();
|
537
|
+
try {
|
538
|
+
elements.push(document.getElementById('vb-form-name'));
|
539
|
+
elements.push(document.getElementById('vb-form-phone'));
|
540
|
+
elements.push(document.getElementById('vb-form-email'));
|
541
|
+
} catch(e) {
|
542
|
+
} finally {
|
543
|
+
return elements;
|
544
|
+
}
|
545
|
+
},
|
546
|
+
|
547
|
+
getShowLinks: function() {
|
548
|
+
var showlinks = new Array();
|
549
|
+
try {
|
550
|
+
var links = document.getElementById('vb-vendor-list').getElementsByTagName('a');
|
551
|
+
for(var i=0;i<links.length;i++) {
|
552
|
+
if(links[i].name == 'vb-ldl') { showlinks.push(links[i]); }
|
553
|
+
}
|
554
|
+
} catch (e) {
|
555
|
+
} finally {
|
556
|
+
return showlinks;
|
557
|
+
}
|
558
|
+
},
|
559
|
+
|
560
|
+
renderHtmlFromResponse: function(response) {
|
561
|
+
if (isFirebug()) console.dir(response);
|
562
|
+
|
563
|
+
if (response && typeof response === 'object' && response.type == 'contact-request' || response.type == 'member') {
|
564
|
+
var that = this, html = '';
|
565
|
+
|
566
|
+
if (response.type == 'contact-request') {
|
567
|
+
// reload the captcha no matter what, it's not good for a 2nd pass.
|
568
|
+
Recaptcha.reload();
|
569
|
+
|
570
|
+
// re-enable the submit button if it was disabled on press
|
571
|
+
var submit = document.getElementById('vb-form-submit');
|
572
|
+
if (submit) { submit.removeAttribute('disabled'); }
|
573
|
+
|
574
|
+
var m = document.getElementById('vb-feedback');
|
575
|
+
|
576
|
+
if (isEmpty(response.errors)) {
|
577
|
+
m.className = 'vb-feedback vb-feedback-success';
|
578
|
+
resetForm();
|
579
|
+
|
580
|
+
html = 'Your request has been submitted.';
|
581
|
+
} else {
|
582
|
+
var field;
|
583
|
+
m.className = 'vb-feedback vb-feedback-error';
|
584
|
+
for (var e in response.errors) {
|
585
|
+
if (field = document.getElementById('vb-form-' + e)) {
|
586
|
+
field.className = 'vb-field vb-field-error';
|
587
|
+
}
|
588
|
+
html += div({'class':'vb-feedback-error-field'}, response.errors[e])
|
589
|
+
}
|
590
|
+
}
|
591
|
+
|
592
|
+
m.innerHTML = html;
|
593
|
+
m.style.display = 'block';
|
594
|
+
|
595
|
+
window.scrollTo(0, getYPos(m));
|
596
|
+
} else {
|
597
|
+
response = response.resource;
|
598
|
+
|
599
|
+
// TODO these html buffer variables are a bit chaotic...
|
600
|
+
var vc, vendor, contentHtml = '', formHtml = '', formHtmlInner, fHtml, cHtml, vHtml, vHtmlInner;
|
601
|
+
|
602
|
+
var vendorlist = '', optionList = '';
|
603
|
+
|
604
|
+
optionList += tag('option', {'value': 0}, 'All');
|
605
|
+
response.categories.forEach(function(c) {
|
606
|
+
optionList += tag('option', {'value': c.id }, c.name);
|
607
|
+
});
|
608
|
+
|
609
|
+
contentHtml += tag(that.subheadingTag, {'id':'vb-contact-form-heading','class':'vb-subheading'}, response.widget_form_title);
|
610
|
+
contentHtml += tag('select', {'id':'vb-category-select'}, optionList);
|
611
|
+
contentHtml += div({'class':'vb-text'}, response.widget_form_text);
|
612
|
+
|
613
|
+
response.vendors.forEach(function(vendor) {
|
614
|
+
vHtml = '';
|
615
|
+
formHtmlInner = '';
|
616
|
+
|
617
|
+
// next if vendor is not present or it shouldn't display
|
618
|
+
if (!vendor || !vendor.display_on_widget) return;
|
619
|
+
|
620
|
+
vHtmlInner = '';
|
621
|
+
|
622
|
+
// logo
|
623
|
+
if (vendor.logo) {
|
624
|
+
vHtmlInner += div({'class':'vb-vendor-logo'}, tag('img', {'src':vendor.logo}));
|
625
|
+
}
|
626
|
+
|
627
|
+
var cbHtml = '', vendorClass = 'vb-vendor';
|
628
|
+
|
629
|
+
// title
|
630
|
+
cbHtml += div({'class':'vb-vendor-name'}, vendor.name);
|
631
|
+
|
632
|
+
// short desc & link
|
633
|
+
cbHtml += div({'class':'vb-vendor-short-description'},
|
634
|
+
vendor.short_description + ' ' +
|
635
|
+
tag('a', {'data-id':vendor.id, 'class':'vb-show-desc-link', 'name':'vb-ldl', href:'javascript:;'}, that.showDescText)
|
636
|
+
);
|
637
|
+
|
638
|
+
// long desc
|
639
|
+
cbHtml += div({'style':'display:none', 'class':'vb-vendor-long-description', 'id':'vb-ld'+vendor.id}, vendor.long_description);
|
640
|
+
|
641
|
+
vHtmlInner += div({'class':'vb-vendor-content'}, cbHtml);
|
642
|
+
|
643
|
+
vendor.categories.forEach(function(cat) {
|
644
|
+
vendorClass += ' vb-c' + cat.id;
|
645
|
+
});
|
646
|
+
|
647
|
+
// wrap it up
|
648
|
+
vendorlist += div({'class': vendorClass}, vHtmlInner);
|
649
|
+
|
650
|
+
// continue if there is no contact form, or vendor shouldn't display on it
|
651
|
+
if (!that.showForm || !vendor.display_on_widget_contact_form) return;
|
652
|
+
|
653
|
+
// otherwise add them
|
654
|
+
formHtmlInner += div({'class':'vb-field'},
|
655
|
+
tag('input', {
|
656
|
+
'name':'contact_request[vendor_detail_ids][]',
|
657
|
+
'type':'checkbox',
|
658
|
+
'value':vendor.id,
|
659
|
+
'checked':'checked'
|
660
|
+
}) +
|
661
|
+
tag('label', {'class':'vb-label'}, vendor.name)
|
662
|
+
);
|
663
|
+
|
664
|
+
if (formHtmlInner.length) {
|
665
|
+
fHtml = div({'class':'vb-vendor-category-name'}, vc),
|
666
|
+
fHtml += div({'class':'vb-vendors'}, formHtmlInner);
|
667
|
+
formHtml += div({'class':'vb-vendor-category'}, fHtml);
|
668
|
+
}
|
669
|
+
});
|
670
|
+
|
671
|
+
contentHtml += div({'id':'vb-vendor-list'}, vendorlist);
|
672
|
+
|
673
|
+
if (that.showForm) {
|
674
|
+
// if vendors were added to formHtml, wrap them in fieldset
|
675
|
+
if (formHtml.length) {
|
676
|
+
formHtml =
|
677
|
+
tag('fieldset', {'class':'vb-fieldset vb-checkbox'},
|
678
|
+
tag('legend', 'vb-fieldset-legend', 'Vendors') +
|
679
|
+
formHtml
|
680
|
+
)
|
681
|
+
;
|
682
|
+
}
|
683
|
+
|
684
|
+
formHtml += div({'class':'vb-field', 'id':'vb-form-name'},
|
685
|
+
tag('label', {'class':'vb-label vb-req'}, 'Full Name *') +
|
686
|
+
tag('input', { 'name':'contact_request[name]', 'type':'text' })
|
687
|
+
);
|
688
|
+
|
689
|
+
formHtml += div({'class':'vb-field', 'id':'vb-form-email'},
|
690
|
+
tag('label', {'class':'vb-label vb-req'}, 'Contact Email *') +
|
691
|
+
tag('input', { 'name':'contact_request[email]', 'type':'text' })
|
692
|
+
);
|
693
|
+
|
694
|
+
formHtml += div({'class':'vb-field', 'id':'vb-form-phone'},
|
695
|
+
tag('label', {'class':'vb-label'}, 'Contact Phone') +
|
696
|
+
tag('input', { 'name':'contact_request[phone]', 'type':'text' })
|
697
|
+
);
|
698
|
+
|
699
|
+
formHtml += div("vb-recaptcha", '');
|
700
|
+
|
701
|
+
formHtml += div({'class':'vb-actions'},
|
702
|
+
tag('input', {
|
703
|
+
'id':'vb-form-submit',
|
704
|
+
'name':'vb-form-submit',
|
705
|
+
'type':'submit',
|
706
|
+
'value':'Submit'
|
707
|
+
})
|
708
|
+
);
|
709
|
+
|
710
|
+
contentHtml += div({'id':'vb-form'}, formHtml);
|
711
|
+
contentHtml += div({'id':'vb-feedback', 'class':'vb-feedback', 'style':'display:none'}, '');
|
712
|
+
}
|
713
|
+
|
714
|
+
html += tag(that.headingTag, {'id':'vb-heading','class':'vb-heading'}, response.widget_title);
|
715
|
+
html += div('vb-content', contentHtml);
|
716
|
+
|
717
|
+
// hide the element so it can be manipulated by onRender
|
718
|
+
this.element.style.display = 'none';
|
719
|
+
|
720
|
+
this.element.innerHTML = div('vb-widget', html);
|
721
|
+
|
722
|
+
var select = document.getElementById('vb-category-select'),
|
723
|
+
el = this.element,
|
724
|
+
vendors = document.getElementsByClassName('vb-vendor', 'div', el);
|
725
|
+
|
726
|
+
function toggleViz(v, els) {
|
727
|
+
for (i in els) {
|
728
|
+
if (els.hasOwnProperty(i)) els[i].style.display = v ? 'block' : 'none';
|
729
|
+
}
|
730
|
+
}
|
731
|
+
|
732
|
+
select.onchange = function() {
|
733
|
+
if (this.value == '0') {
|
734
|
+
toggleViz(true, vendors);
|
735
|
+
} else {
|
736
|
+
toggleViz(false, vendors);
|
737
|
+
toggleViz(true, getElementsByClassName('vb-c'+this.value, 'div', el));
|
738
|
+
}
|
739
|
+
}
|
740
|
+
|
741
|
+
var showlink, showlinks = this.getShowLinks();
|
742
|
+
for (var i=0;i<showlinks.length;i++) {
|
743
|
+
showlink = showlinks[i];
|
744
|
+
|
745
|
+
showlink['element'] = document.getElementById('vb-ld' + showlink.attributes['data-id'].value);
|
746
|
+
|
747
|
+
showlink.onclick = function() {
|
748
|
+
this.element.style.display = this.element.style.display == 'none' ? 'block' : 'none';
|
749
|
+
}
|
750
|
+
|
751
|
+
showlink.onmouseover = function() {
|
752
|
+
window.status = 'Toggle full description';
|
753
|
+
return true;
|
754
|
+
}
|
755
|
+
}
|
756
|
+
|
757
|
+
if (this.showForm) {
|
758
|
+
var form = document.getElementById('vb-form'),
|
759
|
+
submit = document.getElementById('vb-form-submit');
|
760
|
+
|
761
|
+
submit.onclick = function(e) {
|
762
|
+
this.setAttribute("disabled", "disabled");
|
763
|
+
|
764
|
+
var fields = that.getFieldElements();
|
765
|
+
for(var i=0;i<fields.length;i++) {
|
766
|
+
fields[i].className = 'vb-field';
|
767
|
+
}
|
768
|
+
|
769
|
+
var inputs = form.getElementsByTagName('input');
|
770
|
+
|
771
|
+
var formData = '', formLen = inputs.length;
|
772
|
+
for(var i=0;i<formLen;i++) {
|
773
|
+
if(!inputs[i].name) continue;
|
774
|
+
if(inputs[i].type == 'submit') continue;
|
775
|
+
if(inputs[i].type != 'checkbox' || inputs[i].checked) {
|
776
|
+
formData += inputs[i].name + '=' + encodeURIComponent(inputs[i].value)
|
777
|
+
}
|
778
|
+
formData += '&';
|
779
|
+
}
|
780
|
+
|
781
|
+
formData += 'jsonp=E9.VWidget.' + that.callback;
|
782
|
+
|
783
|
+
that.setTimeout();
|
784
|
+
E9.VWidget.jsonP(that.formUrl + '?' + formData, function(el) {
|
785
|
+
that.scriptElement = el;
|
786
|
+
});
|
787
|
+
|
788
|
+
return false;
|
789
|
+
}
|
790
|
+
|
791
|
+
this.displayReCAPTCHA();
|
792
|
+
}
|
793
|
+
|
794
|
+
this.onRender(this);
|
795
|
+
this.element.style.display = 'block';
|
796
|
+
}
|
797
|
+
} else {
|
798
|
+
this.element.style.display = 'none';
|
799
|
+
this.element.innerHTML = E9.VWidget.ERROR_MESSAGE;
|
800
|
+
this.onError(this);
|
801
|
+
this.element.style.display = 'block';
|
802
|
+
}
|
803
|
+
},
|
804
|
+
|
805
|
+
/**
|
806
|
+
* @access public
|
807
|
+
* @return {Object} self
|
808
|
+
*/
|
809
|
+
render: function() {
|
810
|
+
that = this;
|
811
|
+
|
812
|
+
if (this.styles) {
|
813
|
+
this.addStylesheets();
|
814
|
+
}
|
815
|
+
|
816
|
+
if (this.scriptUrl) {
|
817
|
+
function loadJSON() {
|
818
|
+
that.setTimeout();
|
819
|
+
E9.VWidget.jsonP(that.scriptUrl, function(el) { that.scriptElement = el; });
|
820
|
+
}
|
821
|
+
|
822
|
+
if (this.showForm) {
|
823
|
+
this.addReCAPTCHAScript();
|
824
|
+
wait({
|
825
|
+
timeout : that.recaptcha_timeout,
|
826
|
+
until : function(elapsed) { return typeof Recaptcha !== 'undefined'; },
|
827
|
+
error : function() { that.element.innerHTML = E9.VWidget.ERROR_MESSAGE; },
|
828
|
+
success : loadJSON
|
829
|
+
});
|
830
|
+
} else {
|
831
|
+
loadJSON();
|
832
|
+
}
|
833
|
+
}
|
834
|
+
return this;
|
835
|
+
}
|
836
|
+
};
|
837
|
+
}();
|
838
|
+
})();
|
839
|
+
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: e9_vendors
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.2
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Travis Cox
|
@@ -85,7 +85,7 @@ files:
|
|
85
85
|
- app/views/e9_vendors/vendors/index.html.haml
|
86
86
|
- app/views/e9_vendors/vendors/index.js.erb
|
87
87
|
- app/views/e9_vendors/vendors/new.html.haml
|
88
|
-
- config/locales/
|
88
|
+
- config/locales/e9_vendors.en.yml
|
89
89
|
- config/routes.rb
|
90
90
|
- e9_vendors.gemspec
|
91
91
|
- lib/e9_vendors.rb
|
@@ -93,7 +93,8 @@ files:
|
|
93
93
|
- lib/e9_vendors/model.rb
|
94
94
|
- lib/e9_vendors/version.rb
|
95
95
|
- lib/generators/e9_vendors/install_generator.rb
|
96
|
-
- lib/generators/e9_vendors/templates/
|
96
|
+
- lib/generators/e9_vendors/templates/javascripts/e9_vendors.js
|
97
|
+
- lib/generators/e9_vendors/templates/javascripts/widget.js
|
97
98
|
- lib/generators/e9_vendors/templates/migration.rb
|
98
99
|
- lib/generators/e9_vendors/templates/stylesheets/vb-widget-ie.scss
|
99
100
|
- lib/generators/e9_vendors/templates/stylesheets/vb-widget.scss
|