support_center 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,10 @@
1
+ define [
2
+ 'jquery',
3
+ 'support_center/form_component'
4
+ ], (
5
+ $,
6
+ FormComponent
7
+ ) ->
8
+ $ ->
9
+ $(document).on 'questionsModalLoaded', (ev, modalData) ->
10
+ FormComponent.attachTo modalData.container
@@ -0,0 +1,11 @@
1
+ // Generated by CoffeeScript 1.7.1
2
+ (function() {
3
+ define(['jquery', 'support_center/form_component'], function($, FormComponent) {
4
+ return $(function() {
5
+ return $(document).on('questionsModalLoaded', function(ev, modalData) {
6
+ return FormComponent.attachTo(modalData.container);
7
+ });
8
+ });
9
+ });
10
+
11
+ }).call(this);
@@ -0,0 +1,11 @@
1
+ require 'support_center/version'
2
+ require 'support_center/config'
3
+ require 'support_center/server'
4
+
5
+ module SupportCenter
6
+ class << self
7
+ def new
8
+ Server.new
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,43 @@
1
+ module SupportCenter
2
+ class Config
3
+ def initialize(data={})
4
+ @data = {}
5
+ update!(data)
6
+ end
7
+
8
+ def update!(data)
9
+ data.each do |key, value|
10
+ self[key] = value
11
+ end
12
+ end
13
+
14
+ def [](key)
15
+ @data[key.to_sym]
16
+ end
17
+
18
+ def []=(key, value)
19
+ if value.class == Hash
20
+ @data[key.to_sym] = Config.new(value)
21
+ else
22
+ @data[key.to_sym] = value
23
+ end
24
+ end
25
+
26
+ def method_missing(sym, *args)
27
+ if sym.to_s =~ /(.+)=$/
28
+ self[$1] = args.first
29
+ else
30
+ self[sym]
31
+ end
32
+ end
33
+ end
34
+
35
+ class << self
36
+ attr_accessor :config
37
+
38
+ def setup
39
+ self.config ||= Config.new
40
+ yield config
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,30 @@
1
+ require 'sinatra/base'
2
+ require 'zendesk_api'
3
+ require 'support_center/server/helpers'
4
+
5
+ module SupportCenter
6
+ class Server < Sinatra::Base
7
+ dir = File.dirname(File.expand_path(__FILE__))
8
+ set :views, "#{dir}/server/views"
9
+
10
+ helpers SupportCenter::Helpers
11
+
12
+ get '/support/tickets/new/?' do
13
+ slim :'new_ticket'
14
+ end
15
+
16
+ get '/support/tickets/:id/?' do
17
+ body support_client.tickets.find(id: params[:id]).to_json
18
+ end
19
+
20
+ post '/support/tickets/?' do
21
+ ticket = support_client.tickets.build params[:ticket]
22
+ if ticket.save
23
+ {ticket:ticket.id}.to_json
24
+ else
25
+ status 422
26
+ {errors:ticket.errors}.to_json
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,24 @@
1
+ module SupportCenter
2
+ module Helpers
3
+ def support_client
4
+ @support_client ||= ZendeskAPI::Client.new do |config|
5
+ settings = SupportCenter.config.config_service_adapter['support_center']
6
+
7
+ config.url = settings['url']
8
+ config.username = settings['username']
9
+ config.password = settings['password']
10
+ config.retry = true
11
+ end
12
+ end
13
+
14
+ def see_more_faqs_link
15
+ if params[:faq_link]
16
+ "<a class='all_faq_link' href=#{params[:faq_link]}>See All FAQs ...</a>"
17
+ end
18
+ end
19
+
20
+ def field_ids
21
+ @field_ids ||= SupportCenter.config.custom_ticket_fields
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,80 @@
1
+ #feedback_pop_head
2
+ h3 Submit Questions or Feedback
3
+ a.icon_close.sprite.close
4
+ #faqlist
5
+ h4 Oops! The popular FAQ list is currently unavailable.
6
+ == see_more_faqs_link
7
+ #feedback_form_holder
8
+ ul.global_tabs
9
+ li
10
+ a#feedback_form.ld_tab.tab_active href="javascript: void(0);" Send Feedback
11
+ li
12
+ a#questions_form.ld_tab href="javascript: void(0);"Ask A Question
13
+ #feedback_form_tab.feedback_popup_tab.active_tab
14
+ .padding-box
15
+ span.error_msg.hidden Missing Required Field
16
+ form id="feedback_form_element"
17
+ p
18
+ label for='feedback_subject'
19
+ | Your idea or suggestion:
20
+ span.required *
21
+ input#feedback_subject type='text' name='ticket[subject]'
22
+ p
23
+ label for='feedback_description'
24
+ | Describe your idea or how we can improve your experience:
25
+ span.required *
26
+ textarea#feedback_description name='ticket[description]' rows="7"
27
+ p
28
+ label for='feedback_name'
29
+ | Name:
30
+ span.required *
31
+ input#feedback_name type='text' name='ticket[requester][name]'
32
+ p
33
+ label for='feedback_email'
34
+ | Email:
35
+ span.required *
36
+ input#feedback_email type='text' name='ticket[requester][email]'
37
+ input#feedback_browser type='hidden' name="ticket[custom_fields][#{field_ids[:browser]}]"
38
+ input#feedback_current_url type='hidden' name="ticket[custom_fields][#{field_ids[:url]}]"
39
+ input type='hidden' name='ticket[tags]' value='feedback'
40
+ span.spinner.hidden
41
+ input class="button btn_large" name="commit" type="submit" value="Submit"
42
+ .clr
43
+ #thanks_feedback_tab.hidden
44
+ span Thank You for your feedback!
45
+ p We will review your feedback shortly.
46
+ .clr
47
+ #questions_form_tab.feedback_popup_tab
48
+ .padding-box
49
+ span.error_msg.hidden Missing Required Field
50
+ form#questions_form_element
51
+ p
52
+ label for='question_subject'
53
+ | Your question:
54
+ span.required *
55
+ input#question_subject type='text' name='ticket[subject]'
56
+ p
57
+ label for='question_description'
58
+ | Describe your question, please provide details:
59
+ span.required *
60
+ textarea#question_description name='ticket[description]' rows="7"
61
+ p
62
+ label for='question_name'
63
+ | Name:
64
+ span.required *
65
+ input#question_name type='text' name='ticket[requester][name]'
66
+ p
67
+ label for='question_email'
68
+ | Email:
69
+ span.required *
70
+ input#question_email type='text' name='ticket[requester][email]'
71
+ input#question_browser type='hidden' name="ticket[custom_fields][#{field_ids[:browser]}]"
72
+ input#question_current_url type='hidden' name="ticket[custom_fields][#{field_ids[:url]}]"
73
+ input type='hidden' name='ticket[tags]' value='question'
74
+ span.spinner.hidden
75
+ input class="button btn_large" name="commit" type="submit" value="Submit"
76
+ .clr
77
+ #thanks_questions_tab.hidden
78
+ span Thank You for your question!
79
+ p We will review your question shortly and try to get back to you.
80
+ .clr
@@ -0,0 +1,3 @@
1
+ module SupportCenter
2
+ VERSION = '0.1.5'
3
+ end
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "support_center",
3
+ "version": "0.0.1",
4
+ "description": "Support Center",
5
+ "homepage": "https://github.com/primedia/support_center",
6
+ "jam": {
7
+ "dependencies": {
8
+ "jquery": null,
9
+ "underscore":null,
10
+ "jquery.cookie": null
11
+ },
12
+ "main": "support_center.js",
13
+ "include": [
14
+ "support_center.js"
15
+ ]
16
+ },
17
+ "author": {
18
+ "name": "Primedia"
19
+ },
20
+ "repositories": [
21
+ {
22
+ "type": "git",
23
+ "url": "https://github.com/primedia/support_center.git"
24
+ }
25
+ ],
26
+ "github": "https://github.com/primedia/support_center.git"
27
+ }
@@ -0,0 +1,20 @@
1
+
2
+ describe "FormComponent", ->
3
+
4
+ formComponent = null
5
+
6
+ beforeEach ->
7
+ ready = false
8
+
9
+ require ['form_component', 'jasmine-jquery'], (_formComponent) ->
10
+ formComponent = _formComponent
11
+ ready = true
12
+
13
+ waitsFor ->
14
+ return ready
15
+
16
+
17
+ describe "initialize", ->
18
+
19
+ it "is defined", ->
20
+ expect(formComponent).toBeDefined()
File without changes
@@ -0,0 +1,19 @@
1
+ requirejs.config({
2
+ waitSeconds: 120,
3
+ baseUrl: "js/support_center",
4
+ paths: {
5
+ "jasmine-jquery": "/vendor/js/jasmine-jquery",
6
+ "jasmine-flight": "/vendor/js/jasmine-flight",
7
+ "jquery": "/vendor/bower/jquery/jquery",
8
+ "es5-shim": "/vendor/bower/es5-shim/es5-shim",
9
+ "es5-sham": "/vendor/bower/es5-shim/es5-sham",
10
+ "flight/lib/component": "/vendor/bower/flight/lib/component",
11
+ "flight/lib/advice": "/vendor/bower/flight/lib/advice",
12
+ "flight/lib/utils": "/vendor/bower/flight/lib/utils",
13
+ "flight/lib/registry": "/vendor/bower/flight/lib/registry",
14
+ "flight/lib/compose": "/vendor/bower/flight/lib/compose",
15
+ "flight/lib/logger": "/vendor/bower/flight/lib/logger",
16
+ "flight/tools/debug": "/vendor/bower/flight/tools/debug"
17
+ }
18
+
19
+ });
@@ -0,0 +1,24 @@
1
+ // Generated by CoffeeScript 1.7.1
2
+ (function() {
3
+ describe("FormComponent", function() {
4
+ var formComponent;
5
+ formComponent = null;
6
+ beforeEach(function() {
7
+ var ready;
8
+ ready = false;
9
+ require(['form_component', 'jasmine-jquery'], function(_formComponent) {
10
+ formComponent = _formComponent;
11
+ return ready = true;
12
+ });
13
+ return waitsFor(function() {
14
+ return ready;
15
+ });
16
+ });
17
+ return describe("initialize", function() {
18
+ return it("is defined", function() {
19
+ return expect(formComponent).toBeDefined();
20
+ });
21
+ });
22
+ });
23
+
24
+ }).call(this);
@@ -0,0 +1,63 @@
1
+ # src_files
2
+ #
3
+ # Return an array of filepaths relative to src_dir to include before jasmine specs.
4
+ # Default: []
5
+ #
6
+ # EXAMPLE:
7
+ #
8
+ # src_files:
9
+ # - lib/source1.js
10
+ # - lib/source2.js
11
+ # - dist/**/*.js
12
+ #
13
+ src_files:
14
+ - vendor/bower/requirejs/require.js
15
+ - spec/javascripts/config/bootstrap.js
16
+
17
+ # helpers
18
+ #
19
+ # Return an array of filepaths relative to spec_dir to include before jasmine specs.
20
+ # Default: ["helpers/**/*.js"]
21
+ #
22
+ # EXAMPLE:
23
+ #
24
+ # helpers:
25
+ # - helpers/**/*.js
26
+ #
27
+ helpers:
28
+ - helpers/**/*.js
29
+
30
+ # spec_files
31
+ #
32
+ # Return an array of filepaths relative to spec_dir to include.
33
+ # Default: ["**/*[sS]pec.js"]
34
+ #
35
+ # EXAMPLE:
36
+ #
37
+ # spec_files:
38
+ # - **/*[sS]pec.js
39
+ #
40
+ spec_files:
41
+ - '**/*[sS]pec.js'
42
+
43
+ # src_dir
44
+ #
45
+ # Source directory path. Your src_files must be returned relative to this path. Will use root if left blank.
46
+ # Default: project root
47
+ #
48
+ # EXAMPLE:
49
+ #
50
+ # src_dir: public
51
+ #
52
+ src_dir:
53
+
54
+ # spec_dir
55
+ #
56
+ # Spec directory path. Your spec_files must be returned relative to this path.
57
+ # Default: spec/javascripts
58
+ #
59
+ # EXAMPLE:
60
+ #
61
+ # spec_dir: spec/javascripts
62
+ #
63
+ spec_dir: spec/javascripts
@@ -0,0 +1,5 @@
1
+ // Generated by CoffeeScript 1.7.1
2
+ (function() {
3
+
4
+
5
+ }).call(this);
@@ -0,0 +1,11 @@
1
+ require 'simplecov'
2
+ SimpleCov.start
3
+
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
6
+ require 'rspec'
7
+ require 'support_center'
8
+
9
+ # Requires supporting files with custom matchers and macros, etc,
10
+ # in ./support/ and its subdirectories.
11
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
File without changes
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/support_center/version', __FILE__)
3
+ require 'date'
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.authors = ["Joe Dursun", "Matt Perryman", "RentPath Team"]
7
+ gem.email = ["jdursun@rentpath.com", "mperryman@rentpath.com"]
8
+ gem.homepage = 'https://github.com/primedia/support_center'
9
+ gem.description = 'This gem provides an interface to the Zendesk API'
10
+ gem.summary = "Rack middleware to interact with Zendesk API"
11
+ gem.date = Date.today.to_s
12
+ gem.executables = []
13
+ gem.files = `git ls-files | grep -v myapp`.split("\n")
14
+ gem.test_files = `git ls-files -- test/*`.split("\n")
15
+ gem.name = "support_center"
16
+ gem.require_paths = ["lib"]
17
+ gem.version = SupportCenter::VERSION
18
+ gem.required_ruby_version = '>= 1.9'
19
+ gem.add_dependency 'zendesk_api', '~> 1.3'
20
+ gem.add_dependency 'sinatra', '>= 1.2.0'
21
+ gem.add_dependency 'slim'
22
+ end
@@ -0,0 +1,485 @@
1
+ /**
2
+ * This version of the Jasmine Flight framework is a fork of the production version
3
+ * The call to instantiate a new Component has been modified to pass the node.
4
+ * Searck for 'FORK' below to find the differences test
5
+ */
6
+
7
+ /**
8
+ * Copyright 2013, Twitter Inc. and other contributors
9
+ * Licensed under the MIT License
10
+ */
11
+
12
+ (function (root) {
13
+ 'use strict';
14
+
15
+ jasmine.flight = {};
16
+
17
+ /**
18
+ * Wrapper for describe. Load component before each test.
19
+ *
20
+ * @param componentPath
21
+ * @param specDefinitions
22
+ */
23
+
24
+ root.describeComponent = function (componentPath, specDefinitions) {
25
+ jasmine.getEnv().describeComponent(componentPath, specDefinitions);
26
+ };
27
+
28
+ root.ddescribeComponent = function (componentPath, specDefinitions) {
29
+ jasmine.getEnv().ddescribeComponent(componentPath, specDefinitions);
30
+ };
31
+
32
+ var describeComponentFactory = function (componentPath, specDefinitions) {
33
+ return function () {
34
+ beforeEach(function () {
35
+ this.Component = this.component = this.$node = null;
36
+
37
+ var requireCallback = function (registry, Component) {
38
+ registry.reset();
39
+ this.Component = Component;
40
+ }.bind(this);
41
+
42
+ require(['flight/lib/registry', componentPath], requireCallback);
43
+
44
+ waitsFor(function () {
45
+ return this.Component !== null;
46
+ }.bind(this));
47
+ });
48
+
49
+ afterEach(function () {
50
+ if (this.$node) {
51
+ this.$node.remove();
52
+ this.$node = null;
53
+ }
54
+
55
+ var requireCallback = function (defineComponent) {
56
+ if (this.component) {
57
+ this.component = null;
58
+ }
59
+
60
+ this.Component = null;
61
+ defineComponent.teardownAll();
62
+ }.bind(this);
63
+
64
+ require(['flight/lib/component'], requireCallback);
65
+
66
+ waitsFor(function () {
67
+ return this.Component === null;
68
+ }.bind(this));
69
+ });
70
+
71
+ specDefinitions.apply(this);
72
+ };
73
+ };
74
+
75
+ jasmine.Env.prototype.describeComponent = function (componentPath, specDefinitions) {
76
+ describe(componentPath, describeComponentFactory(componentPath, specDefinitions));
77
+ };
78
+
79
+ jasmine.Env.prototype.ddescribeComponent = function (componentPath, specDefinitions) {
80
+ ddescribe(componentPath, describeComponentFactory(componentPath, specDefinitions));
81
+ };
82
+
83
+ /**
84
+ * Wrapper for describe. Load mixin before each test.
85
+ *
86
+ * @param mixinPath
87
+ * @param specDefinitions
88
+ */
89
+
90
+ root.describeMixin = function (mixinPath, specDefinitions) {
91
+ jasmine.getEnv().describeMixin(mixinPath, specDefinitions);
92
+ };
93
+
94
+ root.ddescribeMixin = function (mixinPath, specDefinitions) {
95
+ jasmine.getEnv().ddescribeMixin(mixinPath, specDefinitions);
96
+ };
97
+
98
+ var describeMixinFactory = function (mixinPath, specDefinitions) {
99
+ return function () {
100
+ beforeEach(function () {
101
+ this.Component = this.component = this.$node = null;
102
+
103
+ var requireCallback = function (registry, defineComponent, Mixin) {
104
+ registry.reset();
105
+ this.Component = defineComponent(function () {}, Mixin);
106
+ }.bind(this);
107
+
108
+ require(['flight/lib/registry', 'flight/lib/component', mixinPath], requireCallback);
109
+
110
+ waitsFor(function () {
111
+ return this.Component !== null;
112
+ });
113
+ });
114
+
115
+ afterEach(function () {
116
+ if (this.$node) {
117
+ this.$node.remove();
118
+ this.$node = null;
119
+ }
120
+
121
+ var requireCallback = function (defineComponent) {
122
+ if (this.component) {
123
+ this.component = null;
124
+ }
125
+
126
+ this.Component = null;
127
+ defineComponent.teardownAll();
128
+ }.bind(this);
129
+
130
+ require(['flight/lib/component'], requireCallback);
131
+
132
+ waitsFor(function () {
133
+ return this.Component === null;
134
+ }.bind(this));
135
+ });
136
+
137
+ specDefinitions.apply(this);
138
+ };
139
+ };
140
+
141
+ jasmine.Env.prototype.describeMixin = function (mixinPath, specDefinitions) {
142
+ describe(mixinPath, describeMixinFactory(mixinPath, specDefinitions));
143
+ };
144
+
145
+ jasmine.Env.prototype.ddescribeMixin = function (mixinPath, specDefinitions) {
146
+ ddescribe(mixinPath, describeMixinFactory(mixinPath, specDefinitions));
147
+ };
148
+
149
+ /**
150
+ * Wrapper for describe. Load module before each test.
151
+ *
152
+ * @param modulePath
153
+ * @param specDefinitions
154
+ */
155
+
156
+ root.describeModule = function (modulePath, specDefinitions) {
157
+ return jasmine.getEnv().describeModule(modulePath, specDefinitions);
158
+ };
159
+
160
+ jasmine.Env.prototype.describeModule = function (modulePath, specDefinitions) {
161
+ describe(modulePath, function () {
162
+ beforeEach(function () {
163
+ this.module = null;
164
+
165
+ var requireCallback = function (module) {
166
+ this.module = module;
167
+ }.bind(this);
168
+
169
+ require([modulePath], requireCallback);
170
+
171
+ waitsFor(function () {
172
+ return this.module !== null;
173
+ });
174
+ });
175
+
176
+ specDefinitions.apply(this);
177
+ });
178
+ };
179
+
180
+ /**
181
+ * Create root node and initialize component. Fixture should be html string
182
+ * or jQuery object.
183
+ *
184
+ * @param fixture {String} (Optional)
185
+ * @param options {Options} (Optional)
186
+ */
187
+
188
+ root.setupComponent = function (fixture, options) {
189
+ jasmine.getEnv().currentSpec.setupComponent(fixture, options);
190
+ };
191
+
192
+ jasmine.Spec.prototype.setupComponent = function (fixture, options) {
193
+ if (this.component) {
194
+ this.component.teardown();
195
+ this.$node.remove();
196
+ }
197
+
198
+ if (fixture instanceof jQuery || typeof fixture === 'string') {
199
+ this.$node = $(fixture).addClass('component-root');
200
+ } else {
201
+ this.$node = $('<div class="component-root" />');
202
+ options = fixture;
203
+ fixture = null;
204
+ }
205
+
206
+ $('body').append(this.$node);
207
+
208
+ options = options === undefined ? {} : options;
209
+
210
+ // FORK: Component could not be initialized without a node
211
+ // this.component = (new this.Component()).initialize(this.$node, options);
212
+ this.component = new this.Component(this.$node, options);
213
+ };
214
+
215
+
216
+ (function (namespace) {
217
+ var eventsData = {
218
+ spiedEvents: {},
219
+ handlers: []
220
+ };
221
+
222
+ namespace.formatElement = function ($element) {
223
+ var limit = 200;
224
+ var output = '';
225
+
226
+ if ($element instanceof jQuery) {
227
+ output = jasmine.JQuery.elementToString($element);
228
+ if (output.length > limit) {
229
+ output = output.slice(0, 200) + '...';
230
+ }
231
+ } else {
232
+ //$element should always be a jQuery object
233
+ output = 'element is not a jQuery object';
234
+ }
235
+
236
+ return output;
237
+ };
238
+
239
+ namespace.compareColors = function (color1, color2) {
240
+ if (color1.charAt(0) === color2.charAt(0)) {
241
+ return color1 === color2;
242
+ } else {
243
+ return namespace.hex2rgb(color1) === namespace.hex2rgb(color2);
244
+ }
245
+ };
246
+
247
+ namespace.hex2rgb = function (colorString) {
248
+ if (colorString.charAt(0) !== '#') return colorString;
249
+ // note: hexStr should be #rrggbb
250
+ var hex = parseInt(colorString.substring(1), 16);
251
+ var r = (hex & 0xff0000) >> 16;
252
+ var g = (hex & 0x00ff00) >> 8;
253
+ var b = hex & 0x0000ff;
254
+ return 'rgb(' + r + ', ' + g + ', ' + b + ')';
255
+ };
256
+
257
+ namespace.events = {
258
+ spyOn: function (selector, eventName) {
259
+ eventsData.spiedEvents[[selector, eventName]] = {
260
+ callCount: 0,
261
+ calls: [],
262
+ mostRecentCall: {},
263
+ name: eventName
264
+ };
265
+
266
+ var handler = function (e, data) {
267
+ var call = {
268
+ event: e,
269
+ args: jasmine.util.argsToArray(arguments),
270
+ data: data
271
+ };
272
+ eventsData.spiedEvents[[selector, eventName]].callCount++;
273
+ eventsData.spiedEvents[[selector, eventName]].calls.push(call);
274
+ eventsData.spiedEvents[[selector, eventName]].mostRecentCall = call;
275
+ };
276
+
277
+ jQuery(selector).on(eventName, handler);
278
+ eventsData.handlers.push([selector, eventName, handler]);
279
+ return eventsData.spiedEvents[[selector, eventName]];
280
+ },
281
+
282
+ eventArgs: function (selector, eventName, expectedArg) {
283
+ var actualArgs = eventsData.spiedEvents[[selector, eventName]].mostRecentCall.args;
284
+
285
+ if (!actualArgs) {
286
+ throw 'No event spy found on ' + eventName + '. Try adding a call to spyOnEvent or make sure that the selector the event is triggered on and the selector being spied on are correct.';
287
+ }
288
+
289
+ // remove extra event metadata if it is not tested for
290
+ if ((actualArgs.length === 2) && typeof actualArgs[1] === 'object' &&
291
+ expectedArg && !expectedArg.scribeContext && !expectedArg.sourceEventData && !expectedArg.scribeData) {
292
+ actualArgs[1] = $.extend({}, actualArgs[1]);
293
+ delete actualArgs[1].sourceEventData;
294
+ delete actualArgs[1].scribeContext;
295
+ delete actualArgs[1].scribeData;
296
+ }
297
+
298
+ return actualArgs;
299
+ },
300
+
301
+ wasTriggered: function (selector, event) {
302
+ var spiedEvent = eventsData.spiedEvents[[selector, event]];
303
+ return spiedEvent && spiedEvent.callCount > 0;
304
+ },
305
+
306
+ wasTriggeredWith: function (selector, eventName, expectedArg, env) {
307
+ var actualArgs = jasmine.flight.events.eventArgs(selector, eventName, expectedArg);
308
+ return actualArgs && env.contains_(actualArgs, expectedArg);
309
+ },
310
+
311
+ wasTriggeredWithData: function (selector, eventName, expectedArg, env) {
312
+ var actualArgs = jasmine.flight.events.eventArgs(selector, eventName, expectedArg);
313
+ var valid;
314
+
315
+ if (actualArgs) {
316
+ valid = false;
317
+ for (var i = 0; i < actualArgs.length; i++) {
318
+ if (jasmine.flight.validateHash(expectedArg, actualArgs[i])) {
319
+ return true;
320
+ }
321
+ }
322
+ return valid;
323
+ }
324
+
325
+ return false;
326
+ },
327
+
328
+ cleanUp: function () {
329
+ eventsData.spiedEvents = {};
330
+ // unbind all handlers
331
+ for (var i = 0; i < eventsData.handlers.length; i++) {
332
+ jQuery(eventsData.handlers[i][0]).off(eventsData.handlers[i][1], eventsData.handlers[i][2]);
333
+ }
334
+ eventsData.handlers = [];
335
+ }
336
+ };
337
+
338
+ namespace.validateHash = function (a, b, intersection) {
339
+ var validHash;
340
+ for (var field in a) {
341
+ if ((typeof a[field] === 'object') && (typeof b[field] === 'object')) {
342
+ validHash = a[field] === b[field] || jasmine.flight.validateHash(a[field], b[field]);
343
+ } else if (intersection && (typeof a[field] === 'undefined' || typeof b[field] === 'undefined')) {
344
+ validHash = true;
345
+ } else {
346
+ validHash = (a[field] === b[field]);
347
+ }
348
+ if (!validHash) {
349
+ break;
350
+ }
351
+ }
352
+ return validHash;
353
+ };
354
+ })(jasmine.flight);
355
+
356
+ beforeEach(function () {
357
+ this.addMatchers({
358
+ toHaveBeenTriggeredOn: function () {
359
+ var selector = arguments[0];
360
+ var eventName = typeof this.actual === 'string' ? this.actual : this.actual.name;
361
+ var wasTriggered = jasmine.flight.events.wasTriggered(selector, eventName);
362
+
363
+ this.message = function () {
364
+ var $pp = function (obj) {
365
+ var description;
366
+ var attr;
367
+
368
+ if (!(obj instanceof jQuery)) {
369
+ obj = $(obj);
370
+ }
371
+
372
+ description = [
373
+ obj.get(0).nodeName
374
+ ];
375
+
376
+ attr = obj.get(0).attributes || [];
377
+
378
+ for (var x = 0; x < attr.length; x++) {
379
+ description.push(attr[x].name + '="' + attr[x].value + '"');
380
+ }
381
+
382
+ return '<' + description.join(' ') + '>';
383
+ };
384
+
385
+ if (wasTriggered) {
386
+ return [
387
+ '<div class="value-mismatch">Expected event ' + eventName + ' to have been triggered on' + selector,
388
+ '<div class="value-mismatch">Expected event ' + eventName + ' not to have been triggered on' + selector
389
+ ];
390
+ } else {
391
+ return [
392
+ 'Expected event ' + eventName + ' to have been triggered on ' + $pp(selector),
393
+ 'Expected event ' + eventName + ' not to have been triggered on ' + $pp(selector)
394
+ ];
395
+ }
396
+ };
397
+
398
+ return wasTriggered;
399
+ },
400
+
401
+ toHaveBeenTriggeredOnAndWith: function () {
402
+ var selector = arguments[0];
403
+ var expectedArg = arguments[1];
404
+ var exactMatch = !arguments[2];
405
+ var eventName = typeof this.actual === 'string' ? this.actual : this.actual.name;
406
+ var wasTriggered = jasmine.flight.events.wasTriggered(selector, eventName);
407
+
408
+ this.message = function () {
409
+ var $pp = function (obj) {
410
+ var description;
411
+ var attr;
412
+
413
+ if (!(obj instanceof jQuery)) {
414
+ obj = $(obj);
415
+ }
416
+
417
+ description = [
418
+ obj.get(0).nodeName
419
+ ];
420
+
421
+ attr = obj.get(0).attributes || [];
422
+
423
+ for (var x = 0; x < attr.length; x++) {
424
+ description.push(attr[x].name + '="' + attr[x].value + '"');
425
+ }
426
+
427
+ return '<' + description.join(' ') + '>';
428
+ };
429
+
430
+ if (wasTriggered) {
431
+ var actualArg = jasmine.flight.events.eventArgs(selector, eventName, expectedArg)[1];
432
+ return [
433
+ '<div class="value-mismatch">Expected event ' + eventName + ' to have been triggered on' + selector,
434
+ '<div class="value-mismatch">Expected event ' + eventName + ' not to have been triggered on' + selector
435
+ ];
436
+ } else {
437
+ return [
438
+ 'Expected event ' + eventName + ' to have been triggered on ' + $pp(selector),
439
+ 'Expected event ' + eventName + ' not to have been triggered on ' + $pp(selector)
440
+ ];
441
+ }
442
+ };
443
+
444
+ if (!wasTriggered) {
445
+ return false;
446
+ }
447
+
448
+ if (exactMatch) {
449
+ return jasmine.flight.events.wasTriggeredWith(selector, eventName, expectedArg, this.env);
450
+ } else {
451
+ return jasmine.flight.events.wasTriggeredWithData(selector, eventName, expectedArg, this.env);
452
+ }
453
+ },
454
+
455
+ toHaveCss: function (prop, val) {
456
+ var result;
457
+ if (val instanceof RegExp) {
458
+ result = val.test(this.actual.css(prop));
459
+ } else if (prop.match(/color/)) {
460
+ //IE returns colors as hex strings; other browsers return rgb(r, g, b) strings
461
+ result = jasmine.flight.compareColors(this.actual.css(prop), val);
462
+ } else {
463
+ result = this.actual.css(prop) === val;
464
+ //sometimes .css() returns strings when it should return numbers
465
+ if (!result && typeof val === 'number') {
466
+ result = parseFloat(this.actual.css(prop), 10) === val;
467
+ }
468
+ }
469
+
470
+ this.actual = jasmine.flight.formatElement(this.actual);
471
+ return result;
472
+ }
473
+ });
474
+ });
475
+
476
+ root.spyOnEvent = function (selector, eventName) {
477
+ jasmine.JQuery.events.spyOn(selector, eventName);
478
+ return jasmine.flight.events.spyOn(selector, eventName);
479
+ };
480
+
481
+ afterEach(function () {
482
+ jasmine.flight.events.cleanUp();
483
+ });
484
+
485
+ }(this));