kf5 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a3eb3a3fec42ed6f95a4a436ecaece42593d6edd
4
- data.tar.gz: c7d1515809f053724790d54cca8c3e2b12cfdb28
3
+ metadata.gz: 50231b0a61f410d1d4d532d091b04927d9b275c4
4
+ data.tar.gz: fe9f484fd4ae97eb4443e6aceb07b9663933d411
5
5
  SHA512:
6
- metadata.gz: 5aa8979a24e1087d587f50ff52288bc520d3dab17ea589fc42b6e69eb346f28faf5de8fc903945960b39853ddd68f546c3ece1af85273201a409d68776e68e01
7
- data.tar.gz: dab062f39fbdaa8dc5ba06b28c10f9d55ef1123ae1546582c5b467b28f91efdb52f7c73d06bc27ff0118383de023a0081532846118a3822e7a177aee5452ff22
6
+ metadata.gz: 032f67d39110a4cdc78ad65855b2d74c5b4dfc4d544ce67858fab624fef3533ef3056e0437613aa8d98a0dc400cae945401c8bc3805a2b4d16bd4148921feca4
7
+ data.tar.gz: 1b741f647529dee6eb897ea7392937d319edb2f3487efaddf6876f169f135474cd0a114bc0e62a7a447bc811c6bf8eb123d3dd1d1e5867393fd57db50aada80e
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.2
4
+ before_install: gem install bundler
@@ -1,3 +1,7 @@
1
+ # v0.2.0 / 2015-09-19
2
+
3
+ * add embedded javascript plugin.
4
+
1
5
  # v0.1.0 / 2015-09-03
2
6
 
3
7
  Create project
data/Gemfile CHANGED
@@ -2,3 +2,6 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in presenters.gemspec
4
4
  gemspec
5
+
6
+ gem 'simplecov', :require => false
7
+ gem 'coveralls', :require => false
data/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # KF5
2
2
 
3
+ [![Build Status](https://api.travis-ci.org/emn178/kf5.png)](https://travis-ci.org/emn178/kf5)
4
+ [![Coverage Status](https://coveralls.io/repos/emn178/kf5/badge.svg?branch=master)](https://coveralls.io/r/emn178/kf5?branch=master)
5
+
3
6
  A library to integrate with KF5 help desk.[逸創云客服](http://www.kf5.com/)
4
7
 
5
8
  ## Installation
@@ -50,6 +53,11 @@ Or you can assaign arguments manaully
50
53
  redirect_to_kf5 :username => 'username' # others....
51
54
  ```
52
55
 
56
+ In view, you can use `kf5_tag` to include javascript plugin.
57
+ ```ruby
58
+ = kf5_tag
59
+ ```
60
+
53
61
  ## License
54
62
 
55
63
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,561 @@
1
+ (function(window, config)
2
+ {
3
+ "use strict";
4
+
5
+ var
6
+ script = window.document.getElementById('kf5-provide-supportBox'),
7
+ parts = script.src.split('//'),
8
+ host = parts.length > 1 ? parts[0] + '//' + parts[1].split('/')[0] : '',
9
+ kf5Domain = script.getAttribute('kf5-domain'),
10
+ supportboxConfigURL = '//' + kf5Domain + '/supportbox/buttonconfig',
11
+ _config = {
12
+ btn_name : '帮助按钮',
13
+ facade : '1',
14
+ color : '#7A88FF',
15
+ position : 'right',
16
+ iframeURL : '//' + kf5Domain + '/supportbox/index',
17
+ btn_icon : '//assets.kf5.com/supportbox/images/icon2.png',
18
+ styleURL : '//assets.kf5.com/supportbox/css/btn.css',
19
+ template : '<div id="kw-tab" class="kf5-support-btn" style="{{bg_color}}"> <img src="{{btn_icon}}" alt=""><span>{{title}}</span> </div> <div id="kw-block" class="kf5-support"> <a id="kw-close" class="kf5-close" title="关闭"><i class="icon-clear"></i></a> <div id="kw-loading" class="kf5-loading"> </div> <iframe id="kw-widget-iframe" src="" scrolling="no" frameborder="0"></iframe> </div>'
20
+ };
21
+
22
+ function KF5SupportBox(config)
23
+ {
24
+ this.config = config || {};
25
+
26
+ this.init();
27
+ }
28
+
29
+ window.KF5SupportBox = KF5SupportBox;
30
+
31
+ KF5SupportBox.prototype = {
32
+
33
+ el: null,
34
+
35
+ getElement: function(selector)
36
+ {
37
+ return window.document.getElementById(selector.replace('#', ''));
38
+ },
39
+
40
+ getOpt: function(key)
41
+ {
42
+ if(this.config.hasOwnProperty(key))
43
+ {
44
+ return this.config[key];
45
+ }
46
+ else
47
+ {
48
+ return _config[key];
49
+ }
50
+ },
51
+
52
+ init: function()
53
+ {
54
+ this._prepareStyle();
55
+
56
+ return this;
57
+ },
58
+
59
+ initElements: function()
60
+ {
61
+ this._prepareElement();
62
+ this.append();
63
+
64
+ this._bindEvents();
65
+ },
66
+
67
+ append: function()
68
+ {
69
+ window.document.body.appendChild(this.el);
70
+
71
+ return this;
72
+ },
73
+
74
+ _prepareStyle: function()
75
+ {
76
+ var link = window.document.createElement('link');
77
+
78
+ link.rel = 'styleSheet';
79
+ link.type = 'text/css';
80
+ link.href = this.getOpt('styleURL');
81
+
82
+ var self = this;
83
+ link.onload = function()
84
+ {
85
+ self.initElements();
86
+ link.onload = null;
87
+ };
88
+
89
+ window.document.body.appendChild(link);
90
+
91
+ return this;
92
+ },
93
+
94
+ _prepareElement: function()
95
+ {
96
+ var className = 'kf5-support-123456789';
97
+
98
+ // 位置
99
+ if(this.getOpt('position') === 'left')
100
+ {
101
+ className += ' kf5-left';
102
+ }
103
+ else
104
+ {
105
+ className += ' kf5-right'
106
+ }
107
+
108
+ // 外观
109
+ className += ' kf5-style' + (parseInt(this.getOpt('facade')) || 1);
110
+
111
+ // 设备
112
+ if(this.getOpt('is_mobile'))
113
+ {
114
+ className += ' kf5-mobile';
115
+ }
116
+
117
+ this.el = window.document.createElement('div');
118
+ this.el.setAttribute('class', className);
119
+ this.el.innerHTML = this.getOpt('template')
120
+ .replace('{{title}}', this.getOpt('btn_name') || '获取帮助')
121
+ .replace('{{btn_icon}}', this.getOpt('btn_icon'))
122
+ .replace('{{bg_color}}', this.getOpt('color') ? 'background:' + this.getOpt('color') : '');
123
+
124
+ return this;
125
+ },
126
+
127
+ _bindEvents: function()
128
+ {
129
+ var self = this;
130
+ this.getElement('#kw-tab').onclick = function()
131
+ {
132
+ self.open();
133
+ };
134
+
135
+ this.getElement('#kw-close').onclick = function()
136
+ {
137
+ self.close();
138
+ };
139
+
140
+ this.getElement('#kw-widget-iframe').onload = function()
141
+ {
142
+ // setTimeout(function()
143
+ // {
144
+ self.getElement('#kw-loading').style.display = 'none';
145
+ // }, 2000);
146
+
147
+ };
148
+
149
+ if(cookie('kf5-supportBox-autoOpen'))
150
+ {
151
+ this.open();
152
+ }
153
+
154
+ return this;
155
+ },
156
+
157
+ loadIframe: function()
158
+ {
159
+ var iframe = this.getElement('#kw-widget-iframe');
160
+
161
+ if(!iframe.getAttribute('src'))
162
+ {
163
+ iframe.setAttribute('src', this.getOpt('iframeURL'));
164
+ }
165
+
166
+ return this;
167
+ },
168
+
169
+ // resizeIframe: function(data)
170
+ // {
171
+ // var elem = this.getElement('#kw-block');
172
+
173
+ // if(elem)
174
+ // {
175
+ // elem.style.height = parseFloat(data) + 'px';
176
+ // }
177
+ // },
178
+
179
+ open: function(e)
180
+ {
181
+ if(!this.isOpened)
182
+ {
183
+ hideElement(this.getElement('#kw-tab'));
184
+
185
+ this.loadIframe();
186
+ // slideUp(this.getElement('#kw-widget-iframe'), 400);
187
+ slideUp(this.getElement('#kw-block'), 400);
188
+
189
+ cookie('kf5-supportBox-autoOpen', 1, {expires: 3 / 24, path: '/'});
190
+
191
+ this.isOpened = true;
192
+ }
193
+ },
194
+
195
+ close: function(e)
196
+ {
197
+ // slideDown(this.getElement('#kw-widget-iframe'), 400);
198
+ slideDown(this.getElement('#kw-block'), 400);
199
+
200
+ showElement(this.getElement('#kw-tab'));
201
+ // this.getElement('#kw-widget-iframe').setAttribute('src', '');
202
+
203
+ cookie('kf5-supportBox-autoOpen', null, {path: '/'});
204
+
205
+ this.isOpened = false;
206
+ }
207
+ };
208
+
209
+ var supportbox;
210
+ function onload()
211
+ {
212
+ var script = window.document.createElement('script'),
213
+ configURL = supportboxConfigURL;
214
+
215
+ embed(configURL, function(win)
216
+ {
217
+ if(win.KF5_SUPPORTBOX_BUTTON && win.KF5_SUPPORTBOX_BUTTON.show)
218
+ {
219
+ supportbox = new KF5SupportBox(win.KF5_SUPPORTBOX_BUTTON);
220
+
221
+ // 延时自动加载iframe
222
+ // setTimeout(function()
223
+ // {
224
+ // supportbox.loadIframe();
225
+ // }, 30 * 1000);
226
+ }
227
+ });
228
+ }
229
+
230
+ document.addEventListener('page:load', onload, false);
231
+ window.addEventListener('load', onload, false);
232
+
233
+ // allow cross origin operation
234
+ window.addEventListener('message', function(e)
235
+ {
236
+ var context, cmd, data;
237
+
238
+ if(e.data)
239
+ {
240
+ context = e.data.match(/^([^ ]+)(?: +(.*))?/);
241
+ cmd = context[1];
242
+ data = context[2];
243
+ }
244
+
245
+ if(cmd === 'CMD::showSupportbox')
246
+ {
247
+ if(supportbox)
248
+ {
249
+ supportbox.open();
250
+ }
251
+ }
252
+ else if(cmd === 'CMD::hideSupportbox')
253
+ {
254
+ if(supportbox)
255
+ {
256
+ supportbox.close();
257
+ }
258
+ }
259
+ else if(cmd === 'CMD::resizeIframe')
260
+ {
261
+ // supportbox.resizeIframe(data);
262
+ }
263
+ });
264
+
265
+ function embed(mainJS, output)
266
+ {
267
+ var domain, doc, iframeWindow, iframeDoc,
268
+ iframe = window.document.createElement("iframe");
269
+
270
+ iframe.src = "javascript:false",
271
+ iframe.title = "",
272
+ iframe.role = "presentation",
273
+ (iframe.frameElement || iframe).style.cssText = "display: none",
274
+ window.document.body.appendChild(iframe);
275
+ iframeWindow = iframe.contentWindow,
276
+ iframeDoc = iframeWindow.document;
277
+
278
+ try {
279
+ doc = iframeDoc
280
+ } catch (c) {
281
+ domain = window.document.domain,
282
+ iframe.src = 'javascript:var doc=document.open();doc.domain="' + domain + '";void(0);',
283
+ doc = iframeDoc
284
+ }
285
+
286
+ doc.open().start = function() {
287
+ if(output)
288
+ {
289
+ if(typeof output === 'object')
290
+ {
291
+ output.iframeWindow = iframeWindow;
292
+ }
293
+ else if(typeof output === 'function')
294
+ {
295
+ output(iframeWindow);
296
+ }
297
+ }
298
+ },
299
+
300
+ doc.write('<body onload="document.start();">'),
301
+ doc.write('<script src="' + mainJS + '""></script>'),
302
+ doc.close();
303
+ }
304
+
305
+ function getStyle(el, style)
306
+ {
307
+ var
308
+ pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source,
309
+ rmargin = /^margin/,
310
+ rposition = /^(top|right|bottom|left)$/,
311
+ rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ),
312
+
313
+ getStyles, curCSS;
314
+
315
+ if ( window.getComputedStyle ) {
316
+ getStyles = function( elem ) {
317
+ return elem.ownerDocument.defaultView.getComputedStyle( elem, null );
318
+ };
319
+
320
+ curCSS = function( elem, name, computed ) {
321
+ var width, minWidth, maxWidth, ret,
322
+ style = elem.style;
323
+
324
+ computed = computed || getStyles( elem );
325
+
326
+ // getPropertyValue is only needed for .css('filter') in IE9, see #12537
327
+ ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined;
328
+
329
+ if ( computed ) {
330
+
331
+ // if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
332
+ // ret = jQuery.style( elem, name );
333
+ // }
334
+
335
+ // A tribute to the "awesome hack by Dean Edwards"
336
+ // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right
337
+ // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels
338
+ // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values
339
+ if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) {
340
+
341
+ // Remember the original values
342
+ width = style.width;
343
+ minWidth = style.minWidth;
344
+ maxWidth = style.maxWidth;
345
+
346
+ // Put in the new values to get a computed value out
347
+ style.minWidth = style.maxWidth = style.width = ret;
348
+ ret = computed.width;
349
+
350
+ // Revert the changed values
351
+ style.width = width;
352
+ style.minWidth = minWidth;
353
+ style.maxWidth = maxWidth;
354
+ }
355
+ }
356
+
357
+ // Support: IE
358
+ // IE returns zIndex value as an integer.
359
+ return ret === undefined ?
360
+ ret :
361
+ ret + "";
362
+ };
363
+ } else if ( document.documentElement.currentStyle ) {
364
+ getStyles = function( elem ) {
365
+ return elem.currentStyle;
366
+ };
367
+
368
+ curCSS = function( elem, name, computed ) {
369
+ var left, rs, rsLeft, ret,
370
+ style = elem.style;
371
+
372
+ computed = computed || getStyles( elem );
373
+ ret = computed ? computed[ name ] : undefined;
374
+
375
+ // Avoid setting ret to empty string here
376
+ // so we don't default to auto
377
+ if ( ret == null && style && style[ name ] ) {
378
+ ret = style[ name ];
379
+ }
380
+
381
+ // From the awesome hack by Dean Edwards
382
+ // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
383
+
384
+ // If we're not dealing with a regular pixel number
385
+ // but a number that has a weird ending, we need to convert it to pixels
386
+ // but not position css attributes, as those are proportional to the parent element instead
387
+ // and we can't measure the parent instead because it might trigger a "stacking dolls" problem
388
+ if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) {
389
+
390
+ // Remember the original values
391
+ left = style.left;
392
+ rs = elem.runtimeStyle;
393
+ rsLeft = rs && rs.left;
394
+
395
+ // Put in the new values to get a computed value out
396
+ if ( rsLeft ) {
397
+ rs.left = elem.currentStyle.left;
398
+ }
399
+ style.left = name === "fontSize" ? "1em" : ret;
400
+ ret = style.pixelLeft + "px";
401
+
402
+ // Revert the changed values
403
+ style.left = left;
404
+ if ( rsLeft ) {
405
+ rs.left = rsLeft;
406
+ }
407
+ }
408
+
409
+ // Support: IE
410
+ // IE returns zIndex value as an integer.
411
+ return ret === undefined ?
412
+ ret :
413
+ ret + "" || "auto";
414
+ };
415
+ }
416
+
417
+ return curCSS && curCSS(el, style);
418
+ }
419
+
420
+ function slideUp(elem, duration)
421
+ {
422
+ var originalStyle = elem.getAttribute('style');
423
+ elem.style.display = 'block';
424
+ var originalHeight = parseInt(getStyle(elem, 'height'));
425
+
426
+ elem.style.height = '0px';
427
+
428
+ duration = duration || 500;
429
+ var tStart = Date.now(),
430
+ cost = 0,
431
+ timer = setInterval(function()
432
+ {
433
+ cost = (Date.now() - tStart) / duration;
434
+ cost = cost > 1 ? 1 : cost;
435
+
436
+ elem.style.height = cost * originalHeight + 'px';
437
+
438
+ if(cost >= 1)
439
+ {
440
+ clearInterval(timer);
441
+ timer = null;
442
+
443
+ if(originalStyle)
444
+ {
445
+ elem.setAttribute('style', originalStyle);
446
+ }
447
+ else
448
+ {
449
+ elem.removeAttribute('style');
450
+ }
451
+ elem.style.display = 'block';
452
+ }
453
+ }, 1000 / 60);
454
+ }
455
+
456
+ function slideDown(elem, duration)
457
+ {
458
+ var originalStyle = elem.getAttribute('style');
459
+ elem.style.display = 'block';
460
+
461
+ var originalHeight = parseInt(getStyle(elem, 'height'));
462
+
463
+ duration = duration || 500;
464
+ var tStart = Date.now(),
465
+ cost = 0,
466
+ timer = setInterval(function()
467
+ {
468
+ cost = (Date.now() - tStart) / duration;
469
+ cost = cost > 1 ? 1 : cost;
470
+
471
+ elem.style.height = (1 - cost) * originalHeight + 'px';
472
+
473
+ if(cost >= 1)
474
+ {
475
+ clearInterval(timer);
476
+ timer = null;
477
+
478
+ if(originalStyle)
479
+ {
480
+ elem.setAttribute('style', originalStyle);
481
+ }
482
+ else
483
+ {
484
+ elem.removeAttribute('style');
485
+ }
486
+ elem.style.display = 'none';
487
+ }
488
+ }, 1000 / 60);
489
+ }
490
+
491
+ function hideElement(el)
492
+ {
493
+ el.style.display = 'none';
494
+ }
495
+
496
+ function showElement(el)
497
+ {
498
+ el.style.display = 'block';
499
+ }
500
+
501
+ function cookie(name, value, options)
502
+ {
503
+ if (typeof value != 'undefined')
504
+ { // name and value given, set cookie
505
+ options = options || {};
506
+ if (value === null) {
507
+ value = '';
508
+ options.expires = -1;
509
+ }
510
+ var expires = '';
511
+ if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
512
+ var date;
513
+ if (typeof options.expires == 'number') {
514
+ date = new Date();
515
+ date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
516
+ } else {
517
+ date = options.expires;
518
+ }
519
+ expires = '; expires=' + date.toUTCString();
520
+ }
521
+ var path = options.path ? '; path=' + (options.path) : '';
522
+ var domain = options.domain ? '; domain=' + (options.domain) : '';
523
+ var secure = options.secure ? '; secure' : '';
524
+ document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
525
+ } else {
526
+ var cookieValue = null;
527
+ if (document.cookie && document.cookie != '') {
528
+ var cookies = document.cookie.split(';');
529
+ for (var i = 0; i < cookies.length; i++) {
530
+ var cookie = cookies[i].replace(/^\s+|\s+$/g, '');
531
+ if (cookie.substring(0, name.length + 1) == (name + '=')) {
532
+ cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
533
+ break;
534
+ }
535
+ }
536
+ }
537
+ return cookieValue;
538
+ }
539
+ }
540
+
541
+ function isPC()
542
+ {
543
+ var userAgentInfo = navigator.userAgent;
544
+ var Agents = ['Android', 'iPhone',
545
+ 'SymbianOS', 'Windows Phone',
546
+ 'iPad', 'iPod'];
547
+ var flag = true;
548
+
549
+ for(var i = 0; i < Agents.length; i++)
550
+ {
551
+ if(userAgentInfo.indexOf(Agents[i]) !== -1)
552
+ {
553
+ flag = false;
554
+ break;
555
+ }
556
+ }
557
+
558
+ return flag;
559
+ }
560
+
561
+ })(window);
data/lib/kf5.rb CHANGED
@@ -5,6 +5,7 @@ require "kf5/version"
5
5
  require "kf5/configuration"
6
6
  require "kf5/properties"
7
7
  require "kf5/helper"
8
+ require "kf5/view_helper"
8
9
 
9
10
  module KF5
10
11
  def self.configure(&block)
@@ -21,6 +22,10 @@ module KF5
21
22
  ActiveSupport.on_load :action_controller do
22
23
  include KF5::Helper
23
24
  end
25
+ ActiveSupport.on_load :action_view do
26
+ include KF5::ViewHelper
27
+ end
28
+ Rails.application.config.assets.precompile += %w( kf5.js )
24
29
  end
25
30
  end
26
31
  end
@@ -1,3 +1,3 @@
1
1
  module KF5
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -0,0 +1,7 @@
1
+ module KF5
2
+ module ViewHelper
3
+ def kf5_tag
4
+ javascript_include_tag "kf5.js", :id => "kf5-provide-supportBox", :"kf5-domain" => "#{KF5.configuration.domain}.kf5.com"
5
+ end
6
+ end
7
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kf5
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chen Yi-Cyuan
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-09-03 00:00:00.000000000 Z
11
+ date: 2015-09-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -75,18 +75,21 @@ extra_rdoc_files: []
75
75
  files:
76
76
  - ".gitignore"
77
77
  - ".rspec"
78
+ - ".travis.yml"
78
79
  - CHANGELOG.md
79
80
  - Gemfile
80
81
  - Gemfile.lock
81
82
  - LICENSE.txt
82
83
  - README.md
83
84
  - Rakefile
85
+ - app/assets/javascripts/kf5.js
84
86
  - kf5.gemspec
85
87
  - lib/kf5.rb
86
88
  - lib/kf5/configuration.rb
87
89
  - lib/kf5/helper.rb
88
90
  - lib/kf5/properties.rb
89
91
  - lib/kf5/version.rb
92
+ - lib/kf5/view_helper.rb
90
93
  homepage: https://github.com/emn178/kf5
91
94
  licenses:
92
95
  - MIT