darjeelink 0.11.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +82 -0
- data/Rakefile +24 -0
- data/app/assets/config/darjeelink_manifest.js +2 -0
- data/app/assets/javascripts/darjeelink/application.js +15 -0
- data/app/assets/javascripts/darjeelink/sparkleh.js +248 -0
- data/app/assets/javascripts/darjeelink/tracking_link_generator.js +36 -0
- data/app/assets/stylesheets/darjeelink/application.css +15 -0
- data/app/controllers/darjeelink/application_controller.rb +31 -0
- data/app/controllers/darjeelink/sessions_controller.rb +31 -0
- data/app/controllers/darjeelink/short_links_controller.rb +111 -0
- data/app/controllers/darjeelink/tracking_links_controller.rb +8 -0
- data/app/helpers/darjeelink/application_helper.rb +6 -0
- data/app/importers/darjeelink/rebrandly_importer.rb +48 -0
- data/app/importers/darjeelink/short_link_importer.rb +32 -0
- data/app/jobs/darjeelink/application_job.rb +6 -0
- data/app/mailers/darjeelink/application_mailer.rb +8 -0
- data/app/models/darjeelink/application_record.rb +7 -0
- data/app/models/darjeelink/short_link.rb +58 -0
- data/app/views/darjeelink/short_links/_short_link_form.erb +24 -0
- data/app/views/darjeelink/short_links/edit.html.erb +29 -0
- data/app/views/darjeelink/short_links/index.html.erb +75 -0
- data/app/views/darjeelink/short_links/new.html.erb +12 -0
- data/app/views/darjeelink/tracking_links/new.erb +49 -0
- data/app/views/layouts/darjeelink/application.html.erb +39 -0
- data/config/initializers/darjeelink.rb +31 -0
- data/config/initializers/omniauth.rb +10 -0
- data/config/initializers/rebrandly.rb +5 -0
- data/config/routes.rb +11 -0
- data/db/migrate/20190103122918_create_portkey_short_links.rb +16 -0
- data/db/migrate/20190304094710_case_insensitive_index_short_link_shortened_path.rb +14 -0
- data/db/migrate/20200505220716_rename_portkey_short_links_to_darjeelink_short_links.rb +7 -0
- data/db/migrate/20200505221026_rename_portkey_short_link_index.rb +15 -0
- data/lib/darjeelink.rb +30 -0
- data/lib/darjeelink/configuration.rb +19 -0
- data/lib/darjeelink/engine.rb +23 -0
- data/lib/darjeelink/version.rb +5 -0
- data/lib/tasks/darjeelink.rake +6 -0
- metadata +250 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e3c06938083f8ae098dff14aafa2ee82c7f6a66542c11b456b9cefeb0f5de539
|
4
|
+
data.tar.gz: b76ebe24503d73b87637e7af023d6d08e3f3393e25df173c1f589c51d8389b25
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8acdf94c6b942588ba287d5e745e31b0e36fa8fe1118514eeaaae18e0613b090499924066c7c45b828645df9a3cc4a456916c1f23c11ce528397e1b1fa91105a
|
7
|
+
data.tar.gz: 57ff7590616f9c5b115b5d96ae6b85befc2a37019b8f572c5ac5adababa118860168cef3e8057419adda329fcccdffa021295731b7a4d0f9aabe03158ce5bbac
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2018
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
# Darjeelink
|
2
|
+
|
3
|
+
## What is it?
|
4
|
+
Darjeelink is a very simple link shortener gem.
|
5
|
+
Authorized users can create shortened links.
|
6
|
+
The shortened link can be visited by anyone, and they are redirected to the original link.
|
7
|
+
|
8
|
+
Authorisation is handled by Google oauth.
|
9
|
+
|
10
|
+
It needs to be mounted inside a rails app to be used.
|
11
|
+
|
12
|
+
### Analytics
|
13
|
+
It tracks the number of times a link has been clicked. This is the only metric that is tracked.
|
14
|
+
|
15
|
+
### UTM
|
16
|
+
There is a UTM generator, where you can provide:
|
17
|
+
- a base url
|
18
|
+
- a source and medium
|
19
|
+
- a campaign identifier
|
20
|
+
And you can get a link with UTM params all filled in, and shortern it with one click.
|
21
|
+
|
22
|
+
## Development
|
23
|
+
The recommended approach is to use Vagrant. `vagrant up` will create an isolated darjeelink instance
|
24
|
+
|
25
|
+
### Setup development environment
|
26
|
+
Run `cp .env.sample spec/dummy/.env.development`
|
27
|
+
Nothing else required
|
28
|
+
|
29
|
+
### Setup test environment
|
30
|
+
Run `cp .env.sample spec/dummy/.env.test`
|
31
|
+
Change the database url to be different to the development one i.e. `postgres://darjeelink_dbuser:password@localhost/darjeelink-test`
|
32
|
+
|
33
|
+
## Installation
|
34
|
+
### Installing the private gem
|
35
|
+
Copy the contents of the darjeelink directory into the vendor folder in the
|
36
|
+
darjeelink app repository and run bundle install
|
37
|
+
|
38
|
+
### Mounting the engine
|
39
|
+
Add these lines to your app's routes.rb
|
40
|
+
```
|
41
|
+
mount Darjeelink::Engine => "/"
|
42
|
+
|
43
|
+
# Go to short links
|
44
|
+
get '/:id' => "shortener/shortened_urls#show"
|
45
|
+
```
|
46
|
+
|
47
|
+
### Set the environment variables
|
48
|
+
|
49
|
+
#### Required
|
50
|
+
- DATABASE_URL
|
51
|
+
|
52
|
+
##### For Google Oauth:
|
53
|
+
- AUTH_DOMAIN
|
54
|
+
- GOOGLE_API_CLIENT_ID
|
55
|
+
- GOOGLE_API_CLIENT_SECRET
|
56
|
+
|
57
|
+
#### Optional
|
58
|
+
- FALLBACK_URL - If someone tries to visit a link that does not exist then they will go here.
|
59
|
+
- PERMITTED_IPS - If you want to admin access to certain IP addresses then use this setting.
|
60
|
+
- REBRANDLY_API_KEY - Used to fetch links from rebrandly if importing old links from there.
|
61
|
+
|
62
|
+
## Adding new UTM options
|
63
|
+
In `config/initializers/darjeelink.rb` edit the `config.source_mediums` hash.
|
64
|
+
|
65
|
+
Each key is a hyphenated source-medium. If you just want the source then omit the hyphen and medium.
|
66
|
+
|
67
|
+
Each value is a slightly more readable version for display
|
68
|
+
|
69
|
+
## GDPR
|
70
|
+
No personally identifiable data is stored about the public by this gem.
|
71
|
+
It does not store information on individual clicks, only a counter of how many times a link has been clicked.
|
72
|
+
There are no cookies required to visit a short link.
|
73
|
+
|
74
|
+
For the admin side there is a session cookie that stores information in the session cookie
|
75
|
+
TODO: LIMIT THIS TO EMAIL
|
76
|
+
|
77
|
+
## Contributing
|
78
|
+
This gem has been developed by 38 Degrees to meet our link shortening needs.
|
79
|
+
To Contribute, create a PR, Doesn't have to be from a fork
|
80
|
+
|
81
|
+
## License
|
82
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'bundler/setup'
|
5
|
+
rescue LoadError
|
6
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'rdoc/task'
|
10
|
+
|
11
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
12
|
+
rdoc.rdoc_dir = 'rdoc'
|
13
|
+
rdoc.title = 'Darjeelink'
|
14
|
+
rdoc.options << '--line-numbers'
|
15
|
+
rdoc.rdoc_files.include('README.md')
|
16
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
17
|
+
end
|
18
|
+
|
19
|
+
APP_RAKEFILE = File.expand_path('spec/dummy/Rakefile', __dir__)
|
20
|
+
load 'rails/tasks/engine.rake'
|
21
|
+
|
22
|
+
load 'rails/tasks/statistics.rake'
|
23
|
+
|
24
|
+
require 'bundler/gem_tasks'
|
@@ -0,0 +1,15 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// compiled file. JavaScript code in this file should be added after the last require_* statement.
|
9
|
+
//
|
10
|
+
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
//
|
13
|
+
//= require rails-ujs
|
14
|
+
//= require activestorage
|
15
|
+
//= require ./sparkleh.js
|
@@ -0,0 +1,248 @@
|
|
1
|
+
$(function() {
|
2
|
+
$("body").sparkleh({
|
3
|
+
color: "rainbow",
|
4
|
+
count: 100,
|
5
|
+
overlap: 10
|
6
|
+
});
|
7
|
+
|
8
|
+
// an array can be passed, too for colours
|
9
|
+
// for an image, the image needs to be fully loaded to set
|
10
|
+
// the canvas to it's height/width.
|
11
|
+
// speed allows us to control... the ... velocity
|
12
|
+
$("#image").imagesLoaded( function() {
|
13
|
+
$(".img").sparkleh({
|
14
|
+
count: 25,
|
15
|
+
color: ["#00afec","#fb6f4a","#fdfec5"],
|
16
|
+
speed: 0.4
|
17
|
+
});
|
18
|
+
});
|
19
|
+
});
|
20
|
+
|
21
|
+
$.fn.sparkleh = function(options) {
|
22
|
+
return this.each(function(k,v) {
|
23
|
+
var $this = $(v).css("position","relative");
|
24
|
+
|
25
|
+
var settings = $.extend({
|
26
|
+
width: $this.outerWidth(),
|
27
|
+
height: $this.outerHeight(),
|
28
|
+
color: "#FFFFFF",
|
29
|
+
count: 30,
|
30
|
+
overlap: 0,
|
31
|
+
speed: 1
|
32
|
+
}, options );
|
33
|
+
|
34
|
+
var sparkle = new Sparkle($this, settings);
|
35
|
+
sparkle.over();
|
36
|
+
});
|
37
|
+
}
|
38
|
+
|
39
|
+
function Sparkle($parent, options) {
|
40
|
+
this.options = options;
|
41
|
+
this.init($parent);
|
42
|
+
}
|
43
|
+
|
44
|
+
Sparkle.prototype = {
|
45
|
+
"init" : function($parent) {
|
46
|
+
var _this = this;
|
47
|
+
|
48
|
+
this.$canvas =
|
49
|
+
$("<canvas>")
|
50
|
+
.addClass("sparkle-canvas")
|
51
|
+
.css({
|
52
|
+
position: "absolute",
|
53
|
+
top: "-"+_this.options.overlap+"px",
|
54
|
+
left: "-"+_this.options.overlap+"px",
|
55
|
+
"pointer-events": "none"
|
56
|
+
})
|
57
|
+
.appendTo($parent);
|
58
|
+
|
59
|
+
this.canvas = this.$canvas[0];
|
60
|
+
this.context = this.canvas.getContext("2d");
|
61
|
+
|
62
|
+
this.sprite = new Image();
|
63
|
+
this.sprites = [0, 6, 13, 20];
|
64
|
+
this.sprite.src = this.datauri;
|
65
|
+
|
66
|
+
this.canvas.width = this.options.width + (this.options.overlap * 2);
|
67
|
+
this.canvas.height = this.options.height + (this.options.overlap * 2);
|
68
|
+
|
69
|
+
|
70
|
+
this.particles = this.createSparkles(this.canvas.width, this.canvas.height);
|
71
|
+
|
72
|
+
this.anim = null;
|
73
|
+
this.fade = false;
|
74
|
+
},
|
75
|
+
"createSparkles" : function(w, h) {
|
76
|
+
var holder = [];
|
77
|
+
|
78
|
+
for (var i = 0; i < this.options.count; i++) {
|
79
|
+
var color = this.options.color;
|
80
|
+
|
81
|
+
if (this.options.color == "rainbow") {
|
82
|
+
color = '#' + ('000000' + Math.floor(Math.random() * 16777215).toString(16)).slice(-6);
|
83
|
+
} else if ($.type(this.options.color) === "array") {
|
84
|
+
color = this.options.color[Math.floor(Math.random() * this.options.color.length)];
|
85
|
+
}
|
86
|
+
|
87
|
+
holder[i] = {
|
88
|
+
position: {
|
89
|
+
x: Math.floor(Math.random() * w),
|
90
|
+
y: Math.floor(Math.random() * h)
|
91
|
+
},
|
92
|
+
style: this.sprites[Math.floor(Math.random() * 4)],
|
93
|
+
delta: {
|
94
|
+
x: Math.floor(Math.random() * 1000) - 500,
|
95
|
+
y: Math.floor(Math.random() * 1000) - 500
|
96
|
+
},
|
97
|
+
size: parseFloat((Math.random() * 2).toFixed(2)),
|
98
|
+
color: color
|
99
|
+
};
|
100
|
+
|
101
|
+
}
|
102
|
+
|
103
|
+
return holder;
|
104
|
+
},
|
105
|
+
"draw" : function(time, fade) {
|
106
|
+
var ctx = this.context;
|
107
|
+
|
108
|
+
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
109
|
+
|
110
|
+
for (var i = 0; i < this.options.count; i++) {
|
111
|
+
var derpicle = this.particles[i];
|
112
|
+
var modulus = Math.floor(Math.random() * 7);
|
113
|
+
|
114
|
+
if (Math.floor(time) % modulus === 0) {
|
115
|
+
derpicle.style = this.sprites[Math.floor(Math.random() * 4)];
|
116
|
+
}
|
117
|
+
|
118
|
+
ctx.save();
|
119
|
+
ctx.globalAlpha = derpicle.opacity;
|
120
|
+
ctx.drawImage(this.sprite, derpicle.style, 0, 7, 7, derpicle.position.x, derpicle.position.y, 7, 7);
|
121
|
+
|
122
|
+
if (this.options.color) {
|
123
|
+
ctx.globalCompositeOperation = "source-atop";
|
124
|
+
ctx.globalAlpha = 0.5;
|
125
|
+
ctx.fillStyle = derpicle.color;
|
126
|
+
ctx.fillRect(derpicle.position.x, derpicle.position.y, 7, 7);
|
127
|
+
}
|
128
|
+
|
129
|
+
ctx.restore();
|
130
|
+
}
|
131
|
+
},
|
132
|
+
"update" : function() {
|
133
|
+
var _this = this;
|
134
|
+
|
135
|
+
this.anim = window.requestAnimationFrame(function(time) {
|
136
|
+
_this.updateParticles();
|
137
|
+
_this.draw(time);
|
138
|
+
|
139
|
+
if (_this.fade) {
|
140
|
+
_this.fadeCount -= 1;
|
141
|
+
|
142
|
+
if (_this.fadeCount < 0) {
|
143
|
+
return window.cancelAnimationFrame( _this.anim );
|
144
|
+
}
|
145
|
+
}
|
146
|
+
|
147
|
+
_this.update();
|
148
|
+
});
|
149
|
+
},
|
150
|
+
"cancel" : function() {
|
151
|
+
this.fadeCount = 100;
|
152
|
+
},
|
153
|
+
"over" : function() {
|
154
|
+
window.cancelAnimationFrame(this.anim);
|
155
|
+
|
156
|
+
for (var i = 0; i < this.options.count; i++) {
|
157
|
+
this.particles[i].opacity = Math.random();
|
158
|
+
}
|
159
|
+
|
160
|
+
this.fade = false;
|
161
|
+
this.update();
|
162
|
+
},
|
163
|
+
"out" : function() {
|
164
|
+
this.fade = true;
|
165
|
+
this.cancel();
|
166
|
+
},
|
167
|
+
"updateParticles": function() {
|
168
|
+
for (var i = 0; i < this.options.count; i++) {
|
169
|
+
this.updateParticlePosition(this.particles[i]);
|
170
|
+
this.updateParticleOpacity(this.particles[i]);
|
171
|
+
}
|
172
|
+
},
|
173
|
+
"updateParticleOpacity": function(particle) {
|
174
|
+
if (this.fade) {
|
175
|
+
particle.opacity -= 0.02;
|
176
|
+
} else {
|
177
|
+
particle.opacity -= 0.005;
|
178
|
+
}
|
179
|
+
|
180
|
+
if (particle.opacity <= 0) {
|
181
|
+
particle.opacity = (this.fade) ? 0 : 1;
|
182
|
+
}
|
183
|
+
},
|
184
|
+
"updateParticlePosition": function(particle) {
|
185
|
+
this.moveParticle(particle);
|
186
|
+
|
187
|
+
if (particle.position.x > this.canvas.width) {
|
188
|
+
particle.position.x = -7;
|
189
|
+
} else if (particle.position.x < -7) {
|
190
|
+
particle.position.x = this.canvas.width;
|
191
|
+
}
|
192
|
+
|
193
|
+
if (particle.position.y > this.canvas.height) {
|
194
|
+
particle.position.y = -7;
|
195
|
+
particle.position.x = Math.floor(Math.random()*this.canvas.width);
|
196
|
+
} else if (particle.position.y < -7) {
|
197
|
+
particle.position.y = this.canvas.height;
|
198
|
+
particle.position.x = Math.floor(Math.random()*this.canvas.width);
|
199
|
+
}
|
200
|
+
},
|
201
|
+
"moveParticle": function(particle) {
|
202
|
+
var randX = (Math.random() > Math.random() * 2);
|
203
|
+
var randY = (Math.random() > Math.random() * 3);
|
204
|
+
|
205
|
+
if (randX) {
|
206
|
+
particle.position.x += ((particle.delta.x * this.options.speed) / 1500);
|
207
|
+
}
|
208
|
+
|
209
|
+
if (!randY) {
|
210
|
+
particle.position.y -= ((particle.delta.y * this.options.speed) / 800);
|
211
|
+
}
|
212
|
+
},
|
213
|
+
"datauri" : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABsAAAAHCAYAAAD5wDa1AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNS4xIE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozNDNFMzM5REEyMkUxMUUzOEE3NEI3Q0U1QUIzMTc4NiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDozNDNFMzM5RUEyMkUxMUUzOEE3NEI3Q0U1QUIzMTc4NiI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjM0M0UzMzlCQTIyRTExRTM4QTc0QjdDRTVBQjMxNzg2IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjM0M0UzMzlDQTIyRTExRTM4QTc0QjdDRTVBQjMxNzg2Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+jzOsUQAAANhJREFUeNqsks0KhCAUhW/Sz6pFSc1AD9HL+OBFbdsVOKWLajH9EE7GFBEjOMxcUNHD8dxPBCEE/DKyLGMqraoqcd4j0ChpUmlBEGCFRBzH2dbj5JycJAn90CEpy1J2SK4apVSM4yiKonhePYwxMU2TaJrm8BpykpWmKQ3D8FbX9SOO4/tOhDEG0zRhGAZo2xaiKDLyPGeSyPM8sCxr868+WC/mvu9j13XBtm1ACME8z7AsC/R9r0fGOf+arOu6jUwS7l6tT/B+xo+aDFRo5BykHfav3/gSYAAtIdQ1IT0puAAAAABJRU5ErkJggg=="
|
214
|
+
};
|
215
|
+
|
216
|
+
// $('img.photo',this).imagesLoaded(myFunction)
|
217
|
+
// execute a callback when all images have loaded.
|
218
|
+
// needed because .load() doesn't work on cached images
|
219
|
+
|
220
|
+
// mit license. paul irish. 2010.
|
221
|
+
// webkit fix from Oren Solomianik. thx!
|
222
|
+
|
223
|
+
// callback function is passed the last image to load
|
224
|
+
// as an argument, and the collection as `this`
|
225
|
+
|
226
|
+
$.fn.imagesLoaded = function(callback){
|
227
|
+
var elems = this.filter('img'),
|
228
|
+
len = elems.length,
|
229
|
+
blank = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
|
230
|
+
|
231
|
+
elems.bind('load.imgloaded',function(){
|
232
|
+
if (--len <= 0 && this.src !== blank) {
|
233
|
+
elems.unbind('load.imgloaded');
|
234
|
+
callback.call(elems,this);
|
235
|
+
}
|
236
|
+
}).each(function() {
|
237
|
+
// cached images don't fire load sometimes, so we reset src.
|
238
|
+
if (this.complete || this.complete === undefined){
|
239
|
+
var src = this.src;
|
240
|
+
// webkit hack from http://groups.google.com/group/jquery-dev/browse_thread/thread/eee6ab7b2da50e1f
|
241
|
+
// data uri bypasses webkit log warning (thx doug jones)
|
242
|
+
this.src = blank;
|
243
|
+
this.src = src;
|
244
|
+
}
|
245
|
+
});
|
246
|
+
|
247
|
+
return this;
|
248
|
+
};
|
@@ -0,0 +1,36 @@
|
|
1
|
+
function generateTrackingLink(){
|
2
|
+
var url = $('#url').val();
|
3
|
+
var sourceMedium = $('#source-medium').val();
|
4
|
+
var source = sourceMedium.split('-')[0];
|
5
|
+
var medium = sourceMedium.split('-')[1];
|
6
|
+
|
7
|
+
var id = $('#campaign').val();
|
8
|
+
var today = new Date();
|
9
|
+
var dd = today.getDate();
|
10
|
+
var mm = today.getMonth()+1; //January is 0!
|
11
|
+
var yyyy = today.getFullYear();
|
12
|
+
|
13
|
+
var campaign = dd + "_" + mm + "_" + yyyy + "_" + id
|
14
|
+
|
15
|
+
var queryString = ["utm_source=",source,"&utm_medium=",medium,"&utm_campaign=",campaign,"&bucket=",source,"-",medium,"-",campaign].join('');
|
16
|
+
|
17
|
+
if (url.indexOf('?') > -1) {
|
18
|
+
firstCharacter = '&';
|
19
|
+
} else {
|
20
|
+
firstCharacter = '?'
|
21
|
+
}
|
22
|
+
|
23
|
+
document.getElementById("long_url").value = (url + firstCharacter + queryString);
|
24
|
+
}
|
25
|
+
|
26
|
+
$(document).ready(function(){
|
27
|
+
$("#tracking :input").change(function() {
|
28
|
+
generateTrackingLink();
|
29
|
+
});
|
30
|
+
|
31
|
+
$("#tracking").keypress(function(){
|
32
|
+
generateTrackingLink();
|
33
|
+
});
|
34
|
+
|
35
|
+
document.getElementById("long_url").readOnly = true;
|
36
|
+
});
|
@@ -0,0 +1,15 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
10
|
+
* files in this directory. Styles in this file should be added after the last require_* statement.
|
11
|
+
* It is generally better to create a new file per style scope.
|
12
|
+
*
|
13
|
+
*= require_tree .
|
14
|
+
*= require_self
|
15
|
+
*/
|