├── index.js
├── public
├── add.html
├── form.html
├── add.scss
└── form.scss
├── .gitignore
├── src
├── styler.js
├── jst.js
└── main.js
├── .editorconfig
├── .jshintrc
├── bin
├── .hook_template
└── hook.js
├── bower.json
├── package.json
├── README.md
├── index.html
├── Gulpfile.js
└── dist
├── comments.min.js
└── comments.js
/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require("./src/main");
--------------------------------------------------------------------------------
/public/add.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | build
2 | node_modules
3 | dist/assets
4 | dist/clappr.js
5 | dist/clappr.min.js
--------------------------------------------------------------------------------
/src/styler.js:
--------------------------------------------------------------------------------
1 | var JST = require('./jst');
2 | var $ = require('clappr-zepto');
3 |
4 | var Styler = {
5 | getStyleFor: function(name) {
6 | return $('').html(JST.CSS[name].toString());
7 | }
8 | };
9 |
10 | module.exports = Styler;
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailing": true,
3 | "node": true,
4 | "quotmark": false,
5 | "asi": true,
6 | "expr": true,
7 | "esnext": true,
8 | "eqeqeq": true,
9 | "noempty": true,
10 | "unused": true,
11 | "unused": true,
12 | "trailing": true,
13 | "smarttabs": true,
14 | "white": true
15 | }
16 |
17 |
--------------------------------------------------------------------------------
/bin/.hook_template:
--------------------------------------------------------------------------------
1 | //This file is generated by bin/hook.js
2 | var template = require('lodash.template');
3 | module.exports = {
4 | <% templates.forEach(function(template) { %>
5 | '<%= template.name %>': template('<%= template.content %>'),
6 | <% }); %>
7 | CSS: {
8 | <% styles.forEach(function(style) { %>
9 | '<%= style.name %>': '<%= style.content %>',
10 | <% }); %>
11 | }
12 | };
13 |
--------------------------------------------------------------------------------
/public/form.html:
--------------------------------------------------------------------------------
1 |
4 |
17 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "clappr-comment-plugin",
3 | "main": "dist/comments.min.js",
4 | "version": "0.0.1",
5 | "homepage": "https://github.com/Metrakit/clappr-comment-plugin",
6 | "description": "A Clappr plugin for make your videos more social",
7 | "keywords": [
8 | "clappr",
9 | "comments",
10 | "plugin",
11 | "social"
12 | ],
13 | "authors": [
14 | "Metrakit (http://jordane.net)"
15 | ],
16 | "license": "MIT",
17 | "ignore": [
18 | "**/.*",
19 | "node_modules",
20 | "bower_components",
21 | "build",
22 | "dist/assets",
23 | "dist/clappr.js",
24 | "dist/clappr.min.js"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "comments",
3 | "version": "0.0.0",
4 | "dependencies": {
5 | "clappr": "latest",
6 | "clappr-zepto": "latest"
7 | },
8 | "browserify-shim": {
9 | "Clappr": "Clappr"
10 | },
11 | "devDependencies": {
12 | "browserify": "^6.2.0",
13 | "babelify": "5.0.3",
14 | "gulp": "^3.8.6",
15 | "gulp-minify-css": "^0.3.6",
16 | "lodash.template" : "3.5.1",
17 | "gulp-rename": "^1.2.0",
18 | "gulp-sass": "^0.7.2",
19 | "vinyl-source-stream": "^1.0.0",
20 | "yargs": "latest",
21 | "glob": "^4.0.4",
22 | "mkdirp": "^0.5.0",
23 | "express": "^4.6.1",
24 | "gulp-util": "latest",
25 | "gulp-uglify": "^1.0.1",
26 | "gulp-livereload": "^2.1.0",
27 | "gulp-streamify": "0.0.5"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/bin/hook.js:
--------------------------------------------------------------------------------
1 | // Copyright 2014 Globo.com Clappr authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | var glob = require('glob').sync;
6 | var mkdirp = require('mkdirp').sync;
7 | var path = require('path');
8 | var fs = require('fs');
9 | var template = require('lodash.template');
10 |
11 |
12 | var codeTemplate = template(fs.readFileSync('bin/.hook_template').toString());
13 |
14 | var jstFile = './src/jst.js';
15 |
16 | function format(filePath) {
17 | return fs.readFileSync(filePath).toString().replace(/\r?\n|\r/g, '');
18 | }
19 |
20 |
21 | function copyFiles(asset) {
22 | var targetDir = path.extname(asset) === '.js' ? 'dist/' : 'dist/assets';
23 | fs.createReadStream(asset)
24 | .pipe(fs.createWriteStream(path.join(targetDir, path.basename(asset))));
25 | }
26 |
27 |
28 |
29 | var html = [
30 | {name: 'add', content: glob('build/add.html').map(format)},
31 | {name: 'form', content: glob('build/form.html').map(format)},
32 | ];
33 |
34 | var css = [
35 | {name: 'add', content: glob('build/add.css').map(format)},
36 | {name: 'form', content: glob('build/form.css').map(format)},
37 | ];
38 |
39 |
40 | fs.writeFileSync(jstFile, codeTemplate({templates: html, styles: css}));
41 |
42 | mkdirp('dist/assets/');
43 |
44 | glob('./node_modules/clappr/dist/**/*.{png,jpeg,jpg,gif,swf,eot,ttf,svg}').map(copyFiles);
45 | glob('public/*.{png,jpeg,jpg,gif,swf,eot,ttf,svg}').map(copyFiles);
46 | glob('./node_modules/clappr/dist/*.js').map(copyFiles);
--------------------------------------------------------------------------------
/public/add.scss:
--------------------------------------------------------------------------------
1 | $live-color: #ff0101;
2 | $dvr-color: #fff;
3 | $vod-color: #005aff;
4 |
5 | $disabled-opacity: 0.3;
6 |
7 | $control-height: 32px;
8 | $circle-radius: 3.5px;
9 |
10 | .comments-controls[data-comments-controls] {
11 | display: inline-block;
12 | float: left;
13 | color: $dvr-color;
14 | line-height: 32px;
15 | font-size: 10px;
16 | font-weight: bold;
17 | margin-right: 3px;
18 |
19 | .add-comment {
20 | cursor: pointer;
21 | opacity: .8;
22 | font-weight:lighter;
23 | &:before {
24 | font-size: 16px;
25 | }
26 | &:hover {
27 | text-shadow: rgba(255,255,255,.8) 0 0 5px;
28 | opacity: 1;
29 | }
30 | }
31 |
32 | }
33 |
34 |
35 | .comments-bar {
36 | display: inline-block;
37 | float: left;
38 | line-height: 32px;
39 | font-size: 10px;
40 | font-weight: bold;
41 | margin-left: 6px;
42 | }
43 |
44 | .comment-pointer {
45 | position: absolute;
46 | left: 20px;
47 | top: 8px;
48 |
49 | width: 2px;
50 | height:8px;
51 | background: lightgreen;
52 |
53 | color: lightgreen;
54 | transition: background 0.2s linear;
55 |
56 | &:hover {
57 | background: red;
58 | }
59 | }
60 |
61 | .video-comment {
62 | color: white;
63 | text-align: left;
64 | font-size: 12px !important;
65 | }
66 |
67 | .comment-actif {
68 | padding: 5px !important;
69 | }
70 |
71 | .img-comment {
72 | img {
73 | max-width: 200px;
74 | max-height: 200px;
75 | }
76 | min-width: 100px;
77 | min-height: 50px;
78 | .spinner-three-bounce {
79 | top: 25%;
80 | &> div {
81 | width: 10px;
82 | height: 10px;
83 | }
84 | }
85 |
86 | }
87 |
88 |
89 | // Modification du core
90 |
91 | .media-control[data-media-control] .media-control-layer[data-controls] .bar-container[data-seekbar] .bar-scrubber[data-seekbar] {
92 | z-index: 99;
93 | }
94 |
95 | .seek-time[data-seek-time] {
96 | height: initial !important;
97 | }
--------------------------------------------------------------------------------
/public/form.scss:
--------------------------------------------------------------------------------
1 | .form-comment {
2 | .form-comment-header {
3 | padding: 2px 5px 2px 5px;
4 | background: #888888;
5 | border-top-left-radius: 5px;
6 | border-top-right-radius: 5px;
7 | }
8 |
9 | form {
10 | padding: 5px !important;
11 | }
12 |
13 | img {
14 | max-height: 50px;
15 | max-width: 100px;
16 | }
17 |
18 | // RAZ (Bootstrap, ...)
19 | button, input, textarea {
20 | font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
21 | font-size: initial;
22 | line-height: initial;
23 | color: initial;
24 | }
25 | p {
26 | color: initial;
27 | font-size: initial !important;
28 | }
29 |
30 | input[type="file"] {
31 | width: 0.1px;
32 | height: 0.1px;
33 | opacity: 0;
34 | overflow: hidden;
35 | position: absolute;
36 | z-index: -1;
37 | }
38 |
39 | input[type="file"] + label, button[type="button"] {
40 | display: inline-block;
41 | padding: 2px 5px 2px 5px;
42 | font-size: 1.25em;
43 | font-weight: 700;
44 | }
45 |
46 | input[type="file"] + label {
47 | color: white;
48 | background-color: black;
49 | cursor: pointer;
50 | }
51 |
52 | button[type="button"] {
53 | color: white;
54 | background-color: green;
55 | cursor: pointer;
56 | border: 0;
57 | line-height: 1.4;
58 | }
59 |
60 | input[type="file"]:focus + label,
61 | input[type="file"] + label:hover,
62 | button[type="button"]:focus,
63 | button[type="button"]:hover {
64 | background-color: red;
65 | }
66 |
67 | .text-center {
68 | text-align: center;
69 | }
70 |
71 | position: absolute;
72 | width: 50%;
73 | margin-left: auto;
74 | margin-right: auto;
75 | text-align: left;
76 | background: white;
77 | right:5px;
78 | bottom: 100px;
79 | z-index: 999999;
80 | visibility: hidden;
81 | opacity: 0;
82 | transition: hidden 0s 0.2s, opacity 0.2s linear;
83 |
84 | border-radius: 5px;
85 |
86 | // peut etre pas necessaire
87 | cursor: default;
88 |
89 | textarea {
90 | width: 100%;
91 | }
92 |
93 | .submit-comment {
94 | display: inline-block;
95 | //text-align:center;
96 | }
97 |
98 | }
99 |
100 | .show-form {
101 | visibility: visible;
102 | opacity: .9;
103 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Clappr Comment plugin
2 | ==================
3 |
4 | > A plugin for add comments and show comments on the seekbar of the Clappr HTML5 player
5 |
6 | ## Requirements
7 |
8 | Clappr player: https://github.com/clappr/clappr
9 |
10 | ## Install
11 |
12 | ### Bower
13 |
14 | ```
15 | $ bower install clappr-comment-plugin
16 | ```
17 |
18 | ### Git
19 |
20 | ```
21 | $ git clone https://github.com/Metrakit/clappr-comment-plugin
22 | ```
23 |
24 | ### CDN (jsDelivr)
25 |
26 | ```
27 | https://cdn.jsdelivr.net/clappr.comment/latest/comments.min.js
28 | ```
29 |
30 | ### Using the Plugin
31 |
32 | Add both Clappr and Comments plugin scripts to your HTML:
33 |
34 | ```html
35 |
36 |
37 |
38 |
39 |
40 |
41 | ```
42 | Add the comments Plugin in the Clappr configuration
43 | ```javascript
44 | var player = new Clappr.Player({
45 | ...
46 | plugins: {
47 | core: [Comments]
48 | },
49 | ...
50 | ```
51 |
52 | You can also add some options :
53 | ```javascript
54 | plugins: {
55 | core: [Comments]
56 | },
57 |
58 | // Comment options
59 | videoId: 1,
60 | urlGetComments: "http://localhost/comments-video",
61 | urlAddComments: "http://localhost/submit-comment",
62 | iconComment: "fa fa-comment-o",
63 | iconFont: "FontAwesome",
64 | pointerColor: "orange",
65 | enablePicture: true,
66 | texts: {
67 | addComment: 'Add a comment at',
68 | addCommentLink: "Comment",
69 | minutes: "minutes",
70 | commentPlaceholder: "Put a comment here",
71 | sendComment: "Send"
72 | }
73 | ```
74 |
75 | ### Options availables
76 |
77 | - videoId : (integer) Id of the video
78 | - urlGetComments : (string) the URL for get the comments
79 | - urlAddComments : (string) the URL for add the comments
80 | - iconComment : (string) the icon for add a comment
81 | - iconFont : (string) the font for the icons
82 | - pointerColor : the color of the cursors on the seekbar
83 | - enablePicture : (boolean) availability to add pictures in the comments
84 | - texts : multiple texts to translate
85 |
86 | ## Demo
87 |
88 | http://labs.jordane.net/clappr-comment
89 |
90 |
91 | ## Author
92 |
93 | - Metrakit (Jordane Jouffroy) : contact@jordane.net
94 |
95 | ## Credits
96 |
97 | The Clappr Team : https://github.com/clappr/clappr/graphs/contributors
98 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | Clappr comment plugin - Labs of Jordane
16 |
47 |
48 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
Clappr comment plugin
62 |
63 |
64 |
65 |
66 |
67 |
Options
68 |
69 |
70 | urlGetComments: "http://localhost/comments-video",
71 | urlAddComments: "http://localhost/submit-comment",
72 | iconComment: "fa fa-comment-o",
73 | iconFont: "FontAwesome",
74 | pointerColor: "blue",
75 | enablePicture: true
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/Gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp');
2 | var sass = require('gulp-sass');
3 | var minifyCSS = require('gulp-minify-css');
4 | var babelify = require('babelify');
5 | var rename = require('gulp-rename');
6 | var browserify = require('browserify');
7 | var source = require('vinyl-source-stream');
8 | var exec = require('child_process').exec;
9 | var args = require('yargs').argv;
10 | var express = require('express');
11 | var util = require('gulp-util');
12 | var livereload = require('gulp-livereload');
13 | var uglify = require('gulp-uglify')
14 | var streamify = require('gulp-streamify')
15 |
16 | var files = {
17 | css: 'public/*.css',
18 | scss: 'public/*.scss',
19 | html: 'public/*.html'
20 | };
21 |
22 | var watch_paths = {
23 | js: ['./*.js', './src/*.js'],
24 | assets: './public/*.{html,scss,css}'
25 | };
26 |
27 | gulp.task('pre-build', ['sass', 'copy-html', 'copy-css'], function(done) {
28 | return exec('node bin/hook.js', done);
29 | });
30 |
31 | gulp.task('build', ['pre-build'], function(b) {
32 | return browserify()
33 | .transform(babelify)
34 | .add('./index.js', {entry: true})
35 | .external('underscore')
36 | .external('zepto')
37 | .bundle()
38 | .pipe(source('main.js'))
39 | .pipe(rename( 'comments.js'))
40 | .pipe(gulp.dest('./dist'));
41 | });
42 |
43 | gulp.task('release', ['pre-build'], function() {
44 | return browserify()
45 | .transform(babelify)
46 | .add('./index.js', {entry: true})
47 | .external('underscore')
48 | .external('zepto')
49 | .bundle()
50 | .pipe(source('main.js'))
51 | .pipe(rename( 'comments.min.js'))
52 | .pipe(streamify(uglify()))
53 | .pipe(gulp.dest('./dist'));
54 | });
55 |
56 | gulp.task('sass', function () {
57 | return gulp.src(files.scss)
58 | .pipe(sass())
59 | .pipe(minifyCSS())
60 | .pipe(gulp.dest("build"));
61 | });
62 |
63 | gulp.task("copy-css", function() {
64 | return gulp.src(files.css)
65 | .pipe(minifyCSS())
66 | .pipe(gulp.dest('build'));
67 | });
68 |
69 | gulp.task("copy-html", function() {
70 | return gulp.src(files.html)
71 | .pipe(gulp.dest('build'));
72 | });
73 |
74 | gulp.task('serve', ['build', 'watch'], function() {
75 | express()
76 | .use(express.static('.'))
77 | .use(express.static('./dist'))
78 | .listen(3000);
79 | util.log(util.colors.bgGreen('Listening on port 3000'));
80 | });
81 |
82 |
83 | gulp.task('watch', function() {
84 | var reloadServer = livereload();
85 |
86 | var js = gulp.watch(watch_paths.js);
87 | js.on('change', function(event) {
88 | gulp.start('build', function() {
89 | reloadServer.changed(event.path);
90 | });
91 | });
92 |
93 | var assets = gulp.watch(watch_paths.assets);
94 | assets.on('change', function(event) {
95 | gulp.start(['build'], function() {
96 | reloadServer.changed(event.path);
97 | });
98 | });
99 | util.log(util.colors.bgGreen('Watching for changes...'));
100 | });
101 |
--------------------------------------------------------------------------------
/src/jst.js:
--------------------------------------------------------------------------------
1 | //This file is generated by bin/hook.js
2 | var template = require('lodash.template');
3 | module.exports = {
4 |
5 | 'add': template(''),
6 |
7 | 'form': template(''),
8 |
9 | CSS: {
10 |
11 | 'add': '.comments-controls[data-comments-controls]{display:inline-block;float:left;color:#fff;line-height:32px;font-size:10px;font-weight:700;margin-right:3px}.comments-controls[data-comments-controls] .add-comment{cursor:pointer;opacity:.8;font-weight:lighter}.comments-controls[data-comments-controls] .add-comment:before{font-size:16px}.comments-controls[data-comments-controls] .add-comment:hover{text-shadow:rgba(255,255,255,.8) 0 0 5px;opacity:1}.comments-bar{display:inline-block;float:left;line-height:32px;font-size:10px;font-weight:700;margin-left:6px}.comment-pointer{position:absolute;left:20px;top:8px;width:2px;height:8px;background:#90ee90;color:#90ee90;transition:background .2s linear}.comment-pointer:hover{background:red}.video-comment{color:#fff;text-align:left;font-size:12px!important}.comment-actif{padding:5px!important}.img-comment{min-width:100px;min-height:50px}.img-comment img{max-width:200px;max-height:200px}.img-comment .spinner-three-bounce{top:25%}.img-comment .spinner-three-bounce>div{width:10px;height:10px}.media-control[data-media-control] .media-control-layer[data-controls] .bar-container[data-seekbar] .bar-scrubber[data-seekbar]{z-index:99}.seek-time[data-seek-time]{height:initial!important}',
12 |
13 | 'form': '.form-comment{position:absolute;width:50%;margin-left:auto;margin-right:auto;text-align:left;background:#fff;right:5px;bottom:100px;z-index:999999;visibility:hidden;opacity:0;transition:hidden 0s .2s,opacity .2s linear;border-radius:5px;cursor:default}.form-comment .form-comment-header{padding:2px 5px;background:#888;border-top-left-radius:5px;border-top-right-radius:5px}.form-comment form{padding:5px!important}.form-comment img{max-height:50px;max-width:100px}.form-comment button,.form-comment input,.form-comment textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:initial;line-height:initial;color:initial}.form-comment p{color:initial;font-size:initial!important}.form-comment input[type=file]{width:.1px;height:.1px;opacity:0;overflow:hidden;position:absolute;z-index:-1}.form-comment button[type=button],.form-comment input[type=file]+label{display:inline-block;padding:2px 5px;font-size:1.25em;font-weight:700}.form-comment input[type=file]+label{color:#fff;background-color:#000;cursor:pointer}.form-comment button[type=button]{color:#fff;background-color:green;cursor:pointer;border:0;line-height:1.4}.form-comment button[type=button]:focus,.form-comment button[type=button]:hover,.form-comment input[type=file]+label:hover,.form-comment input[type=file]:focus+label{background-color:red}.form-comment .text-center{text-align:center}.form-comment textarea{width:100%}.form-comment .submit-comment{display:inline-block}.show-form{visibility:visible;opacity:.9}',
14 |
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | var UICorePlugin = Clappr.UICorePlugin;
2 | var JST = require('./jst');
3 | var Styler = require('./styler');
4 | var Events = Clappr.Events;
5 |
6 | class Comments extends UICorePlugin {
7 |
8 | get name() { return 'comments'; }
9 |
10 | get events() {
11 | return {
12 | 'click .add-comment': 'clickOnContainer',
13 | }
14 | }
15 |
16 | get attributes() {
17 | return {
18 | 'class': 'comments-controls',
19 | 'data-comments-controls': '',
20 | }
21 | }
22 |
23 | constructor(core) {
24 | super(core)
25 | this.core = core
26 | this.actualTime = 0
27 | }
28 |
29 |
30 | /**
31 | * Bind events
32 | */
33 | bindEvents() {
34 | this.listenTo(this.core.mediaControl, 'mediacontrol:rendered', this.make)
35 | this.listenTo(this.core.mediaControl.container, 'container:timeupdate', this.timeUpdate)
36 | this.listenTo(this.core.mediaControl.container, 'container:play', this.play)
37 | }
38 |
39 |
40 | /**
41 | * Render
42 | */
43 | render() {
44 | this.core.options.commentImg = this.core.options.commentImg != undefined ? this.core.options.commentImg : true;
45 | this.videoId = this.core.$el.parent().attr('data-video-id')
46 | this.make()
47 | }
48 |
49 |
50 | /**
51 | * Event on play
52 | */
53 | play() {
54 | this.dismissForm()
55 | }
56 |
57 |
58 | /**
59 | * Make the template and prepare the plugin
60 | */
61 | make() {
62 | // Create new DOM element add a button
63 | var styleAddBtn = Styler.getStyleFor('add');
64 | console.log(styleAddBtn[0])
65 |
66 | this.$playButton = this.core.mediaControl.$el.find('.media-control-button');
67 | this.$el.html(JST.add)
68 | .append(styleAddBtn[0]);
69 |
70 | this.core.mediaControl.$('.media-control-right-panel[data-media-control]').append(this.$el);
71 |
72 |
73 |
74 | // Create new DOM element for add the form
75 | var styleForm = Styler.getStyleFor('form');
76 |
77 | /**
78 | * Style options
79 | */
80 |
81 | var styleOptions = '";
95 |
96 | this.$el.formComment = document.createElement("div")
97 |
98 | if (this.core.options.texts) {
99 | var formText = {
100 | addAt: this.core.options.texts.addComment ? this.core.options.texts.addComment : "Add a comment at",
101 | minutes: this.core.options.texts.minutes ? this.core.options.texts.minutes : "minutes",
102 | placeholder: this.core.options.texts.commentPlaceholder ? this.core.options.texts.commentPlaceholder : "Put a comment here",
103 | send: this.core.options.texts.sendComment ? this.core.options.texts.sendComment : "Send"
104 | }
105 | } else {
106 | var formText = {
107 | addAt: "Add a comment at",
108 | minutes: "minutes",
109 | placeholder: "Put a comment here",
110 | send: "Send"
111 | }
112 |
113 | }
114 |
115 | $(this.$el.formComment).html(JST.form(formText))
116 | .addClass('form-comment')
117 | .append(styleForm[0])
118 | .append(styleOptions)
119 | this.core.mediaControl.container.$el.append(this.$el.formComment)
120 |
121 | this.core.mediaControl.container.$el.find('.form-comment').click(function(e) {
122 | e.stopPropagation();
123 | });
124 |
125 |
126 | /**
127 | * Options
128 | */
129 |
130 | // [OPTION] Icon for add a new comment or Text
131 | if (this.core.options.iconComment) {
132 | this.core.mediaControl.$el.find('.add-comment').addClass(this.core.options.iconComment);
133 | } else if (this.core.options.texts && this.core.options.texts.addCommentLink) {
134 | this.core.mediaControl.$el.find('.add-comment').text(this.core.options.texts.addCommentLink);
135 | } else {
136 | this.core.mediaControl.$el.find('.add-comment').text('Comment');
137 | }
138 |
139 | // [OPTION] Display input file if picture is enabled
140 | if (this.core.options.enablePicture) {
141 | this.core.mediaControl.container.$el.find('input[type="file"]').show();
142 | }
143 |
144 |
145 | // Generate comment (get the video Id in option)
146 | if (!isNaN(this.core.mediaControl.container.getDuration())) {
147 | this.getComments(this.core.options.videoId)
148 | } else {
149 | this.videoUnReady = true
150 | }
151 |
152 | this.core.mediaControl.container.$el.find('.submit-comment').click(() => this.submitComment(this));
153 |
154 | this.core.mediaControl.$seekBarContainer.append(this.commentPointer)
155 |
156 | this.core.mediaControl.seekTime.$el.prepend('')
157 |
158 | $('.form-comment input[type="file"]').change(function(){
159 | if (this.files && this.files[0]) {
160 | var reader = new FileReader();
161 | reader.onload = function (e) {
162 | $('.form-comment img').attr('src', e.target.result);
163 | }
164 | reader.readAsDataURL(this.files[0]);
165 | }
166 | });
167 |
168 | }
169 |
170 |
171 | /**
172 | * Get the comments with API (GET)
173 | * @param Int videoId
174 | */
175 | getComments(videoId) {
176 |
177 | if (!this.pointers) {
178 | this.pointers = new Array;
179 | // Alert if "urlGetComments" is missing
180 | if (!this.core.options.urlGetComments) {
181 | alert('An url is needed in the options for the API (POST). Option "urlGetComments"'); return;
182 | }
183 |
184 | $.get(this.core.options.urlGetComments + '/' + videoId, (function(data) {
185 |
186 | for(var i = 0; i < data.length; i++) {
187 | this.createCommentPointer(data[i])
188 | }
189 |
190 | this.displayingComment(this)
191 | }).bind(this))
192 |
193 | }
194 | }
195 |
196 |
197 | /**
198 | * Display comment when event with mouse
199 | * @param Object this
200 | */
201 | displayingComment(elem) {
202 | this.core.mediaControl.$seekBarContainer.find('.comment-pointer').on('mouseover', (function(e) {
203 | elem.showComment(elem, this)
204 | }));
205 | this.core.mediaControl.$seekBarContainer.find('.comment-pointer').on('mouseout', () => this.hideComment(this));
206 | }
207 |
208 |
209 | /**
210 | * Create a comment pointers
211 | * @param Json data[comment, time, imgUrl]
212 | */
213 | createCommentPointer(data) {
214 |
215 | this.pointers[data.time] = document.createElement("span")
216 | $(this.pointers[data.time]).addClass("comment-pointer")
217 | .attr('data-comment', data.comment)
218 |
219 | if(data.imgUrl) {
220 | $(this.pointers[data.time]).attr('data-imgUrl', data.imgUrl)
221 | }
222 |
223 | this.timePercent = (data.time / this.core.mediaControl.container.getDuration()) * 100
224 | $(this.pointers[data.time]).css('left', this.timePercent + '%');
225 |
226 | if (!isNaN(this.timePercent)) {
227 | this.core.mediaControl.$seekBarContainer.append(this.pointers[data.time])
228 | }
229 |
230 | }
231 |
232 |
233 | /**
234 | * Show a comment when hover a pointer
235 | * @param this
236 | * @param DOM pointer
237 | */
238 | showComment(elem, pointer) {
239 | elem.core.mediaControl.seekTime.$('.video-comment')
240 | .html($(pointer).attr('data-comment'))
241 | .addClass('comment-actif');
242 |
243 | if (this.core.options.videoId && $(pointer).attr('data-imgUrl')) {
244 | elem.core.mediaControl.seekTime.$('.video-comment').prepend('')
245 | var img = $("
").attr('src', $(pointer).attr('data-imgUrl'));
246 | img.on('load', function(){
247 | console.log("test")
248 | if (!this.complete || typeof this.naturalWidth == "undefined" || this.naturalWidth == 0) {
249 | // wrong image
250 | } else {
251 | console.log(this)
252 | elem.core.mediaControl.seekTime.$('.img-comment').html(this)
253 | }
254 | });
255 | }
256 | }
257 |
258 |
259 | /**
260 | * Hide a comment
261 | * @param this
262 | */
263 | hideComment(elem) {
264 | elem.core.mediaControl.seekTime.$('.video-comment').html('')
265 | .removeClass('comment-actif')
266 | }
267 |
268 |
269 | /**
270 | * Send a new comment to API (POST)
271 | * Data:
272 | * comment (string)
273 | * picture (file, optionnal)
274 | * time (int)
275 | * @param Object this
276 | */
277 | submitComment(elem) {
278 |
279 | // Alert if "urlAddComments" is missing
280 | if (!this.core.options.urlAddComments) {
281 | alert('An url is needed in the options for the API (POST). Option "urlAddComments"'); return;
282 | }
283 |
284 | var form = elem.core.mediaControl.container.$el.find('form')
285 | var fd = new FormData();
286 |
287 | // [OPTION] Add input file if enabled
288 | if (this.core.options.enablePicture) {
289 |
290 | var picture = $('input[type="file"]')[1].files;
291 |
292 | if (picture.length == 1) {
293 | fd.append('picture', picture[0])
294 | }
295 |
296 | }
297 |
298 | // All inputs
299 | var inputs = $(form).serializeArray();
300 |
301 | $.each(inputs, function(key, input) {
302 | fd.append(input.name, input.value);
303 | })
304 | fd.append('time', Math.round(elem.actualTime));
305 |
306 | // Ajax request for send the comment form
307 | $.ajax({
308 | url: this.core.options.urlAddComments,
309 | type: 'POST',
310 | data: fd,
311 | async: false,
312 | success: function(data){
313 | elem.createCommentPointer(data)
314 | elem.displayingComment(elem)
315 | elem.dismissForm()
316 | },
317 | cache: false,
318 | contentType: false,
319 | processData: false
320 | })
321 | }
322 |
323 |
324 | /**
325 | * Dismiss the form
326 | */
327 | dismissForm() {
328 | if ($(this.$el.formComment).css('visibility') == "visible") {
329 | $(this.$el.formComment).removeClass('show-form')
330 | }
331 | }
332 |
333 |
334 | /**
335 | * Event when click on container
336 | */
337 | clickOnContainer() {
338 |
339 | if ($(this.$el.formComment).css('visibility') == "visible") {
340 | $(this.$el.formComment).removeClass('show-form')
341 | } else {
342 | this.core.mediaControl.container.pause()
343 | this.$playButton.addClass('paused')
344 | var actualTime = Math.round(this.actualTime)/100
345 | $(this.$el.formComment).find('.comment-time').text(actualTime)
346 | $(this.$el.formComment).addClass('show-form')
347 | }
348 |
349 | }
350 |
351 |
352 | /**
353 | * Event when time change
354 | * @param Int position
355 | * @param Int duration
356 | */
357 | timeUpdate(position, duration) {
358 | this.actualTime = position;
359 |
360 | if ($(this.$el.formComment).css('visibility') == "visible") {
361 | $(this.$el.formComment).find('.comment-time').text(Math.round(this.actualTime)/100)
362 | }
363 |
364 | if (this.videoUnReady && this.videoUnReady == true) {
365 | this.getComments(this.core.options.videoId)
366 | this.videoUnReady == false
367 | }
368 | }
369 |
370 |
371 | }
372 |
373 |
374 | module.exports = window.Comments = Comments;
--------------------------------------------------------------------------------
/dist/comments.min.js:
--------------------------------------------------------------------------------
1 | !function t(e,n,r){function o(a,s){if(!n[a]){if(!e[a]){var c="function"==typeof require&&require;if(!s&&c)return c(a,!0);if(i)return i(a,!0);var u=new Error("Cannot find module '"+a+"'");throw u.code="MODULE_NOT_FOUND",u}var l=n[a]={exports:{}};e[a][0].call(l.exports,function(t){var n=e[a][1][t];return o(n?n:t)},l,l.exports,t,e,n,r)}return n[a].exports}for(var i="function"==typeof require&&require,a=0;a0?$.fn.concat.apply([],t):t}function u(t){return t.replace(/::/g,"/").replace(/([A-Z]+)([A-Z][a-z])/g,"$1_$2").replace(/([a-z\d])([A-Z])/g,"$1_$2").replace(/_/g,"-").toLowerCase()}function l(t){return t in F?F[t]:F[t]=new RegExp("(^|\\s)"+t+"(\\s|$)")}function f(t,e){return"number"!=typeof e||D[u(t)]?e:e+"px"}function p(t){var e,n;return N[t]||(e=A.createElement(t),A.body.appendChild(e),n=getComputedStyle(e,"").getPropertyValue("display"),e.parentNode.removeChild(e),"none"==n&&(n="block"),N[t]=n),N[t]}function h(t){return"children"in t?k.call(t.children):$.map(t.childNodes,function(t){return 1==t.nodeType?t:void 0})}function m(t,e){var n,r=t?t.length:0;for(n=0;r>n;n++)this[n]=t[n];this.length=r,this.selector=e||""}function d(t,e,n){for(j in e)n&&(i(e[j])||Q(e[j]))?(i(e[j])&&!i(t[j])&&(t[j]={}),Q(e[j])&&!Q(t[j])&&(t[j]=[]),d(t[j],e[j],n)):e[j]!==C&&(t[j]=e[j])}function g(t,e){return null==e?$(t):$(t).filter(e)}function v(t,n,r,o){return e(n)?n.call(t,r,o):n}function y(t,e,n){null==n?t.removeAttribute(e):t.setAttribute(e,n)}function b(t,e){var n=t.className||"",r=n&&n.baseVal!==C;return e===C?r?n.baseVal:n:void(r?n.baseVal=e:t.className=e)}function x(t){try{return t?"true"==t||("false"==t?!1:"null"==t?null:+t+""==t?+t:/^[\[\{]/.test(t)?$.parseJSON(t):t):t}catch(e){return t}}function w(t,e){e(t);for(var n=0,r=t.childNodes.length;r>n;n++)w(t.childNodes[n],e)}var C,j,$,E,T,S,O=[],P=O.concat,_=O.filter,k=O.slice,A=window.document,N={},F={},D={"column-count":1,columns:1,"font-weight":1,"line-height":1,opacity:1,"z-index":1,zoom:1},M=/^\s*<(\w+|!)[^>]*>/,R=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,L=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,U=/^(?:body|html)$/i,z=/([A-Z])/g,q=["val","css","html","text","data","width","height","offset"],I=["after","prepend","before","append"],B=A.createElement("table"),W=A.createElement("tr"),Z={tr:A.createElement("tbody"),tbody:B,thead:B,tfoot:B,td:W,th:W,"*":A.createElement("div")},H=/complete|loaded|interactive/,X=/^[\w-]*$/,V={},G=V.toString,Y={},J=A.createElement("div"),K={tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},Q=Array.isArray||function(t){return t instanceof Array};return Y.matches=function(t,e){if(!e||!t||1!==t.nodeType)return!1;var n=t.webkitMatchesSelector||t.mozMatchesSelector||t.oMatchesSelector||t.matchesSelector;if(n)return n.call(t,e);var r,o=t.parentNode,i=!o;return i&&(o=J).appendChild(t),r=~Y.qsa(o,e).indexOf(t),i&&J.removeChild(t),r},T=function(t){return t.replace(/-+(.)?/g,function(t,e){return e?e.toUpperCase():""})},S=function(t){return _.call(t,function(e,n){return t.indexOf(e)==n})},Y.fragment=function(t,e,n){var r,o,a;return R.test(t)&&(r=$(A.createElement(RegExp.$1))),r||(t.replace&&(t=t.replace(L,"<$1>$2>")),e===C&&(e=M.test(t)&&RegExp.$1),e in Z||(e="*"),a=Z[e],a.innerHTML=""+t,r=$.each(k.call(a.childNodes),function(){a.removeChild(this)})),i(n)&&(o=$(r),$.each(n,function(t,e){q.indexOf(t)>-1?o[t](e):o.attr(t,e)})),r},Y.Z=function(t,e){return new m(t,e)},Y.isZ=function(t){return t instanceof Y.Z},Y.init=function(t,n){var r;if(!t)return Y.Z();if("string"==typeof t)if(t=t.trim(),"<"==t[0]&&M.test(t))r=Y.fragment(t,RegExp.$1,n),t=null;else{if(n!==C)return $(n).find(t);r=Y.qsa(A,t)}else{if(e(t))return $(A).ready(t);if(Y.isZ(t))return t;if(Q(t))r=s(t);else if(o(t))r=[t],t=null;else if(M.test(t))r=Y.fragment(t.trim(),RegExp.$1,n),t=null;else{if(n!==C)return $(n).find(t);r=Y.qsa(A,t)}}return Y.Z(r,t)},$=function(t,e){return Y.init(t,e)},$.extend=function(t){var e,n=k.call(arguments,1);return"boolean"==typeof t&&(e=t,t=n.shift()),n.forEach(function(n){d(t,n,e)}),t},Y.qsa=function(t,e){var n,r="#"==e[0],o=!r&&"."==e[0],i=r||o?e.slice(1):e,a=X.test(i);return t.getElementById&&a&&r?(n=t.getElementById(i))?[n]:[]:1!==t.nodeType&&9!==t.nodeType&&11!==t.nodeType?[]:k.call(a&&!r&&t.getElementsByClassName?o?t.getElementsByClassName(i):t.getElementsByTagName(e):t.querySelectorAll(e))},$.contains=A.documentElement.contains?function(t,e){return t!==e&&t.contains(e)}:function(t,e){for(;e&&(e=e.parentNode);)if(e===t)return!0;return!1},$.type=t,$.isFunction=e,$.isWindow=n,$.isArray=Q,$.isPlainObject=i,$.isEmptyObject=function(t){var e;for(e in t)return!1;return!0},$.inArray=function(t,e,n){return O.indexOf.call(e,t,n)},$.camelCase=T,$.trim=function(t){return null==t?"":String.prototype.trim.call(t)},$.uuid=0,$.support={},$.expr={},$.noop=function(){},$.map=function(t,e){var n,r,o,i=[];if(a(t))for(r=0;r=0?t:t+this.length]},toArray:function(){return this.get()},size:function(){return this.length},remove:function(){return this.each(function(){null!=this.parentNode&&this.parentNode.removeChild(this)})},each:function(t){return O.every.call(this,function(e,n){return t.call(e,n,e)!==!1}),this},filter:function(t){return e(t)?this.not(this.not(t)):$(_.call(this,function(e){return Y.matches(e,t)}))},add:function(t,e){return $(S(this.concat($(t,e))))},is:function(t){return this.length>0&&Y.matches(this[0],t)},not:function(t){var n=[];if(e(t)&&t.call!==C)this.each(function(e){t.call(this,e)||n.push(this)});else{var r="string"==typeof t?this.filter(t):a(t)&&e(t.item)?k.call(t):$(t);this.forEach(function(t){r.indexOf(t)<0&&n.push(t)})}return $(n)},has:function(t){return this.filter(function(){return o(t)?$.contains(this,t):$(this).find(t).size()})},eq:function(t){return-1===t?this.slice(t):this.slice(t,+t+1)},first:function(){var t=this[0];return t&&!o(t)?t:$(t)},last:function(){var t=this[this.length-1];return t&&!o(t)?t:$(t)},find:function(t){var e,n=this;return e=t?"object"==typeof t?$(t).filter(function(){var t=this;return O.some.call(n,function(e){return $.contains(e,t)})}):1==this.length?$(Y.qsa(this[0],t)):this.map(function(){return Y.qsa(this,t)}):$()},closest:function(t,e){var n=this[0],o=!1;for("object"==typeof t&&(o=$(t));n&&!(o?o.indexOf(n)>=0:Y.matches(n,t));)n=n!==e&&!r(n)&&n.parentNode;return $(n)},parents:function(t){for(var e=[],n=this;n.length>0;)n=$.map(n,function(t){return(t=t.parentNode)&&!r(t)&&e.indexOf(t)<0?(e.push(t),t):void 0});return g(e,t)},parent:function(t){return g(S(this.pluck("parentNode")),t)},children:function(t){return g(this.map(function(){return h(this)}),t)},contents:function(){return this.map(function(){return this.contentDocument||k.call(this.childNodes)})},siblings:function(t){return g(this.map(function(t,e){return _.call(h(e.parentNode),function(t){return t!==e})}),t)},empty:function(){return this.each(function(){this.innerHTML=""})},pluck:function(t){return $.map(this,function(e){return e[t]})},show:function(){return this.each(function(){"none"==this.style.display&&(this.style.display=""),"none"==getComputedStyle(this,"").getPropertyValue("display")&&(this.style.display=p(this.nodeName))})},replaceWith:function(t){return this.before(t).remove()},wrap:function(t){var n=e(t);if(this[0]&&!n)var r=$(t).get(0),o=r.parentNode||this.length>1;return this.each(function(e){$(this).wrapAll(n?t.call(this,e):o?r.cloneNode(!0):r)})},wrapAll:function(t){if(this[0]){$(this[0]).before(t=$(t));for(var e;(e=t.children()).length;)t=e.first();$(t).append(this)}return this},wrapInner:function(t){var n=e(t);return this.each(function(e){var r=$(this),o=r.contents(),i=n?t.call(this,e):t;o.length?o.wrapAll(i):r.append(i)})},unwrap:function(){return this.parent().each(function(){$(this).replaceWith($(this).children())}),this},clone:function(){return this.map(function(){return this.cloneNode(!0)})},hide:function(){return this.css("display","none")},toggle:function(t){return this.each(function(){var e=$(this);(t===C?"none"==e.css("display"):t)?e.show():e.hide()})},prev:function(t){return $(this.pluck("previousElementSibling")).filter(t||"*")},next:function(t){return $(this.pluck("nextElementSibling")).filter(t||"*")},html:function(t){return 0 in arguments?this.each(function(e){var n=this.innerHTML;$(this).empty().append(v(this,t,e,n))}):0 in this?this[0].innerHTML:null},text:function(t){return 0 in arguments?this.each(function(e){var n=v(this,t,e,this.textContent);this.textContent=null==n?"":""+n}):0 in this?this[0].textContent:null},attr:function(t,e){var n;return"string"!=typeof t||1 in arguments?this.each(function(n){if(1===this.nodeType)if(o(t))for(j in t)y(this,j,t[j]);else y(this,t,v(this,e,n,this.getAttribute(t)))}):this.length&&1===this[0].nodeType?!(n=this[0].getAttribute(t))&&t in this[0]?this[0][t]:n:C},removeAttr:function(t){return this.each(function(){1===this.nodeType&&t.split(" ").forEach(function(t){y(this,t)},this)})},prop:function(t,e){return t=K[t]||t,1 in arguments?this.each(function(n){this[t]=v(this,e,n,this[t])}):this[0]&&this[0][t]},data:function(t,e){var n="data-"+t.replace(z,"-$1").toLowerCase(),r=1 in arguments?this.attr(n,e):this.attr(n);return null!==r?x(r):C},val:function(t){return 0 in arguments?this.each(function(e){this.value=v(this,t,e,this.value)}):this[0]&&(this[0].multiple?$(this[0]).find("option").filter(function(){return this.selected}).pluck("value"):this[0].value)},offset:function(t){if(t)return this.each(function(e){var n=$(this),r=v(this,t,e,n.offset()),o=n.offsetParent().offset(),i={top:r.top-o.top,left:r.left-o.left};"static"==n.css("position")&&(i.position="relative"),n.css(i)});if(!this.length)return null;if(!$.contains(A.documentElement,this[0]))return{top:0,left:0};var e=this[0].getBoundingClientRect();return{left:e.left+window.pageXOffset,top:e.top+window.pageYOffset,width:Math.round(e.width),height:Math.round(e.height)}},css:function(e,n){if(arguments.length<2){var r,o=this[0];if(!o)return;if(r=getComputedStyle(o,""),"string"==typeof e)return o.style[T(e)]||r.getPropertyValue(e);if(Q(e)){var i={};return $.each(e,function(t,e){i[e]=o.style[T(e)]||r.getPropertyValue(e)}),i}}var a="";if("string"==t(e))n||0===n?a=u(e)+":"+f(e,n):this.each(function(){this.style.removeProperty(u(e))});else for(j in e)e[j]||0===e[j]?a+=u(j)+":"+f(j,e[j])+";":this.each(function(){this.style.removeProperty(u(j))});return this.each(function(){this.style.cssText+=";"+a})},index:function(t){return t?this.indexOf($(t)[0]):this.parent().children().indexOf(this[0])},hasClass:function(t){return t?O.some.call(this,function(t){return this.test(b(t))},l(t)):!1},addClass:function(t){return t?this.each(function(e){if("className"in this){E=[];var n=b(this),r=v(this,t,e,n);r.split(/\s+/g).forEach(function(t){$(this).hasClass(t)||E.push(t)},this),E.length&&b(this,n+(n?" ":"")+E.join(" "))}}):this},removeClass:function(t){return this.each(function(e){if("className"in this){if(t===C)return b(this,"");E=b(this),v(this,t,e,E).split(/\s+/g).forEach(function(t){E=E.replace(l(t)," ")}),b(this,E.trim())}})},toggleClass:function(t,e){return t?this.each(function(n){var r=$(this),o=v(this,t,n,b(this));o.split(/\s+/g).forEach(function(t){(e===C?!r.hasClass(t):e)?r.addClass(t):r.removeClass(t)})}):this},scrollTop:function(t){if(this.length){var e="scrollTop"in this[0];return t===C?e?this[0].scrollTop:this[0].pageYOffset:this.each(e?function(){this.scrollTop=t}:function(){this.scrollTo(this.scrollX,t)})}},scrollLeft:function(t){if(this.length){var e="scrollLeft"in this[0];return t===C?e?this[0].scrollLeft:this[0].pageXOffset:this.each(e?function(){this.scrollLeft=t}:function(){this.scrollTo(t,this.scrollY)})}},position:function(){if(this.length){var t=this[0],e=this.offsetParent(),n=this.offset(),r=U.test(e[0].nodeName)?{top:0,left:0}:e.offset();return n.top-=parseFloat($(t).css("margin-top"))||0,n.left-=parseFloat($(t).css("margin-left"))||0,r.top+=parseFloat($(e[0]).css("border-top-width"))||0,r.left+=parseFloat($(e[0]).css("border-left-width"))||0,{top:n.top-r.top,left:n.left-r.left}}},offsetParent:function(){return this.map(function(){for(var t=this.offsetParent||A.body;t&&!U.test(t.nodeName)&&"static"==$(t).css("position");)t=t.offsetParent;return t})}},$.fn.detach=$.fn.remove,["width","height"].forEach(function(t){var e=t.replace(/./,function(t){return t[0].toUpperCase()});$.fn[t]=function(o){var i,a=this[0];return o===C?n(a)?a["inner"+e]:r(a)?a.documentElement["scroll"+e]:(i=this.offset())&&i[t]:this.each(function(e){a=$(this),a.css(t,v(this,o,e,a[t]()))})}}),I.forEach(function(e,n){var r=n%2;$.fn[e]=function(){var e,o,i=$.map(arguments,function(n){return e=t(n),"object"==e||"array"==e||null==n?n:Y.fragment(n)}),a=this.length>1;return i.length<1?this:this.each(function(t,e){o=r?e:e.parentNode,e=0==n?e.nextSibling:1==n?e.firstChild:2==n?e:null;var s=$.contains(A.documentElement,o);i.forEach(function(t){if(a)t=t.cloneNode(!0);else if(!o)return $(t).remove();o.insertBefore(t,e),s&&w(t,function(t){null==t.nodeName||"SCRIPT"!==t.nodeName.toUpperCase()||t.type&&"text/javascript"!==t.type||t.src||window.eval.call(window,t.innerHTML)})})})},$.fn[r?e+"To":"insert"+(n?"Before":"After")]=function(t){return $(t)[e](this),this}}),Y.Z.prototype=m.prototype=$.fn,Y.uniq=S,Y.deserializeValue=x,$.zepto=Y,$}();window.Zepto=r,void 0===window.$&&(window.$=r),function(t){function e(t){return t._zid||(t._zid=p++)}function n(t,n,i,a){if(n=r(n),n.ns)var s=o(n.ns);return(g[e(t)]||[]).filter(function(t){return!(!t||n.e&&t.e!=n.e||n.ns&&!s.test(t.ns)||i&&e(t.fn)!==e(i)||a&&t.sel!=a)})}function r(t){var e=(""+t).split(".");return{e:e[0],ns:e.slice(1).sort().join(" ")}}function o(t){return new RegExp("(?:^| )"+t.replace(" "," .* ?")+"(?: |$)")}function i(t,e){return t.del&&!y&&t.e in b||!!e}function a(t){return x[t]||y&&b[t]||t}function s(n,o,s,c,l,p,h){var m=e(n),d=g[m]||(g[m]=[]);o.split(/\s/).forEach(function(e){if("ready"==e)return t(document).ready(s);var o=r(e);o.fn=s,o.sel=l,o.e in x&&(s=function(e){var n=e.relatedTarget;return!n||n!==this&&!t.contains(this,n)?o.fn.apply(this,arguments):void 0}),o.del=p;var m=p||s;o.proxy=function(t){if(t=u(t),!t.isImmediatePropagationStopped()){t.data=c;var e=m.apply(n,t._args==f?[t]:[t].concat(t._args));return e===!1&&(t.preventDefault(),t.stopPropagation()),e}},o.i=d.length,d.push(o),"addEventListener"in n&&n.addEventListener(a(o.e),o.proxy,i(o,h))})}function c(t,r,o,s,c){var u=e(t);(r||"").split(/\s/).forEach(function(e){n(t,e,o,s).forEach(function(e){delete g[u][e.i],"removeEventListener"in t&&t.removeEventListener(a(e.e),e.proxy,i(e,c))})})}function u(e,n){return(n||!e.isDefaultPrevented)&&(n||(n=e),t.each($,function(t,r){var o=n[t];e[t]=function(){return this[r]=w,o&&o.apply(n,arguments)},e[r]=C}),(n.defaultPrevented!==f?n.defaultPrevented:"returnValue"in n?n.returnValue===!1:n.getPreventDefault&&n.getPreventDefault())&&(e.isDefaultPrevented=w)),e}function l(t){var e,n={originalEvent:t};for(e in t)j.test(e)||t[e]===f||(n[e]=t[e]);return u(n,t)}var f,p=1,h=Array.prototype.slice,m=t.isFunction,d=function(t){return"string"==typeof t},g={},v={},y="onfocusin"in window,b={focus:"focusin",blur:"focusout"},x={mouseenter:"mouseover",mouseleave:"mouseout"};v.click=v.mousedown=v.mouseup=v.mousemove="MouseEvents",t.event={add:s,remove:c},t.proxy=function(n,r){var o=2 in arguments&&h.call(arguments,2);if(m(n)){var i=function(){return n.apply(r,o?o.concat(h.call(arguments)):arguments)};return i._zid=e(n),i}if(d(r))return o?(o.unshift(n[r],n),t.proxy.apply(null,o)):t.proxy(n[r],n);throw new TypeError("expected function")},t.fn.bind=function(t,e,n){return this.on(t,e,n)},t.fn.unbind=function(t,e){return this.off(t,e)},t.fn.one=function(t,e,n,r){return this.on(t,e,n,r,1)};var w=function(){return!0},C=function(){return!1},j=/^([A-Z]|returnValue$|layer[XY]$)/,$={preventDefault:"isDefaultPrevented",stopImmediatePropagation:"isImmediatePropagationStopped",stopPropagation:"isPropagationStopped"};t.fn.delegate=function(t,e,n){return this.on(e,t,n)},t.fn.undelegate=function(t,e,n){return this.off(e,t,n)},t.fn.live=function(e,n){return t(document.body).delegate(this.selector,e,n),this},t.fn.die=function(e,n){return t(document.body).undelegate(this.selector,e,n),this},t.fn.on=function(e,n,r,o,i){var a,u,p=this;return e&&!d(e)?(t.each(e,function(t,e){p.on(t,n,r,e,i)}),p):(d(n)||m(o)||o===!1||(o=r,r=n,n=f),(o===f||r===!1)&&(o=r,r=f),o===!1&&(o=C),p.each(function(f,p){i&&(a=function(t){return c(p,t.type,o),o.apply(this,arguments)}),n&&(u=function(e){var r,i=t(e.target).closest(n,p).get(0);return i&&i!==p?(r=t.extend(l(e),{currentTarget:i,liveFired:p}),(a||o).apply(i,[r].concat(h.call(arguments,1)))):void 0}),s(p,e,o,r,n,u||a)}))},t.fn.off=function(e,n,r){var o=this;return e&&!d(e)?(t.each(e,function(t,e){o.off(t,n,e)}),o):(d(n)||m(r)||r===!1||(r=n,n=f),r===!1&&(r=C),o.each(function(){c(this,e,r,n)}))},t.fn.trigger=function(e,n){return e=d(e)||t.isPlainObject(e)?t.Event(e):u(e),e._args=n,this.each(function(){e.type in b&&"function"==typeof this[e.type]?this[e.type]():"dispatchEvent"in this?this.dispatchEvent(e):t(this).triggerHandler(e,n)})},t.fn.triggerHandler=function(e,r){var o,i;return this.each(function(a,s){o=l(d(e)?t.Event(e):e),o._args=r,o.target=s,t.each(n(s,e.type||e),function(t,e){return i=e.proxy(o),o.isImmediatePropagationStopped()?!1:void 0})}),i},"focusin focusout focus blur load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select keydown keypress keyup error".split(" ").forEach(function(e){t.fn[e]=function(t){return 0 in arguments?this.bind(e,t):this.trigger(e)}}),t.Event=function(t,e){d(t)||(e=t,t=e.type);var n=document.createEvent(v[t]||"Events"),r=!0;if(e)for(var o in e)"bubbles"==o?r=!!e[o]:n[o]=e[o];return n.initEvent(t,r,!0),u(n)}}(r),function(t){function e(e,n,r){var o=t.Event(n);return t(e).trigger(o,r),!o.isDefaultPrevented()}function n(t,n,r,o){return t.global?e(n||y,r,o):void 0}function r(e){e.global&&0===t.active++&&n(e,null,"ajaxStart")}function o(e){e.global&&!--t.active&&n(e,null,"ajaxStop")}function i(t,e){var r=e.context;return e.beforeSend.call(r,t,e)===!1||n(e,r,"ajaxBeforeSend",[t,e])===!1?!1:void n(e,r,"ajaxSend",[t,e])}function a(t,e,r,o){var i=r.context,a="success";r.success.call(i,t,a,e),o&&o.resolveWith(i,[t,a,e]),n(r,i,"ajaxSuccess",[e,r,t]),c(a,e,r)}function s(t,e,r,o,i){var a=o.context;o.error.call(a,r,e,t),i&&i.rejectWith(a,[r,e,t]),n(o,a,"ajaxError",[r,o,t||e]),c(e,r,o)}function c(t,e,r){var i=r.context;r.complete.call(i,e,t),n(r,i,"ajaxComplete",[e,r]),o(r)}function u(){}function l(t){return t&&(t=t.split(";",2)[0]),t&&(t==j?"html":t==C?"json":x.test(t)?"script":w.test(t)&&"xml")||"text"}function f(t,e){return""==e?t:(t+"&"+e).replace(/[&?]{1,2}/,"?")}function p(e){e.processData&&e.data&&"string"!=t.type(e.data)&&(e.data=t.param(e.data,e.traditional)),!e.data||e.type&&"GET"!=e.type.toUpperCase()||(e.url=f(e.url,e.data),e.data=void 0)}function h(e,n,r,o){return t.isFunction(n)&&(o=r,r=n,n=void 0),t.isFunction(r)||(o=r,r=void 0),{url:e,data:n,success:r,dataType:o}}function m(e,n,r,o){var i,a=t.isArray(n),s=t.isPlainObject(n);t.each(n,function(n,c){i=t.type(c),o&&(n=r?o:o+"["+(s||"object"==i||"array"==i?n:"")+"]"),!o&&a?e.add(c.name,c.value):"array"==i||!r&&"object"==i?m(e,c,r,n):e.add(n,c)})}var d,g,v=0,y=window.document,b=/