├── .editorconfig
├── .github
├── issue_template.md
└── pull_request_template.md
├── .gitignore
├── .jshintrc
├── .npmignore
├── .travis.yml
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── docs
├── configuration-options.md
├── network-hooks.md
└── styles.md
├── examples
└── animation.html
├── gulpfile.js
├── index.html
├── package.json
├── pioneer.json
├── src
├── share-button.js
├── share-button.styl
├── share-utils.js
├── string-utils.js
└── svg
│ ├── email.svg
│ ├── export.svg
│ ├── facebook.svg
│ ├── googlePlus.svg
│ ├── linkedin.svg
│ ├── pinterest.svg
│ ├── reddit.svg
│ ├── twitter.svg
│ └── whatsapp.svg
└── tests
├── features
├── animation.feature
├── basic.feature
├── email.feature
├── facebook.feature
├── googleplus.feature
├── linkedin.feature
├── meta.feature
├── ordering.feature
├── pinterest.feature
├── reddit.feature
├── twitter.feature
├── ui.feature
└── whatsapp.feature
├── fixtures
├── animation.html
├── basic.html
├── email.html
├── facebook.html
├── facebook_no_sdk.html
├── googleplus.html
├── linkedin.html
├── meta.html
├── ordering.html
├── pinterest.html
├── reddit.html
├── twitter.html
├── ui.html
└── whatsapp.html
├── helpers
└── helpers.coffee
├── steps
├── animation.coffee
├── basic.coffee
├── email.coffee
├── facebook.coffee
├── googleplus.coffee
├── linkedin.coffee
├── meta.coffee
├── ordering.coffee
├── pinterest.coffee
├── reddit.coffee
├── twitter.coffee
├── ui.coffee
└── whatsapp.coffee
└── widgets
└── shareButton.coffee
/.editorconfig:
--------------------------------------------------------------------------------
1 | # This file is for unifying the coding style for different editors and IDEs
2 | # editorconfig.org
3 |
4 | root = true
5 |
6 | [*]
7 | end_of_line = lf
8 | insert_final_newline = true
9 | indent_style = space
10 | indent_size = 2
11 |
--------------------------------------------------------------------------------
/.github/issue_template.md:
--------------------------------------------------------------------------------
1 | ## Details
2 |
3 | ### Description
4 |
5 | ### Affected Tech
6 |
7 | #### OS(s)
8 |
9 | -
10 |
11 | #### Browser(s)
12 |
13 | -
14 |
15 | ### Steps to Reproduce
16 |
17 | [jsfiddle Example](http://jsfiddle.net/28fn23bj/)
18 |
19 | 1.
20 | 2.
21 | 3.
22 |
23 | ### TL; DR
24 |
25 | ...
26 |
27 | ## Comments
28 |
29 | Anything else related to the issue that doesn't fit above.
30 |
31 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | ## Description
2 |
3 | [Link to issue number]()
4 |
5 | ### Changes
6 |
7 | Explain why?
8 |
9 | #### Screenshot
10 |
11 | ![]()
12 |
13 |
14 |
15 | ### PR Checklist
16 |
17 | - [ ] [Clean commits](https://github.com/carrot/share-button/blob/master/CONTRIBUTING.md#commit-cleanliness)
18 | - [ ] Passing Tests
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | .com.apple.timemachine.supported
4 | phantomjsdriver.log
5 | dist
6 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "esnext": true,
3 | "browser": true,
4 | "devel": true
5 | }
6 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | .com.apple.timemachine.supported
4 | phantomjsdriver.log
5 | .github
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | sudo: false
3 | before_script:
4 | - python -m SimpleHTTPServer > /dev/null 2>&1 &
5 | - sleep 2
6 | - gulp build
7 | node_js:
8 | - 'node'
9 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Share Button
2 | Hello there! First of all, thanks for being interested in share-button and helping out. We all think you are awesome, and by contributing to open source projects, you are making the world a better place. That being said, there are a few ways to make the process of contributing code to share-button smoother, detailed below:
3 |
4 | ## Filing Issues
5 | If you are filing an issue, it is **required** that you include a jsfiddle that reproduces the issue you are having. The share button is a very simple and straightforward script, and often times if you are encountering a bug it is the result of some other code on your page. This is not something you should file an issue about, as we don't have time to debug your code. By taking a look at the issue with the button in isolation, it is easier to determine whether you have found a bug with share-button or whether the issue was introduced by your code.
6 |
7 | There is a [template jsfiddle here](http://jsfiddle.net/28fn23bj/) for you to experiment with if you'd like.
8 |
9 | If you are opening an issue to suggest a feature, that's totally fine, although we might not add the feature for you immediately. If you are after a new feature, the best course of action is to open an issue describing the feature you want in detail. If we approve of the feature, try submitting a pull request to add it yourself! As mentioned above, this project is fairly straightforward, and there are explicit instructions below for getting set up -- a contribution is a good way to get your feet wet with open source.
10 |
11 | ## Getting Set Up
12 | - Clone the project down
13 | - Install [nodejs](http://nodejs.org) (version 4.0.0 or higher)
14 | - _Windows users only:_ Install [Python](https://wiki.python.org/moin/BeginnersGuide/Download) (version 2.x.x) and set up [environment path](http://stackoverflow.com/a/6318188/1308734).
15 | - Run `npm install`
16 | - Make changes to the files in the `src` folder
17 | - Run `gulp build` to build the js files into `dist`
18 | - Open `index.html` locally to see your changes.
19 |
20 | ## Testing
21 | This project is constantly evolving, and to ensure that things are secure and working for everyone, we need to have tests. If you are adding a new feature, please make sure to add a test for it. We are using [pioneer](pioneerjs.com) for testing with [phantom.js](http://phantomjs.org/). For getting setup with those please checkout their setup instructions on their sites. All said and done, tests for this library are tough - at very least make sure to visually confirm that it's working using index.html in the root of the project. If it's a visual please make sure that everything looks okay in [examples/animation.html].
22 |
23 | To run the test suite, make sure you have installed [PhantomJS](//phantomjs.org/download.html), then you can use the `npm test` command to run them.
24 |
25 | ## Commit Cleanliness
26 | It's ok if you start out with a bunch of experimentation and your commit log isn't totally clean, but before any pull requests are accepted, we like to have a nice clean commit log. That means [well-written and clear commit messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) and commits that each do something significant, rather than being typo or bug fixes.
27 |
28 | If you submit a pull request that doesn't have a clean commit log, we will ask you to clean it up before we accept. This means being familiar with rebasing - if you are not, [this guide](https://help.github.com/articles/interactive-rebase) by github should help you to get started, and feel free to ask us anything, we are happy to help.
29 |
30 | ## Deployment
31 | When deploying new changes you'll run create a new git tag, push to github, run `gulp build`, publish to npm. Then you'll want to package all the files in the `dist` folder up into (a zip & tarball), and these will be added to your release on github.
32 |
33 | Happy Developing!
34 |
35 | 
36 |
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | License (MIT)
2 | -------------
3 |
4 | Copyright (c) 2013 Jeff Escalante, Carrot Creative
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
7 |
8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9 |
10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
Share Button
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | # An Introduction
13 | Simple, light, flexible, and good-looking share button. [See it in action!](http://sharebutton.co/)
14 |
15 | ## Why Should You Use This?
16 | All major social networks have their own share widgets you can put on your page, but this isn't ideal for a variety of reasons:
17 |
18 | 1. They tend to be slow-loading.
19 | 2. They inject extra javascript and DOM elements into your page making it slower.
20 | 3. They generally aren't customizable enough to fit the design of your site.
21 | 4. Managing each provider's code snippets etc is repetitive and needless. Additionally, they can make your front-end code quite messy.
22 | 5. The buttons themselves take up a lot of space (especially the Facebook share button).
23 |
24 | Let's take a quick look at the alternative, using this little plugin:
25 |
26 | 1. It doesn't load any iframes or extra javascript making the overall load time much faster.
27 | 2. It looks simple and clean by default, and can be customized in any and every way.
28 | 3. All you have to do to use it is include the script and call `new ShareButton` on a `share-button` element. That's two lines of code total, the script link and the share call.
29 | 4. It's tiny and compact, expanding only when the user actually wants to share something.
30 |
31 | # Getting Started
32 | 1. [Download the latest script & stylesheet](https://github.com/carrot/share-button/releases) and include it on your page.
33 | 2. Make a `share-button` element on your page
34 | 3. In your javascript, call `new ShareButton()`
35 | 4. Pass options to the share call if you want (details below)
36 | 5. Profit!
37 |
38 | ```html
39 |
40 | ```
41 |
42 | ```js
43 | new ShareButton({
44 | networks: {
45 | facebook: {
46 | appId: "abc123"
47 | }
48 | }
49 | });
50 | ```
51 |
52 | ## NPM installation
53 |
54 | 1. `npm i --save-dev share-button`
55 | 2. Make a `share-button` element on your page
56 | 3. In your javascript, `var ShareButton = require('share-button');`
57 | 4. Pass options to the share call if you want (details below)
58 | 5. Profit!
59 |
60 | ```html
61 |
62 | ```
63 |
64 | ```js
65 | var ShareButton = require('share-button');
66 |
67 | new ShareButton({
68 | networks: {
69 | facebook: {
70 | appId: "abc123"
71 | }
72 | }
73 | });
74 | ```
75 |
76 | # Customization
77 | ## Configuration Options
78 | The share button is extremely flexible. As such we provide the ability to pass a wide array of options for additional configuration. All configuration options are available here: [Configuration Options](https://github.com/carrot/share-button/blob/master/docs/configuration-options.md)
79 |
80 | ## Styles
81 | Additionally, you're able to customize the look and feel of the button and animations though CSS. All CSS styles and how to modify them are available here: [CSS Styles](https://github.com/carrot/share-button/blob/master/docs/styles.md)
82 |
83 | ## Hooks
84 | You are able to set `before` and `after` hooks when a user clicks a network. This allows you to dynamically change attributes for that button. For more information: [click here](https://github.com/carrot/share-button/blob/master/docs/network-hooks.md)
85 |
86 | # Public API
87 | The share button also returns a simple API that can be used to control it should you need to. Example shown below:
88 |
89 | ```js
90 | var share = new ShareButton(); // Grabs all share-button elements on page
91 |
92 | share.toggle(); // toggles the share button popup
93 | share.open(); // open the share button popup
94 | share.close(); // closes the share button popup
95 | share.config; // exposes the configurations listed above
96 | ```
97 |
98 | # Fonts
99 | As of version 1.0.0 we completely removed the `Entypo` font set!
100 |
101 | # Inspiration
102 | This project was inspired by [this dribbble shot](http://dribbble.com/shots/1072278) and [this cssdeck experiment](http://cssdeck.com/labs/css-social-share-button) - huge props to these two guys for some incredible ideas and work.
103 |
104 | # Contributing and License
105 | - Contributing Guidelines can be found [here](https://github.com/carrot/share-button/blob/master/CONTRIBUTING.md)
106 | - Licensed under MIT - [details here](https://github.com/carrot/share-button/blob/master/LICENSE)
107 |
--------------------------------------------------------------------------------
/docs/configuration-options.md:
--------------------------------------------------------------------------------
1 | # Configuration Options
2 | You can pass an options object when you call `ShareButton` on an element to make things a little more flexible.
3 |
4 | Passing configuration options:
5 |
6 | ```js
7 | config = {
8 | networks: {
9 | facebook: {
10 | appId: '12345'
11 | }
12 | }
13 | }
14 |
15 | var share = new ShareButton('.share-button', config);
16 | ```
17 |
18 | All options:
19 |
20 | ```js
21 | config = {
22 | protocol: // the protocol you'd prefer to use. [Default: your current protocol]
23 | url: // the url you'd like to share. [Default: `window.location.href`]
24 | title: // title to be shared alongside your link [Default: See below in defaults section]
25 | description: // text to be shared alongside your link, [Default: See below in defaults section]
26 | image: // image to be shared [Default: See below in defaults section]
27 | ui: {
28 | flyout: // change the flyout direction of the shares. chose from `top left`, `top center`, `top right`, `bottom left`, `bottom right`, `bottom center`, `middle left`, or `middle right` [Default: `top center`]
29 | button_font: // include the Lato font set from the Google Fonts API. [Default: `true`]
30 | buttonText: // change the text of the button, [Default: `Share`]
31 | icon_font: // include the minified Entypo font set. [Default: `true`]
32 | },
33 | networks: {
34 | googlePlus: {
35 | enabled: // Enable Google+. [Default: true]
36 | url: // the url you'd like to share to Google+ [Default: config.url]
37 | },
38 | twitter: {
39 | enabled: // Enable Twitter. [Default: true]
40 | url: // the url you'd like to share to Twitter [Default: config.url]
41 | description: // text to be shared alongside your link to Twitter [Default: config.description]
42 | },
43 | facebook: {
44 | enabled: // Enable Facebook. [Default: true]
45 | load_sdk: // Load the FB SDK. If false, it will default to Facebook's sharer.php implementation.
46 | // NOTE: This will disable the ability to dynamically set values and rely directly on applicable Open Graph tags.
47 | // [Default: true]
48 | url: // the url you'd like to share to Facebook [Default: config.url]
49 | appId: // Facebook app id for tracking shares. if provided, will use the facebook API
50 | title: // title to be shared alongside your link to Facebook [Default: config.title]
51 | caption: // caption to be shared alongside your link to Facebook [Default: null]
52 | description: // text to be shared alongside your link to Facebook [Default: config.description]
53 | image: // image to be shared to Facebook [Default: config.image]
54 | },
55 | pinterest: {
56 | enabled: // Enable Pinterest. [Default: true]
57 | url: // the url you'd like to share to Pinterest [Default: config.url]
58 | image: // image to be shared to Pinterest [Default: config.image]
59 | description: // text to be shared alongside your link to Pinterest [Default: config.description]
60 | },
61 | reddit: {
62 | enabled: // Enable Reddit. [Default: true]
63 | url: // the url you'd like to share to Reddit [Default: config.url]
64 | title: // title to be shared alongside your link to Reddit [Default: config.title]
65 | },
66 | linkedin: {
67 | enabled: // Enable LinkedIn. [Default: true]
68 | url: // the url you'd like to share to LinkedIn [Default: config.url]
69 | title: // title to be shared alongside your link to LinkedIn [Default: config.title],
70 | description: // text to be shared alongside your link to LinkedIn [Default: config.description]
71 | },
72 | whatsapp: {
73 | enabled: // Enable WhatsApp. [Default: true]
74 | description: // text to be shared alongside your link to WhatsApp [Default: config.description],
75 | url: // the url you'd like to share to WhatsApp [Default: config.url]
76 | },
77 | email: {
78 | enabled: // Enable Email. [Default: true]
79 | title: // the subject of the email [Default: config.title]
80 | description: // The body of the email [Default: config.description]
81 | }
82 | }
83 | }
84 | ```
85 |
86 | ## Defaults
87 | The follow logic is used to populate default values for some of the config keys above:
88 | - _title_ - The first defined, non-null value out of (in order):
89 | - meta tag name='og:title' content attribute value
90 | - meta tag name='twitter:title' content attribute value
91 | - document's title tag value
92 |
93 | - _image_ - the first defined, non-null value out of (in order):
94 | - meta tag name='og:image' content attribute value
95 | - meta tag name='twitter:image' content attribute value
96 |
97 | - _description_ - the first defined, non-null value out of (in order):
98 | - meta tag name='og:description' content attribute value
99 | - meta tag name='twitter:description' content attribute value
100 | - meta tag name='description' content attribute value
101 |
--------------------------------------------------------------------------------
/docs/network-hooks.md:
--------------------------------------------------------------------------------
1 | # Network Hooks
2 | You are able to set `before` and `after` hooks when a user clicks a network. The context passed to the hook is the current network's configuration. To change any of the network's configuration before or after instantiating the share, you must alter the value and return `this` as shown in the examples below.
3 |
4 | ```js
5 | config = {
6 | networks: {
7 | facebook: {
8 | before: function() {
9 | this.url = "https://github.com/carrot/share-button";
10 | this.text = "Changing the Facebook Share Configurations";
11 | },
12 | after: function() {
13 | console.log("User shared:", this.url);
14 | }
15 | }
16 | }
17 | }
18 | ```
19 |
20 | **Example:**
21 |
22 | ```js
23 | new Share(".share-button-top", {
24 | title: "Share Button",
25 | networks: {
26 | facebook: {
27 | appId: "602752456409826",
28 | before: function() {
29 | console.log("BEFORE", this);
30 | this.url = "https://github.com/carrot/share-button";
31 | this.text = "Changing the Facebook Share Configurations";
32 | },
33 | after: function() {
34 | console.log("User shared:", this.url);
35 | }
36 | }
37 | }
38 | });
39 | ```
40 |
41 | On a page with multiple share buttons, you can use the `before` hook to dynamically set the share URL:
42 |
43 | ```js
44 | new Share(".share-button", {
45 | networks: {
46 | facebook: {
47 | before: function(element) {
48 | this.url = element.getAttribute("data-url");
49 | },
50 | after: function() {
51 | console.log("User shared:", this.url);
52 | }
53 | }
54 | }
55 | });
56 | ```
57 |
--------------------------------------------------------------------------------
/docs/styles.md:
--------------------------------------------------------------------------------
1 | # Styles
2 | The Share Button styles attempt to be an all-for-one solution for any site. To customize your share button you can modify the options you're passing into your object or you can create your own stylesheet!
3 |
4 | Within `share-button.styl` you'll find two mixins: `network()` & `socialIcon()`. The `network()` mixin is what makes the network list responsive. `socialIcon()` is how we insert each svg and define their colors. After all the mixins and colors are defined then we setup the share-button's styles.
5 |
6 | ## Customization
7 | If you're going to cusomtize the share button you'll need to define styles for these main elements:
8 |
9 | ```
10 | share-button - the button that holds ` Share`
11 | ul - holds the list of social networks
12 | li - holds each network
13 | a - the link tag that is a network
14 | ```
15 |
16 | If you'd like to control the width of each network based on the amount of networks, you can use `.networks-{num}`. If you're using all the networks, don't forget about `whats-app` for mobile, there will be 8.
17 |
18 | Happy developing! :D
19 |
--------------------------------------------------------------------------------
/examples/animation.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Share Button Collision Test
4 |
5 |
6 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp');
2 | var uglify = require('gulp-uglify');
3 | var rename = require('gulp-rename');
4 | var accord = require('gulp-accord');
5 | var browserify = require('gulp-browserify');
6 | var shell = require('gulp-shell');
7 | var del = require('del');
8 | var autoprefixer = require('autoprefixer-stylus');
9 | var axis = require('axis');
10 | var poststylus = require('poststylus');
11 | var postcssSVG = require('postcss-svg');
12 |
13 | gulp.task('unbuild', function() {
14 | del(['dist']);
15 | });
16 |
17 | gulp.task('style', function() {
18 | var styleShareButton = gulp
19 | .src('src/share-button.styl')
20 | .pipe(accord('stylus', {
21 | use: [
22 | autoprefixer(),
23 | axis(),
24 | poststylus([postcssSVG({ paths: ['./src/svg' ]})])
25 | ]
26 | }))
27 | .pipe(gulp.dest('dist/'))
28 | .pipe(shell(
29 | ['node_modules/.bin/minify --output dist/share-button.min.css dist/share-button.css']
30 | ))
31 | });
32 |
33 | gulp.task('script', function() {
34 | var umdShareButton = gulp
35 | .src(['src/share-button.js'], { read: false })
36 | .pipe(browserify({
37 | transform: ['babelify'],
38 | standalone: 'ShareButton'
39 | }))
40 | .pipe(gulp.dest('dist/'))
41 | .pipe(uglify())
42 | .pipe(rename({ suffix: '.min' }))
43 | .pipe(gulp.dest('dist/'));
44 | });
45 |
46 | gulp.task('build', ['script', 'style']);
47 | gulp.task('default', ['build']);
48 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Share Button Test
6 |
7 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | new share-button ();
88 |
89 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "share-button",
3 | "description": "fast, beautiful, and painless social shares",
4 | "version": "1.0.3",
5 | "homepage": "http://sharebutton.co",
6 | "main": "dist/share-button.js",
7 | "bugs": {
8 | "url": "https://github.com/carrot/share-button/issues"
9 | },
10 | "author": "Jeff Escalante ",
11 | "contributors": [
12 | {
13 | "name": "Tom Milewski",
14 | "email": "tmilewski@gmail.com"
15 | },
16 | {
17 | "name": "Henry Snopek",
18 | "email": "hhsnopek@gmail.com"
19 | },
20 | {
21 | "name": "Patrick Bacon-Blaber",
22 | "email": "pablaber225@gmail.com"
23 | }
24 | ],
25 | "repository": {
26 | "type": "git",
27 | "url": "https://github.com/carrot/share-button"
28 | },
29 | "license": "MIT",
30 | "dependencies": {
31 | },
32 | "devDependencies": {
33 | "autoprefixer-stylus": "^0.8.0",
34 | "axis": "^0.5.0",
35 | "babelify": "^6.3.0",
36 | "core-js": "^1.2.0",
37 | "del": "^2.0.2",
38 | "gulp": "^3.9.0",
39 | "gulp-accord": "^0.2.0",
40 | "gulp-browserify": "^0.5.1",
41 | "gulp-rename": "^1.2.2",
42 | "gulp-shell": "^0.5.0",
43 | "gulp-uglify": "^1.4.1",
44 | "minifier": "^0.7.1",
45 | "pioneer": "^0.11.6",
46 | "postcss-svg": "^1.0.1",
47 | "poststylus": "^0.2.1",
48 | "stylus": "^0.52.4"
49 | },
50 | "scripts": {
51 | "test": "pioneer",
52 | "build": "gulp build",
53 | "unbuild": "gulp unbuild",
54 | "build-css": "gulp style",
55 | "build-js": "gulp script"
56 | },
57 | "keywords": [
58 | "share",
59 | "social"
60 | ],
61 | "engines": {}
62 | }
63 |
--------------------------------------------------------------------------------
/pioneer.json:
--------------------------------------------------------------------------------
1 | {
2 | "feature": "tests/features",
3 | "require": [
4 | "tests/steps",
5 | "tests/widgets",
6 | "tests/helpers"
7 | ],
8 | "format": "pioneerformat.js",
9 | "driver": "phantomjs",
10 | "error_formatter": "errorformat.js",
11 | "preventReload": false,
12 | "coffee": true,
13 | "verbose": true
14 | }
15 |
--------------------------------------------------------------------------------
/src/share-button.js:
--------------------------------------------------------------------------------
1 | require('core-js/fn/symbol');
2 | require('core-js/fn/array/iterator');
3 | require('core-js/fn/math/trunc');
4 | import ShareUtils from './share-utils';
5 | import StringUtils from './string-utils';
6 |
7 | /**
8 | * Sharebutton
9 | * @class
10 | * @classdesc
11 | * @extends ShareUtils
12 |
13 | * @param {String} element
14 | * @param {Object} options
15 | */
16 | class ShareButton extends ShareUtils {
17 | constructor(element, options) {
18 | super();
19 |
20 | if (typeof element === 'object') {
21 | this.element = undefined;
22 | options = element;
23 | } else
24 | this.element = element;
25 |
26 | this.el = {
27 | head: document.getElementsByTagName('head')[0],
28 | body: document.getElementsByTagName('body')[0]
29 | };
30 |
31 | this.config = {
32 | enabledNetworks: 0,
33 | protocol: '//',
34 | url: window.location.href,
35 | caption: null,
36 | title: this._defaultTitle(),
37 | image: this._defaultImage(),
38 | description: this._defaultDescription(),
39 |
40 | ui: {
41 | flyout: 'sb-top sb-center',
42 | buttonText: 'Share',
43 | namespace: 'sb-',
44 | networkOrder: [],
45 | collision: false,
46 | },
47 |
48 | networks: {
49 | googlePlus: {
50 | enabled: true,
51 | url: null
52 | },
53 | twitter: {
54 | enabled: true,
55 | url: null,
56 | description: null
57 | },
58 | facebook: {
59 | enabled: true,
60 | loadSdk: true,
61 | url: null,
62 | appId: null,
63 | title: null,
64 | caption: null,
65 | description: null,
66 | image: null
67 | },
68 | pinterest: {
69 | enabled: true,
70 | url: null,
71 | image: null,
72 | description: null
73 | },
74 | reddit: {
75 | enabled: true,
76 | url: null,
77 | title: null
78 | },
79 | linkedin: {
80 | enabled: true,
81 | url: null,
82 | title: null,
83 | description: null
84 | },
85 | whatsapp: {
86 | enabled: true,
87 | description: null,
88 | url: null
89 | },
90 | email: {
91 | enabled: true,
92 | title: null,
93 | description: null
94 | }
95 | }
96 | };
97 |
98 | this.listener = null;
99 | this._setup(this.element, options);
100 | }
101 |
102 | /**
103 | * @method open
104 | * @description Opens Share Button
105 | */
106 | open() { this._public('Open'); }
107 |
108 | /**
109 | * @method close
110 | * @description Cpens Share Button
111 | */
112 | close() { this._public('Close'); }
113 |
114 | /**
115 | * @method toggle
116 | * @description Toggles Share Button
117 | */
118 | toggle() { this._public('Toggle'); }
119 |
120 | /**
121 | * @method toggleListen
122 | * @description Toggles the Share Button listener, good for updaing share
123 | * button for CSS animations.
124 | */
125 | toggleListen() { this._public('Listen'); }
126 |
127 | /**
128 | * @method _public
129 | * @description Executes action
130 | * @private
131 | *
132 | * @param {String} action
133 | */
134 | _public(action) {
135 | let instances;
136 |
137 | if (typeof element === 'undefined')
138 | instances =
139 | super._objToArray(document.getElementsByTagName('share-button'));
140 | else
141 | instances = document.querySelectorAll(element);
142 |
143 | for (let instance of instances) {
144 | let networks =
145 | instance.getElementsByClassName(`${this.config.ui.namespace}social`)[0];
146 | this[`_event${action}`](instance, networks);
147 | }
148 | }
149 |
150 | /**
151 | * @method _setup
152 | * @description Sets up Share Button
153 | * @private
154 | *
155 | * @param {String} element selector
156 | * @param {Object} opts
157 | */
158 | _setup(element, opts) {
159 | let instances;
160 |
161 | if (typeof element === 'undefined')
162 | instances =
163 | super._objToArray(document.getElementsByTagName('share-button'));
164 | else {
165 | instances = document.querySelectorAll(`share-button${element}`);
166 | if (typeof instances === 'object')
167 | instances = super._objToArray(instances);
168 | }
169 |
170 | // Adding user configs to default configs
171 | this._merge(this.config, opts);
172 |
173 | // Disable whatsapp display if not a mobile device
174 | if (this.config.networks.whatsapp.enabled && !this._isMobile())
175 | this.config.networks.whatsapp.enabled = false;
176 |
177 | // Default order of networks if no network order entered
178 | if (this.config.ui.networkOrder.length === 0)
179 | this.config.ui.networkOrder = [
180 | 'pinterest',
181 | 'twitter',
182 | 'facebook',
183 | 'whatsapp',
184 | 'googlePlus',
185 | 'reddit',
186 | 'linkedin',
187 | 'email'
188 | ];
189 |
190 | for (let network of Object.keys(this.config.networks)) {
191 | if (this.config.ui.networkOrder.indexOf(network.toString()) < 0) {
192 | this.config.networks[network].enabled = false;
193 | this.config.ui.networkOrder.push(network);
194 | }
195 | }
196 |
197 | this._fixFlyout();
198 | this._detectNetworks();
199 | this._normalizeNetworkConfiguration();
200 |
201 | // Inject Facebook JS SDK (if Facebook is enabled)
202 | if (this.config.networks.facebook.enabled &&
203 | this.config.networks.facebook.loadSdk)
204 | this._injectFacebookSdk();
205 |
206 | // Initialize instances
207 | let index = 0;
208 | for (let instance of instances) {
209 | this._setupInstance(instance, index++);
210 | }
211 | }
212 |
213 | /**
214 | * @method _setupInstance
215 | * @description Sets up each instance with config and styles
216 | * @private
217 | *
218 | * @param {DOMNode} instance
219 | * @param {Integer} index
220 | */
221 | _setupInstance(instance, index) {
222 | this._hide(instance);
223 |
224 | // Add necessary classes to instance
225 | // (Note: FF doesn't support adding multiple classes in a single call)
226 | this._addClass(instance, `sharer-${index}`);
227 | this._injectHtml(instance);
228 | this._show(instance);
229 |
230 | let networksCon =
231 | instance.getElementsByClassName(`${this.config.ui.namespace}social`)[0];
232 | let networks = instance.getElementsByTagName('li');
233 |
234 | this._addClass(networksCon, `networks-${this.config.enabledNetworks}`);
235 | instance.addEventListener('click', () =>
236 | this._eventToggle(instance, networksCon)
237 | );
238 |
239 | // Add listener to activate networks and close button
240 | for (let k in Object.keys(networks)) {
241 | let network = networks[k];
242 |
243 | if (typeof(network) !== "undefined") {
244 | let name = network.getAttribute('data-network');
245 | let a = network.getElementsByTagName('a')[0];
246 |
247 | this._addClass(network, this.config.networks[name].class);
248 |
249 | if (network.className.indexOf('email') < 0)
250 | a.setAttribute('onclick', 'return false');
251 |
252 | a.addEventListener('mousedown', () => {
253 | this._hook('before', name, instance);
254 | });
255 | a.addEventListener('mouseup', () => {
256 | this[`_network${StringUtils.capFLetter(name)}`](network);
257 | });
258 | a.addEventListener('click', () => {
259 | this._hook('after', name, instance);
260 | });
261 | }
262 | }
263 | }
264 |
265 | /**
266 | * @method _eventToggle
267 | * @description Toggles 'active' class on button
268 | * @private
269 | *
270 | * @param {DOMNode} button
271 | * @param {DOMNode} networks
272 | */
273 | _eventToggle(button, networks) {
274 | if (this._hasClass(networks, 'active'))
275 | this._eventClose(networks);
276 | else
277 | this._eventOpen(button, networks);
278 | }
279 |
280 | /**
281 | * @method _eventOpen
282 | * @description Add 'active' class & remove 'load' class on button
283 | * @private
284 | *
285 | * @param {DOMNode} button
286 | * @param {DOMNode} networks
287 | */
288 | _eventOpen(button, networks) {
289 | if (this._hasClass(networks, 'load'))
290 | this._removeClass(networks, 'load');
291 | if (this.collision)
292 | this._collisionDetection(button, networks);
293 |
294 | this._addClass(networks, 'active');
295 | }
296 |
297 | /**
298 | * @method _eventClose
299 | * @description Remove 'active' class on button
300 | * @private
301 | *
302 | * @param {DOMNode} button
303 | */
304 | _eventClose(button) {
305 | this._removeClass(button, 'active');
306 | }
307 |
308 | /**
309 | * @method _eventListen
310 | * @description Toggles weather or not a button's classes are being
311 | * constantly updated regardless of scrolls or window resizes.
312 | * @private
313 | *
314 | * @param {DOMNode} button
315 | * @param {DOMNode} networks
316 | */
317 | _eventListen(button, networks) {
318 | let dimensions = this._getDimensions(button, networks);
319 | if (this.listener === null)
320 | this.listener = window.setInterval(() =>
321 | this._adjustClasses(button, networks, dimensions), 100
322 | );
323 | else {
324 | window.clearInterval(this.listener);
325 | this.listener = null;
326 | }
327 | }
328 |
329 | /**
330 | * @method _fixFlyout
331 | * @description Fixes the flyout entered by the user to match their provided
332 | * namespace
333 | *@private
334 | */
335 | _fixFlyout() {
336 | let flyouts = this.config.ui.flyout.split(' ');
337 | if (flyouts[0].substring(0,this.config.ui.namespace.length) !==
338 | this.config.ui.namespace)
339 | flyouts[0] = `${this.config.ui.namespace}${flyouts[0]}`;
340 | if (flyouts[1].substring(0,this.config.ui.namespace.length) !==
341 | this.config.ui.namespace)
342 | flyouts[1] = `${this.config.ui.namespace}${flyouts[1]}`;
343 | this.config.ui.flyout = flyouts.join(' ');
344 | }
345 |
346 | /**
347 | * @method _collisionDetection
348 | * @description Adds listeners the first time a button is clicked to call
349 | * this._adjustClasses during scrolls and resizes.
350 | * @private
351 | *
352 | * @param {DOMNode} button - share button
353 | * @param {DOMNode} networks - list of social networks
354 | */
355 | _collisionDetection(button, networks) {
356 | let dimensions = this._getDimensions(button, networks);
357 | this._adjustClasses(button, networks, dimensions);
358 |
359 | if (!button.classList.contains('clicked')) {
360 | window.addEventListener('scroll', () =>
361 | this._adjustClasses(button, dimensions));
362 | window.addEventListener('resize', () =>
363 | this._adjustClasses(button, dimensions));
364 | button.classList.add('clicked');
365 | }
366 | }
367 |
368 | /**
369 | * @method _getDimensions
370 | * @description Returns an object with the dimensions of the button and
371 | * label elements of a Share Button.
372 | * @private
373 | *
374 | * @param {DOMNode} button
375 | * @param {DOMNode} networks
376 | * @returns {Object}
377 | */
378 | _getDimensions(button, networks) {
379 | return {
380 | networksWidth: networks.offsetWidth,
381 | buttonHeight: button.offsetHeight,
382 | buttonWidth: button.offsetWidth
383 | };
384 | }
385 |
386 | /**
387 | * @method _adjustClasses
388 | * @description Adjusts the positioning of the list of social networks based
389 | * off of where the share button is relative to the window.
390 | *
391 | * @private
392 | * @param {DOMNode} button
393 | * @param {DOMNode} networks
394 | * @param {Object} dimensions
395 | */
396 | _adjustClasses(button, networks, dimensions) {
397 | let windowWidth = window.innerWidth;
398 | let windowHeight = window.innerHeight;
399 | let leftOffset = button.getBoundingClientRect().left +
400 | dimensions.buttonWidth / 2;
401 | let rightOffset = windowWidth - leftOffset;
402 | let topOffset = button.getBoundingClientRect().top +
403 | dimensions.buttonHeight / 2;
404 | let position =
405 | this._findLocation(leftOffset, topOffset, windowWidth, windowHeight);
406 |
407 | if (position[1] === "middle" && position[0] !== "center" &&
408 | ((position[0] === "left" &&
409 | windowWidth <= leftOffset + 220 + dimensions.buttonWidth / 2) ||
410 | (position[0] === "right" &&
411 | windowWidth <= rightOffset + 220 + dimensions.buttonWidth / 2)
412 | )
413 | ) {
414 | networks.classList.add(`${this.config.ui.namespace}top`);
415 | networks.classList.remove(`${this.config.ui.namespace}middle`);
416 | networks.classList.remove(`${this.config.ui.namespace}bottom`);
417 | }
418 | else {
419 | switch(position[0]) {
420 | case "left":
421 | networks.classList.add(`${this.config.ui.namespace}right`);
422 | networks.classList.remove(`${this.config.ui.namespace}center`);
423 | networks.classList.remove(`${this.config.ui.namespace}left`);
424 | break;
425 | case "center":
426 | if (position[1] !== "top")
427 | networks.classList.add(`${this.config.ui.namespace}top`);
428 | networks.classList.add(`${this.config.ui.namespace}center`);
429 | networks.classList.remove(`${this.config.ui.namespace}left`);
430 | networks.classList.remove(`${this.config.ui.namespace}right`);
431 | networks.classList.remove(`${this.config.ui.namespace}middle`);
432 | break;
433 | case "right":
434 | networks.classList.add(`${this.config.ui.namespace}left`);
435 | networks.classList.remove(`${this.config.ui.namespace}center`);
436 | networks.classList.remove(`${this.config.ui.namespace}right`);
437 | break;
438 | }
439 | switch(position[1]) {
440 | case "top":
441 | networks.classList.add(`${this.config.ui.namespace}bottom`);
442 | networks.classList.remove(`${this.config.ui.namespace}middle`);
443 | if (position[0] !== "center")
444 | networks.classList.remove(`${this.config.ui.namespace}top`);
445 | break;
446 | case "middle":
447 | if (position[0] !== "center") {
448 | networks.classList.add(`${this.config.ui.namespace}middle`);
449 | networks.classList.remove(`${this.config.ui.namespace}top`);
450 | }
451 | networks.classList.remove(`${this.config.ui.namespace}bottom`);
452 | break;
453 | case "bottom":
454 | networks.classList.add(`${this.config.ui.namespace}top`);
455 | networks.classList.remove(`${this.config.ui.namespace}middle`);
456 | networks.classList.remove(`${this.config.ui.namespace}bottom`);
457 | break;
458 | }
459 | }
460 | }
461 |
462 | /**
463 | * @method _findLocation
464 | * @description Finds the location of the label given by its x and y value
465 | * with respect to the window width and window height given.
466 | * @private
467 | *
468 | * @param {number} labelX
469 | * @param {number} labelY
470 | * @param {number} windowWidth
471 | * @param {number} windowHeight
472 | * @returns {Array}
473 | */
474 | _findLocation(labelX, labelY, windowWidth, windowHeight) {
475 | let xPosition = ["left", "center", "right"];
476 | let yPosition = ["top", "middle", "bottom"];
477 | let xLocation =
478 | Math.trunc(3 * (1 - ((windowWidth - labelX) / windowWidth)));
479 | let yLocation =
480 | Math.trunc(3 * (1 - ((windowHeight - labelY) / windowHeight)));
481 | if (xLocation >= 3) xLocation = 2;
482 | else if (xLocation <= -1) xLocation = 0;
483 | if (yLocation >= 3) yLocation = 2;
484 | else if (yLocation <= -1) yLocation = 0;
485 | return [xPosition[xLocation], yPosition[yLocation]];
486 | }
487 |
488 | /**
489 | * @method _networkFacebook
490 | * @description Create & display a Facebook window
491 | * @private
492 | */
493 | _networkFacebook(element) {
494 | if (this.config.networks.facebook.loadSdk) {
495 | if (!window.FB) {
496 | console.error('The Facebook JS SDK hasn\'t loaded yet.');
497 | return this._updateHref(element, 'https://www.facebook.com/sharer/sharer.php', {
498 | u: this.config.networks.facebook.url
499 | });
500 | }
501 | return FB.ui({
502 | method:'feed',
503 | name: this.config.networks.facebook.title,
504 | link: this.config.networks.facebook.url,
505 | picture: this.config.networks.facebook.image,
506 | caption: this.config.networks.facebook.caption,
507 | description: this.config.networks.facebook.description
508 | });
509 | } else {
510 | return this._updateHref(
511 | element,
512 | 'https://www.facebook.com/sharer/sharer.php', {
513 | u: this.config.networks.facebook.url
514 | }
515 | );
516 | }
517 | }
518 |
519 | /**
520 | * @method _networkTwitter
521 | * @description Create & display a Twitter window
522 | * @private
523 | */
524 | _networkTwitter(element) {
525 | this._updateHref(element, 'https://twitter.com/intent/tweet', {
526 | text: this.config.networks.twitter.description,
527 | url: this.config.networks.twitter.url
528 | });
529 | }
530 |
531 | /**
532 | * @method _networkGooglePlus
533 | * @description Create & display a Google Plus window
534 | * @private
535 | */
536 | _networkGooglePlus(element) {
537 | this._updateHref(element, 'https://plus.google.com/share', {
538 | url: this.config.networks.googlePlus.url
539 | });
540 | }
541 |
542 | /**
543 | * @method _networkPinterest
544 | * @description Create & display a Pinterest window
545 | * @private
546 | */
547 | _networkPinterest(element) {
548 | this._updateHref(element, 'https://www.pinterest.com/pin/create/button', {
549 | url: this.config.networks.pinterest.url,
550 | media: this.config.networks.pinterest.image,
551 | description: this.config.networks.pinterest.description
552 | });
553 | }
554 |
555 | /**
556 | * @method _networkLinkedIn
557 | * @description Create & display a Linkedin window
558 | * @private
559 | */
560 | _networkLinkedin(element) {
561 | this._updateHref(element, 'https://www.linkedin.com/shareArticle', {
562 | mini: 'true',
563 | url: this.config.networks.linkedin.url,
564 | title: this.config.networks.linkedin.title,
565 | summary: this.config.networks.linkedin.description
566 | });
567 | }
568 |
569 | /**
570 | * @method _networkEmail
571 | * @description Create & display an Email window
572 | * @private
573 | */
574 | _networkEmail(element) {
575 | this._updateHref(element, 'mailto:', {
576 | subject: this.config.networks.email.title,
577 | body: this.config.networks.email.description
578 | });
579 | }
580 |
581 | /**
582 | * @method _networkReddit
583 | * @description Create & display a Reddit window
584 | * @private
585 | */
586 | _networkReddit(element) {
587 | this._updateHref(element, 'http://www.reddit.com/submit', {
588 | url: this.config.networks.reddit.url,
589 | title: this.config.networks.reddit.title
590 | });
591 | }
592 |
593 | /**
594 | * @method _networkWhatsapp
595 | * @description Create & display a Whatsapp window
596 | * @private
597 | */
598 | _networkWhatsapp(element) {
599 | this._updateHref(element, 'whatsapp://send', {
600 | text: this.config.networks.whatsapp.description + " " +
601 | this.config.networks.whatsapp.url
602 | });
603 | }
604 |
605 | /**
606 | * @method _injectStylesheet
607 | * @description Inject link to stylesheet
608 | * @private
609 | *
610 | * @param {String} url
611 | */
612 | _injectStylesheet(url) {
613 | if (!this.el.head.querySelector(`link[href='${url}']`)) {
614 | let link = document.createElement("link");
615 | link.setAttribute("rel", "stylesheet");
616 | link.setAttribute("href", url);
617 | this.el.head.appendChild(link);
618 | }
619 | }
620 |
621 | /**
622 | * @method _injectHtml
623 | * @description Inject button structure
624 | * @private
625 | *
626 | * @param {DOMNode} instance
627 | */
628 | _injectHtml(instance) {
629 | let networks = this.config.ui.networkOrder;
630 | let networkList = '';
631 |
632 | for (let network of networks) {
633 | networkList += ` `;
634 | }
635 | instance.innerHTML = `${this.config.ui.buttonText}`;
636 | }
637 |
638 | /**
639 | * @method _injectFacebookSdk
640 | * @description Inject Facebook SDK
641 | * @private
642 | */
643 | _injectFacebookSdk() {
644 | if (!window.FB && this.config.networks.facebook.appId &&
645 | !this.el.body.querySelector('#fb-root')) {
646 | let script = document.createElement('script');
647 | script.text = `window.fbAsyncInit=function(){FB.init({appId:'${this.config.networks.facebook.appId}',status:true,xfbml:true})};(function(e,t,n){var r,i=e.getElementsByTagName(t)[0];if (e.getElementById(n)){return}r=e.createElement(t);r.id=n;r.src='//connect.facebook.net/en_US/all.js';i.parentNode.insertBefore(r,i)})(document,'script','facebook-jssdk');`;
648 |
649 | let fbRoot = document.createElement('div');
650 | fbRoot.id = 'fb-root';
651 |
652 | this.el.body.appendChild(fbRoot);
653 | this.el.body.appendChild(script);
654 | }
655 | }
656 |
657 | /**
658 | * @method _hook
659 | * @description Hook helper function
660 | * @private
661 | *
662 | * @param {String} type
663 | * @param {String} network
664 | * @param {DOMNode} instance
665 | */
666 | _hook(type, network, instance) {
667 | let fn = this.config.networks[network][type];
668 |
669 | if (typeof fn === 'function') {
670 | let opts = fn.call(this.config.networks[network], instance);
671 |
672 | if (opts !== undefined) {
673 | opts = this._normalizeFilterConfigUpdates(opts);
674 | this.extend(this.config.networks[network], opts, true);
675 | this._normalizeNetworkConfiguration();
676 | }
677 | }
678 | }
679 |
680 | /**
681 | * @method _defaultTitle
682 | * @description Gets default title
683 | * @private
684 | *
685 | * @returns {String}
686 | */
687 | _defaultTitle() {
688 | let content;
689 | if ((content = (document.querySelector('meta[property="og:title"]') ||
690 | document.querySelector('meta[name="twitter:title"]'))))
691 | return content.getAttribute('content');
692 | else if ((content = document.querySelector('title')))
693 | return content.textContent || content.innerText;
694 | }
695 |
696 | /**
697 | * @method _defaultImage
698 | * @description Gets default image
699 | * @private
700 | *
701 | * @returns {String}
702 | */
703 | _defaultImage() {
704 | let content;
705 | if ((content = (document.querySelector('meta[property="og:image"]') ||
706 | document.querySelector('meta[name="twitter:image"]'))))
707 | return content.getAttribute('content');
708 | }
709 |
710 | /**
711 | * @method _defaultDescription
712 | * @description Gets default description
713 | * @private
714 | *
715 | * @returns {String}
716 | */
717 | _defaultDescription() {
718 | let content;
719 | if ((content = (document.querySelector('meta[property="og:description"]') ||
720 | document.querySelector('meta[name="twitter:description"]') ||
721 | document.querySelector('meta[name="description"]'))))
722 | return content.getAttribute('content');
723 | else
724 | return '';
725 | }
726 |
727 | /**
728 | * @method _detectNetworks
729 | * @description Detect number of networks in use and display/hide
730 | * @private
731 | */
732 | _detectNetworks() {
733 | // Update network-specific configuration with global configurations
734 | for (let network of Object.keys(this.config.networks)) {
735 | let display;
736 | for (let option of Object.keys(this.config.networks[network])) {
737 | if (this.config.networks[network][option] === null) {
738 | this.config.networks[network][option] = this.config[option];
739 | }
740 | }
741 |
742 | // Check for enabled networks and display them
743 | if (this.config.networks[network].enabled) {
744 | this.class = 'enabled';
745 | this.config.enabledNetworks += 1;
746 | }
747 | else
748 | this.class = 'disabled';
749 |
750 | this.config.networks[network].class = this.class;
751 | }
752 | }
753 |
754 | /**
755 | * @method _normalizeNetworkConfiguration
756 | * @description Normalizes network configuration for Facebook & Twitter
757 | * @private
758 | */
759 | _normalizeNetworkConfiguration() {
760 | // Don't load FB SDK if FB appId isn't present
761 | if (!this.config.networks.facebook.appId)
762 | this.config.networks.facebook.loadSdk = false;
763 |
764 | // Encode Twitter description for URL
765 | if (!!this.config.networks.twitter.description)
766 | if (!this._isEncoded(this.config.networks.twitter.description))
767 | this.config.networks.twitter.description =
768 | encodeURIComponent(this.config.networks.twitter.description);
769 |
770 | // Typecast Facebook appId to a String
771 | if (typeof this.config.networks.facebook.appId === 'number')
772 | this.config.networks.facebook.appId =
773 | this.config.networks.facebook.appId.toString();
774 | }
775 |
776 | /**
777 | * @method _normalizeFilterConfigUpdates
778 | * @description Normalizes Facebook config
779 | * @private
780 | *
781 | * @param {Object} opts
782 | * @returns {Object}
783 | */
784 | _normalizeFilterConfigUpdates(opts) {
785 | if (this.config.networks.facebook.appId !== opts.appId) {
786 | console.warn('You are unable to change the Facebook appId after the button has been initialized. Please update your Facebook filters accordingly.');
787 | delete(opts.appId);
788 | }
789 |
790 | if (this.config.networks.facebook.loadSdk !== opts.loadSdk) {
791 | console.warn('You are unable to change the Facebook loadSdk option after the button has been initialized. Please update your Facebook filters accordingly.');
792 | delete(opts.appId);
793 | }
794 |
795 | return opts;
796 | }
797 | }
798 |
799 | module.exports = ShareButton;
800 |
--------------------------------------------------------------------------------
/src/share-button.styl:
--------------------------------------------------------------------------------
1 | @import url('//fonts.googleapis.com/css?family=Lato');
2 |
3 | network($amt)
4 | $width = ($amt * 60)px
5 |
6 | &.sb-center
7 | if ($width >= 480px)
8 | @media screen and (max-width: 520px)
9 | white-space: initial
10 | text-align: center
11 | width: 420px
12 |
13 | if ($width >= 380px)
14 | @media screen and (max-width: 460px)
15 | white-space: initial
16 | text-align: center
17 | width: 360px
18 |
19 | if ($width >= 320px)
20 | @media screen and (max-width: 400px)
21 | white-space: initial
22 | text-align: center
23 | width: 300px
24 |
25 | socialIcon($name)
26 | li[class*={"'" + $name + "'"}]
27 | background: convert($name)
28 |
29 | &:before
30 | background-image: svg($name, '[fill]: #fff')
31 |
32 | &:after
33 | background-image: svg($name, '[fill]: #000')
34 |
35 | networks = 'email' 'facebook' 'googlePlus' 'linkedin' 'pinterest' 'reddit' 'twitter' 'whatsapp'
36 |
37 | email = #42C5B0
38 | facebook = #3B5998
39 | googlePlus = #E34429
40 | linkedin = #4875B4
41 | pinterest = #C5282F
42 | reddit = #a1caf2
43 | twitter = #6CDFEA
44 | whatsapp = #4DC247
45 | label-bg = #a29baa
46 | label-color = #333333
47 |
48 | share-button
49 | position: relative
50 | font-size: 16px
51 | color: label-color
52 | background: label-bg
53 | padding: 5px 10px 5px 1.75em
54 | border-radius: 5px
55 | font-family: Lato, sans-serif
56 | font-weight: 800
57 | -webkit-font-smoothing: antialiased
58 | cursor: pointer
59 | white-space: nowrap
60 | transition()
61 | upcase()
62 |
63 | &:hover
64 | color: rgba(label-color, 0.8)
65 | background: rgba(label-bg, 0.8)
66 |
67 | &:before
68 | position: absolute
69 | line-height: 1em
70 | left: 0.6em
71 | width: 1em
72 | height: 1em
73 | content: ' '
74 | background: svg("export", "[fill]: #000") no-repeat
75 |
76 | .sb-social
77 | position: absolute
78 | opacity: 0
79 | visibility: hidden
80 | transition: all 0.4s ease
81 |
82 | &.sb-center
83 | left: 50%
84 |
85 | &.sb-top
86 | top: 0
87 | transform: translate(-50%, -100%)
88 |
89 | &.sb-bottom
90 | bottom: 0
91 | transform: translate(-50%, 100%)
92 |
93 | &.active
94 | &.sb-top
95 | top: -1em
96 |
97 | &.sb-bottom
98 | bottom: -1em
99 |
100 | &.sb-left
101 | left: 50%
102 |
103 | &.sb-top
104 | top: 0
105 | transform: translate(calc(-100% + 30px), -100%)
106 |
107 | &.sb-middle
108 | top: 50%
109 | left: 0
110 | transform: translate(-100%, -50%)
111 |
112 | &.sb-bottom
113 | bottom: 0
114 | transform: translate(calc(-100% + 30px), 100%)
115 |
116 | &.active
117 | &.sb-top
118 | top: -1em
119 |
120 | &.sb-middle
121 | left: -1em
122 |
123 | &.sb-bottom
124 | bottom: -1em
125 |
126 | &.sb-right
127 | left: 50%
128 |
129 | &.sb-top
130 | top: 0
131 | transform: translate(-30px, -100%)
132 |
133 | &.sb-middle
134 | top: 50%
135 | left: 100%
136 | transform: translate(0, -50%)
137 |
138 | &.sb-bottom
139 | bottom: 0
140 | transform: translate(-30px, 100%)
141 |
142 | &.active
143 | &.sb-top
144 | top: -1em
145 |
146 | &.sb-middle
147 | left: calc(100% + 1em)
148 |
149 | &.sb-bottom
150 | bottom: -1em
151 |
152 | &.active
153 | opacity: 1
154 | transition: all .4s ease
155 | visibility: visible
156 |
157 | &.load
158 | transition: none !important
159 |
160 | for num in (1..8)
161 | &.networks-{num}
162 | network(num)
163 |
164 | ul
165 | margin: 0
166 | padding: 0
167 | list-style: none
168 | line-height: 0
169 |
170 | li
171 | position: relative
172 | height: 22px
173 | width: 60px
174 | padding: 12px 0
175 | margin: 0
176 | text-align: center
177 | font-size: 20px
178 | cursor: pointer
179 | z-index: 2
180 | box-sizing: content-box
181 | transition()
182 |
183 | &.enabled
184 | display: inline-block
185 |
186 | &.disabled
187 | display: none
188 |
189 | &:hover
190 | &:before
191 | opacity: 0
192 |
193 | &:after
194 | opacity: .5
195 |
196 | &:before, &:after
197 | content: ' '
198 | position: absolute
199 | width: inherit
200 | height: inherit
201 | transform: translate(-20%, 0)
202 | transition()
203 | background-repeat: no-repeat !important
204 |
205 | &:before
206 | opacity: 1
207 |
208 | &:after
209 | opacity: 0
210 |
211 | a
212 | position: absolute
213 | top: 0
214 | left: 0
215 | width: 100%
216 | height: 100%
217 | z-index: 3
218 |
219 | for name in networks
220 | socialIcon(name)
221 |
222 |
--------------------------------------------------------------------------------
/src/share-utils.js:
--------------------------------------------------------------------------------
1 | import StringUtils from './string-utils';
2 |
3 | /**
4 | * ShareUtils
5 | * @class
6 | * @classdesc A nice set of utilities.
7 | */
8 | class ShareUtils {
9 | _getStyle(ele, css) {
10 | var strValue = "";
11 |
12 | if (document.defaultView && document.defaultView.getComputedStyle) {
13 | strValue = document.defaultView.getComputedStyle(ele, "")
14 | .getPropertyValue(css);
15 | } else if (ele.currentStyle) {
16 | css = css.replace(/\-(\w)/g, function (strMatch, p1) {
17 | return p1.toUpperCase();
18 | });
19 | strValue = ele.currentStyle[css];
20 | }
21 |
22 | return strValue;
23 | }
24 |
25 | /**
26 | * @method _hide
27 | * @description Change element's display to 'none'
28 | * @private
29 | *
30 | * @param {DOMNode} el
31 | */
32 | _hide(el) {
33 | el.style.display = "none";
34 | }
35 |
36 | /**
37 | * @method _show
38 | * @description Change element's display to 'block'
39 | * @private
40 | *
41 | * @param {DOMNode} el
42 | */
43 | _show(el) {
44 | el.style.display = "initial";
45 | }
46 |
47 | /**
48 | * @method _hasClass
49 | * @description Wrapper to see if an element contains a class.
50 | * @private
51 | *
52 | * @param {DOMNode} el
53 | * @param {String} className
54 | * @returns {Boolean}
55 | */
56 | _hasClass(el, className) {
57 | return el.classList.contains(className);
58 | }
59 |
60 | /**
61 | * @method addClass
62 | * @description Wrapper to add class to element.
63 | * @private
64 | *
65 | * @param {DOMNode} el
66 | * @param {String} className
67 | */
68 | _addClass(el, className) {
69 | el.classList.add(className);
70 | }
71 |
72 | /**
73 | * @method removeClass
74 | * @description Wrapper to remove class from element.
75 | * @private
76 | *
77 | * @param {DOMNode} el
78 | * @param {String} className
79 | */
80 | _removeClass(el, className) {
81 | el.classList.remove(className);
82 | }
83 |
84 | /**
85 | * @method _isEncoded
86 | * @description Wrapper to check if the string is encoded.
87 | * @private
88 | *
89 | * @param {String} str
90 | * @param {Boolean}
91 | */
92 | _isEncoded(str) {
93 | str = StringUtils.toRFC3986(str);
94 | return decodeURIComponent(str) !== str;
95 | }
96 |
97 | /**
98 | * @method _encode
99 | * @description Wrapper to _encode a string if the string isn't already encoded.
100 | * @private
101 | *
102 | * @param {DOMNode} el
103 | * @param {String} className
104 | */
105 | _encode(str) {
106 | if (typeof str === 'undefined' || str === null || this._isEncoded(str))
107 | return encodeURIComponent(str);
108 | else
109 | return StringUtils.toRFC3986(str);
110 | }
111 |
112 | /**
113 | * @method _getUrl
114 | * @description Returns the correct share URL based off of the incoming
115 | * URL and parameters given
116 | * @private
117 | *
118 | * @param {String} url
119 | * @param {boolean} encode
120 | * @param {Object} params
121 | */
122 | _getUrl(url, encode=false, params={}) {
123 | let qs = (() => {
124 | let results = [];
125 | for (let k of Object.keys(params)) {
126 | let v = params[k];
127 | results.push(`${k}=${this._encode(v)}`);
128 | }
129 | return results.join('&');
130 | })();
131 |
132 | if (qs) qs = `?${qs}`;
133 |
134 | return url + qs;
135 | }
136 |
137 | /**
138 | * @method _updateHref
139 | * @description Makes the elements a tag have a href of the popup link and
140 | * as pops up the share window for the element
141 | * @private
142 | *
143 | * @param {DOMNode} element
144 | * @param {String} url
145 | * @param {Object} params
146 | */
147 | _updateHref(element, url, params) {
148 | let encode = url.indexOf('mailto:') >= 0;
149 | let a = element.getElementsByTagName('a')[0];
150 | a.setAttribute('href', this._getUrl(url, !encode, params));
151 | if(!encode && (!this.config.networks.facebook.loadSdk || element.getAttribute('class') !== 'facebook')) {
152 | let popup = {
153 | width: 500,
154 | height: 350
155 | };
156 |
157 | popup.top = (screen.height / 2) - (popup.height / 2);
158 | popup.left = (screen.width / 2) - (popup.width / 2);
159 |
160 | window.open(
161 | a.href,
162 | 'targetWindow', `
163 | toolbar=no,
164 | location=no,
165 | status=no,
166 | menubar=no,
167 | scrollbars=yes,
168 | resizable=yes,
169 | left=${popup.left},
170 | top=${popup.top},
171 | width=${popup.width},
172 | height=${popup.height}
173 | `
174 | );
175 | }
176 | }
177 |
178 | /**
179 | * @method popup
180 | * @description Create a window for specified network
181 | *
182 | * @param {String} url
183 | * @param {Object} params
184 | */
185 | popup(url, params={}) {
186 | let popup = {
187 | width: 500,
188 | height: 350
189 | };
190 |
191 | popup.top = (screen.height / 2) - (popup.height / 2);
192 | popup.left = (screen.width / 2) - (popup.width / 2);
193 |
194 | let qs = (() => {
195 | let results = [];
196 | for (let k of Object.keys(params)) {
197 | let v = params[k];
198 | results.push(`${k}=${this._encode(v)}`);
199 | }
200 | return results.join('&');
201 | })();
202 |
203 | if (qs) qs = `?${qs}`;
204 |
205 | // This does work even though it contains \n once converted.
206 | window.open(
207 | url+qs,
208 | 'targetWindow', `
209 | toolbar=no,
210 | location=no,
211 | status=no,
212 | menubar=no,
213 | scrollbars=yes,
214 | resizable=yes,
215 | left=${popup.left},
216 | top=${popup.top},
217 | width=${popup.width},
218 | height=${popup.height}
219 | `
220 | );
221 | }
222 |
223 | /**
224 | * @method _merge
225 | * @description Combines two (or more) objects, giving the last one precedence
226 | * @author svlasov-gists
227 | * [Original Gist]{@link https://gist.github.com/svlasov-gists/2383751}
228 | *
229 | * @param {Object} target
230 | * @param {Object} source
231 | * @return {Object} target
232 | */
233 | _merge(target, source) {
234 | if (typeof target !== 'object') target = {};
235 |
236 | for (let property in source) {
237 | if (source.hasOwnProperty(property)) {
238 | let sourceProperty = source[property];
239 |
240 | if (typeof sourceProperty === 'object') {
241 | target[property] = this._merge(target[property], sourceProperty);
242 | continue;
243 | }
244 |
245 | target[property] = sourceProperty;
246 | }
247 | }
248 |
249 | for (let a = 2, l = arguments.length; a < l; a++)
250 | _merge(target, arguments[a]);
251 |
252 | return target;
253 | }
254 |
255 | /**
256 | * @method _objectToArray
257 | * @description Takes an Object and converts it into an array of Objects. This is used when converting a list of DOMNodes into an array.
258 | *
259 | * @param {Object} obj
260 | * @returns {Array} arr
261 | */
262 | _objToArray(obj) {
263 | let arr = [];
264 |
265 | for (let k in obj)
266 | if (typeof obj[k] === 'object') arr.push(obj[k]);
267 |
268 | return arr;
269 | }
270 |
271 | /**
272 | * @method _isMobile
273 | * @description Returns true if current device is mobile (or PhantomJS for
274 | * testing purposes), and false otherwise
275 | * @author kriskbx
276 | * [Original Gist] {@link https://github.com/kriskbx/whatsapp-sharing/blob/master/src/button.js}
277 | * @private
278 | */
279 | _isMobile() {
280 | return navigator.userAgent.match(/Android|iPhone|PhantomJS/i) &&
281 | !navigator.userAgent.match(/iPod|iPad/i);
282 | }
283 | }
284 |
285 | export default ShareUtils;
286 |
--------------------------------------------------------------------------------
/src/string-utils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * StringUtils
3 | * @class
4 | * @classdesc String utilities.
5 | */
6 | class StringUtils {
7 | /**
8 | * @method toRFC3986
9 | * @description Encodes the string in RFC3986
10 | *
11 | * @param {String}
12 | * @return {String}
13 | */
14 | static toRFC3986(s) {
15 | let tmp = encodeURIComponent(s);
16 | tmp.replace(/[!'()*]/g, function(c) {
17 | return `%${c.charCodeAt(0).toString(16)}`;
18 | });
19 | }
20 |
21 | /**
22 | * @method capFLetter
23 | * @description Returns a capitalized version of the string
24 | *
25 | * @param {String}
26 | * @return {String}
27 | */
28 | static capFLetter(s) {
29 | return s.charAt(0).toUpperCase() + s.slice(1);
30 | }
31 | }
32 |
33 | export default StringUtils;
34 |
--------------------------------------------------------------------------------
/src/svg/email.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/svg/export.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/svg/facebook.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/svg/googlePlus.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/svg/linkedin.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/svg/pinterest.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/svg/reddit.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/svg/twitter.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/svg/whatsapp.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/tests/features/animation.feature:
--------------------------------------------------------------------------------
1 | Feature: Collision Detection
2 |
3 | Background:
4 | Given I create an Animated Share Button
5 |
6 | @animation
7 | Scenario: Classes will be porperly adjusted according to Share Button position
8 | Then The classes will be correct at middle center
9 | Then The classes will be correct at bottom left
10 | Then The classes will be correct at middle left
11 | Then The classes will be correct at top left
12 | Then The classes will be correct at top center
13 | Then The classes will be correct at top right
14 | Then The classes will be correct at middle right
15 | Then The classes will be correct at bottom right
16 | Then The classes will be correct at bottom center
17 |
--------------------------------------------------------------------------------
/tests/features/basic.feature:
--------------------------------------------------------------------------------
1 | Feature: Basic Button
2 |
3 | Background:
4 | Given I create a basic Share Button
5 |
6 | @basic
7 | Scenario: All social networks should be displayed
8 | When I click the Share Button
9 | Then I should see all Social Networks
10 |
--------------------------------------------------------------------------------
/tests/features/email.feature:
--------------------------------------------------------------------------------
1 | Feature: Email Network
2 |
3 | Background:
4 | Given I create an Email Share Button
5 |
6 | @email
7 | Scenario: Email network should be displayed and have the correct URL
8 | When I click the Email Share Button
9 | Then I should see the Email button
10 | When I click the Email button
11 | Then I should have a correct Email share url
12 |
--------------------------------------------------------------------------------
/tests/features/facebook.feature:
--------------------------------------------------------------------------------
1 | Feature: Facebook Network
2 |
3 | Background:
4 | Given I create a Facebook Share Button with SDK enabled
5 |
6 | @facebook
7 | Scenario: Facebook network should be displayed and have no url
8 | When I click the Facebook Share Button
9 | Then I should see the Facebook button
10 | When I click the Facebook button
11 | Then I should have no url
12 |
13 | @facebook
14 | Scenario: Facebook network should be displayed and have the fallback URL
15 | When I click the Facebook Share Button
16 | Then I should see the Facebook button
17 | When the FB SDK is not loaded
18 | And I click the Facebook button
19 | Then I should have a PHP Facebook share url
20 |
21 | Background:
22 | Given I create a Facebook Share Button with SDK disabled
23 |
24 | @facebook
25 | Scenario: Facebook network should be displayed and have the fallback URL
26 | When I click the Facebook Share Button
27 | Then I should see the Facebook button
28 | When I click the Facebook button
29 | Then I should have a PHP Facebook share url
30 |
--------------------------------------------------------------------------------
/tests/features/googleplus.feature:
--------------------------------------------------------------------------------
1 | Feature: Google Plus Network
2 |
3 | Background:
4 | Given I create a Google Plus Share Button
5 |
6 | @googleplus
7 | Scenario: Google Plus network should be displayed and have the correct URL
8 | When I click the Google Plus Share Button
9 | Then I should see the Google Plus button
10 | When I click the Google Plus button
11 | Then I should have a correct Google Plus share url
12 |
--------------------------------------------------------------------------------
/tests/features/linkedin.feature:
--------------------------------------------------------------------------------
1 | Feature: Linkedin Network
2 |
3 | Background:
4 | Given I create a Linkedin Share Button
5 |
6 | @linkedin
7 | Scenario: Linkedin network should be displayed and have the correct URL
8 | When I click the Linkedin Share Button
9 | Then I should see the Linkedin button
10 | When I click the Linkedin button
11 | Then I should have a correct Linkedin share url
12 |
--------------------------------------------------------------------------------
/tests/features/meta.feature:
--------------------------------------------------------------------------------
1 | Feature: Meta Tag Inheritance
2 |
3 | Background:
4 | Given I create and click a meta tag Share Button
5 |
6 | @meta
7 | Scenario: Network URLs should inherit meta tag properties
8 | When I click all the network buttons
9 | Then All buttons should have valid URLs
10 |
--------------------------------------------------------------------------------
/tests/features/ordering.feature:
--------------------------------------------------------------------------------
1 | Feature: Network Ordering
2 |
3 | Background:
4 | Given I create a network ordering Share Button
5 |
6 | @ordering
7 | Scenario: The networks should be displayed in the correct order
8 | When I click the network ordering Share Button
9 | Then I should see the correct number of networks
10 | And They should be in the correct order
11 |
--------------------------------------------------------------------------------
/tests/features/pinterest.feature:
--------------------------------------------------------------------------------
1 | Feature: Pinterest Network
2 |
3 | Background:
4 | Given I create a Pinterest Share Button
5 |
6 | @pinterest
7 | Scenario: Pinterest network should be displayed and have the correct URL
8 | When I click the Pinterest Share Button
9 | Then I should see the Pinterest button
10 | When I click the Pinterest button
11 | Then I should have a correct Pinterest share url
12 |
--------------------------------------------------------------------------------
/tests/features/reddit.feature:
--------------------------------------------------------------------------------
1 | Feature: Reddit Network
2 |
3 | Background:
4 | Given I create a Reddit Share Button
5 |
6 | @reddit
7 | Scenario: Reddit network should be displayed and have the correct URL
8 | When I click the Reddit Share Button
9 | Then I should see the Reddit button
10 | When I click the Reddit button
11 | Then I should have a correct Reddit share url
12 |
--------------------------------------------------------------------------------
/tests/features/twitter.feature:
--------------------------------------------------------------------------------
1 | Feature: Twitter Network
2 |
3 | Background:
4 | Given I create a Twitter Share Button
5 |
6 | @twitter
7 | Scenario: Twitter network should be displayed and have the correct URL
8 | When I click the Twitter Share Button
9 | Then I should see the Twitter button
10 | When I click the Twitter button
11 | Then I should have a correct Twitter share url
12 |
--------------------------------------------------------------------------------
/tests/features/ui.feature:
--------------------------------------------------------------------------------
1 | Feature: UI Options
2 |
3 | Background:
4 | Given I create a UI Share Button
5 |
6 | @ui
7 | Scenario: User UI options should be implemented
8 | Then The Share Button should have the correct text
9 | And The buttons should have the correct classes
10 |
--------------------------------------------------------------------------------
/tests/features/whatsapp.feature:
--------------------------------------------------------------------------------
1 | Feature: Whatsapp Network
2 |
3 | Background:
4 | Given I create a Whatsapp Share Button
5 |
6 | @whatsapp
7 | Scenario: Whatsapp network should be displayed and have the correct URL
8 | When I click the Whatsapp Share Button
9 | Then I should see the Whatsapp button
10 | When I click the Whatsapp button
11 | Then I should have a correct Whatsapp share url
12 |
--------------------------------------------------------------------------------
/tests/fixtures/animation.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Share Button Test: Animation
5 |
6 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/tests/fixtures/basic.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Share Button Test: Basic
5 |
6 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/tests/fixtures/email.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Share Button Test: Email
5 |
6 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/tests/fixtures/facebook.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Share Button Test: Facebook
5 |
6 |
21 |
22 |
23 |
24 |
25 |
26 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/tests/fixtures/facebook_no_sdk.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Share Button Test: Facebook
5 |
6 |
21 |
22 |
23 |
24 |
25 |
26 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/tests/fixtures/googleplus.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Share Button Test: Google Plus
5 |
6 |
21 |
22 |
23 |
24 |
25 |
26 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/tests/fixtures/linkedin.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Share Button Test: Linkedin
5 |
6 |
21 |
22 |
23 |
24 |
25 |
26 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/tests/fixtures/meta.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Share Button Test: Meta Tag Networks
5 |
6 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/tests/fixtures/ordering.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Share Button Test: Ordering
5 |
6 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/tests/fixtures/pinterest.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Share Button Test: Pinterest
5 |
6 |
21 |
22 |
23 |
24 |
25 |
26 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/tests/fixtures/reddit.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Share Button Test: Reddit
5 |
6 |
21 |
22 |
23 |
24 |
25 |
26 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/tests/fixtures/twitter.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Share Button Test: Twitter
5 |
6 |
7 |
22 |
23 |
24 |
25 |
26 |
27 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/tests/fixtures/ui.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Share Button Test: UI
5 |
6 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/tests/fixtures/whatsapp.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Share Button Test: Whatsapp
5 |
6 |
21 |
22 |
23 |
24 |
25 |
26 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/tests/helpers/helpers.coffee:
--------------------------------------------------------------------------------
1 | path = require 'path'
2 |
3 | module.exports = ->
4 |
5 | @Helpers = @Helpers || {}
6 |
7 | @Helpers.fixture = (name) ->
8 | fixtureBase = path.join("tests/fixtures", name)
9 | return "http://localhost:8000/" + fixtureBase + '.html'
10 |
--------------------------------------------------------------------------------
/tests/steps/animation.coffee:
--------------------------------------------------------------------------------
1 | module.exports = ->
2 | @Given /^I create an Animated Share Button$/, () ->
3 | @driver.visit("file:///#{process.cwd()}/tests/fixtures/animation.html")
4 |
5 | @Then /^The classes will be correct at middle center$/, () ->
6 | shareButton = new @Widgets.ShareButton()
7 | social = new @Widgets.ShareButtonSocial()
8 | div = new @Widgets.SBDiv()
9 | shareButton.addClass('middle-center').then ->
10 | social.getAttribute('class').then (classList) ->
11 | (classList.indexOf('sb-top') >= 0 &&
12 | classList.indexOf('sb-center') >=0).should.be.true
13 |
14 | @Then /^The classes will be correct at bottom left$/, () ->
15 | shareButton = new @Widgets.ShareButton()
16 | social = new @Widgets.ShareButtonSocial()
17 | shareButton.removeClass('middle-center').then ->
18 | shareButton.addClass('bottom-left').then ->
19 | social.getAttribute('class').then (classList) ->
20 | (classList.indexOf('sb-top') >= 0 &&
21 | classList.indexOf('sb-right') >= 0 &&
22 | classList.indexOf('sb-center') < 0).should.be.true
23 |
24 | @Then /^The classes will be correct at middle left$/, () ->
25 | shareButton = new @Widgets.ShareButton()
26 | social = new @Widgets.ShareButtonSocial()
27 | shareButton.removeClass('bottom-left').then ->
28 | shareButton.addClass('middle-left').then ->
29 | social.getAttribute('class').then (classList) ->
30 | (classList.indexOf('sb-middle') >= 0 &&
31 | classList.indexOf('sb-right') >=0 &&
32 | classList.indexOf('sb-top') < 0).should.be.true
33 |
34 | @Then /^The classes will be correct at top left$/, () ->
35 | shareButton = new @Widgets.ShareButton()
36 | social = new @Widgets.ShareButtonSocial()
37 | shareButton.removeClass('middle-left').then ->
38 | shareButton.addClass('top-left').then ->
39 | social.getAttribute('class').then (classList) ->
40 | (classList.indexOf('sb-bottom') >= 0 &&
41 | classList.indexOf('sb-right') >=0 &&
42 | classList.indexOf('sb-middle') < 0).should.be.true
43 |
44 | @Then /^The classes will be correct at top center$/, () ->
45 | shareButton = new @Widgets.ShareButton()
46 | social = new @Widgets.ShareButtonSocial()
47 | shareButton.removeClass('top-left').then ->
48 | shareButton.addClass('top-center').then ->
49 | social.getAttribute('class').then (classList) ->
50 | (classList.indexOf('sb-bottom') >= 0 &&
51 | classList.indexOf('sb-center') >=0 &&
52 | classList.indexOf('sb-right') < 0).should.be.true
53 |
54 | @Then /^The classes will be correct at top right$/, () ->
55 | shareButton = new @Widgets.ShareButton()
56 | social = new @Widgets.ShareButtonSocial()
57 | shareButton.removeClass('top-center').then ->
58 | shareButton.addClass('top-right').then ->
59 | social.getAttribute('class').then (classList) ->
60 | (classList.indexOf('sb-bottom') >= 0 &&
61 | classList.indexOf('sb-left') >=0 &&
62 | classList.indexOf('sb-center') < 0).should.be.true
63 |
64 | @Then /^The classes will be correct at middle right$/, () ->
65 | shareButton = new @Widgets.ShareButton()
66 | social = new @Widgets.ShareButtonSocial()
67 | shareButton.removeClass('top-right').then ->
68 | shareButton.addClass('middle-right').then ->
69 | social.getAttribute('class').then (classList) ->
70 | (classList.indexOf('sb-middle') >= 0 &&
71 | classList.indexOf('sb-left') >=0 &&
72 | classList.indexOf('sb-bottom') < 0).should.be.true
73 |
74 | @Then /^The classes will be correct at bottom right$/, () ->
75 | shareButton = new @Widgets.ShareButton()
76 | social = new @Widgets.ShareButtonSocial()
77 | shareButton.removeClass('middle-right').then ->
78 | shareButton.addClass('bottom-right').then ->
79 | social.getAttribute('class').then (classList) ->
80 | (classList.indexOf('sb-top') >= 0 &&
81 | classList.indexOf('sb-left') >=0 &&
82 | classList.indexOf('sb-middle') < 0).should.be.true
83 |
84 | @Then /^The classes will be correct at bottom center$/, () ->
85 | shareButton = new @Widgets.ShareButton()
86 | social = new @Widgets.ShareButtonSocial()
87 | shareButton.removeClass('bottom-right').then ->
88 | shareButton.addClass('bottom-center').then ->
89 | social.getAttribute('class').then (classList) ->
90 | (classList.indexOf('sb-top') >= 0 &&
91 | classList.indexOf('sb-center') >=0 &&
92 | classList.indexOf('sb-left') < 0).should.be.true
93 |
--------------------------------------------------------------------------------
/tests/steps/basic.coffee:
--------------------------------------------------------------------------------
1 | networks = [
2 | 'pinterest'
3 | 'twitter'
4 | 'facebook'
5 | 'whatsapp'
6 | 'gplus'
7 | 'reddit'
8 | 'linkedin'
9 | 'paper-plane'
10 | ]
11 |
12 | module.exports = ->
13 | @Given /^I create a basic Share Button$/, ->
14 | @driver.visit("file:///#{process.cwd()}/tests/fixtures/basic.html")
15 |
16 | @When /^I click the Share Button$/, ->
17 | new @Widgets.ShareButton().click()
18 |
19 | @Then /^I should see all Social Networks$/, ->
20 | new @Widgets
21 | .ShareButtonNetworks()
22 | .each (item, i) ->
23 | unless (item.hasClass('whatsapp').then (tf) -> tf)
24 | item.isVisible().should.eventually.eql(true)
25 |
--------------------------------------------------------------------------------
/tests/steps/email.coffee:
--------------------------------------------------------------------------------
1 | module.exports = ->
2 | @Given /^I create an Email Share Button$/, () ->
3 | @driver.visit("file:///#{process.cwd()}/tests/fixtures/email.html")
4 |
5 | @When /^I click the Email Share Button$/, () ->
6 | new @Widgets.ShareButton().click()
7 |
8 | @Then /^I should see the Email button$/, () ->
9 | new @Widgets
10 | .ShareButtonNetworks()
11 | .filter( (item) ->
12 | item.hasClass('email')
13 | .then (class1) ->
14 | return class1
15 | .then (class1) ->
16 | item.hasClass('enabled')
17 | .then (class2) ->
18 | return (class1 && class2)
19 | )
20 | .should.eventually.have.length(1)
21 |
22 | @When /^I click the Email button$/, () ->
23 | new @Widgets
24 | .ShareButtonNetworks()
25 | .filter( (item) ->
26 | item.hasClass('email')
27 | )
28 | .then (list) ->
29 | list[0].click('a')
30 |
31 | @Then /^I should have a correct Email share url$/, () ->
32 | new @Widgets
33 | .ShareButtonNetworks()
34 | .filter( (item) ->
35 | item.hasClass('email')
36 | )
37 | .then (list) ->
38 | list[0].getAttribute(
39 | selector: 'a',
40 | attribute: 'href'
41 | ).should.eventually.eql('mailto:?subject=email%20title&body=email%20description')
42 |
--------------------------------------------------------------------------------
/tests/steps/facebook.coffee:
--------------------------------------------------------------------------------
1 | module.exports = ->
2 | @Given /^I create a Facebook Share Button with SDK enabled$/, () ->
3 | @driver.visit("http://localhost:8000/tests/fixtures/facebook.html")
4 |
5 | @Given /^I create a Facebook Share Button with SDK disabled$/, () ->
6 | @driver.visit("http://localhost:8000/tests/fixtures/facebook_no_sdk.html")
7 |
8 | @When /^I click the Facebook Share Button$/, () ->
9 | new @Widgets.ShareButton().click()
10 |
11 | @Then /^I should see the Facebook button$/, () ->
12 | new @Widgets
13 | .ShareButtonNetworks()
14 | .filter( (item) ->
15 | item.hasClass('facebook')
16 | .then (class1) ->
17 | return class1
18 | .then (class1) ->
19 | item.hasClass('enabled')
20 | .then (class2) ->
21 | return (class1 && class2)
22 | )
23 | .should.eventually.have.length(1)
24 |
25 | @When /^the FB SDK is not loaded$/, () ->
26 | # Simulate FB didn't load.
27 | new @Widgets.ShareButton().removeFacebookSDK()
28 |
29 | @When /^I click the Facebook button$/, () ->
30 | new @Widgets
31 | .ShareButtonNetworks()
32 | .filter( (item) ->
33 | item.hasClass('facebook')
34 | )
35 | .then (list) ->
36 | list[0].click('a')
37 |
38 | @Then /^I should have a PHP Facebook share url$/, () ->
39 | new @Widgets
40 | .ShareButtonNetworks()
41 | .filter( (item) ->
42 | item.hasClass('facebook')
43 | )
44 | .then (list) ->
45 | list[0].getAttribute(
46 | selector: 'a',
47 | attribute: 'href'
48 | ).should.eventually.eq('https://www.facebook.com/sharer/sharer.php?u=http%3A%2F%2Fwww.example.com')
49 |
50 | @Then /^I should have no url$/, () ->
51 | new @Widgets
52 | .ShareButtonNetworks()
53 | .filter( (item) ->
54 | item.hasClass('facebook')
55 | )
56 | .then (list) ->
57 | list[0].getAttribute(
58 | selector: 'a',
59 | attribute: 'href'
60 | ).should.eventually.eq(null)
61 |
--------------------------------------------------------------------------------
/tests/steps/googleplus.coffee:
--------------------------------------------------------------------------------
1 | module.exports = ->
2 | @Given /^I create a Google Plus Share Button$/, () ->
3 | @driver.visit("file:///#{process.cwd()}/tests/fixtures/googleplus.html")
4 |
5 | @When /^I click the Google Plus Share Button$/, () ->
6 | new @Widgets.ShareButton().click()
7 |
8 | @Then /^I should see the Google Plus button$/, () ->
9 | new @Widgets
10 | .ShareButtonNetworks()
11 | .filter( (item) ->
12 | item.hasClass('googlePlus')
13 | .then (class1) ->
14 | return class1
15 | .then (class1) ->
16 | item.hasClass('enabled')
17 | .then (class2) ->
18 | return (class1 && class2)
19 | )
20 | .should.eventually.have.length(1)
21 |
22 | @When /^I click the Google Plus button$/, () ->
23 | new @Widgets
24 | .ShareButtonNetworks()
25 | .filter( (item) ->
26 | item.hasClass('googlePlus')
27 | )
28 | .then (list) ->
29 | list[0].click('a')
30 |
31 | @Then /^I should have a correct Google Plus share url$/, () ->
32 | new @Widgets
33 | .ShareButtonNetworks()
34 | .filter( (item) ->
35 | item.hasClass('googlePlus')
36 | )
37 | .then (list) ->
38 | list[0].getAttribute(
39 | selector: 'a',
40 | attribute: 'href'
41 | ).should.eventually.eq('https://plus.google.com/share?url=http%3A%2F%2Fwww.example.com%2F')
42 |
--------------------------------------------------------------------------------
/tests/steps/linkedin.coffee:
--------------------------------------------------------------------------------
1 | module.exports = ->
2 | @Given /^I create a Linkedin Share Button$/, () ->
3 | @driver.visit("file:///#{process.cwd()}/tests/fixtures/linkedin.html")
4 |
5 | @When /^I click the Linkedin Share Button$/, () ->
6 | new @Widgets.ShareButton().click()
7 |
8 | @Then /^I should see the Linkedin button$/, () ->
9 | new @Widgets
10 | .ShareButtonNetworks()
11 | .filter( (item) ->
12 | item.hasClass('linkedin')
13 | .then (class1) ->
14 | return class1
15 | .then (class1) ->
16 | item.hasClass('enabled')
17 | .then (class2) ->
18 | return (class1 && class2)
19 | )
20 | .should.eventually.have.length(1)
21 |
22 | @When /^I click the Linkedin button$/, () ->
23 | new @Widgets
24 | .ShareButtonNetworks()
25 | .filter( (item) ->
26 | item.hasClass('linkedin')
27 | )
28 | .then (list) ->
29 | list[0].click('a')
30 |
31 | @Then /^I should have a correct Linkedin share url$/, () ->
32 | new @Widgets
33 | .ShareButtonNetworks()
34 | .filter( (item) ->
35 | item.hasClass('linkedin')
36 | )
37 | .then (list) ->
38 | list[0].getAttribute(
39 | selector: 'a',
40 | attribute: 'href'
41 | ).should.eventually.eq('https://www.linkedin.com/shareArticle?mini=true&url=http%3A%2F%2Fwww.example.com&title=linkedin%20title&summary=linkedin%20description')
42 |
--------------------------------------------------------------------------------
/tests/steps/meta.coffee:
--------------------------------------------------------------------------------
1 | hrefs = [
2 | 'https://www.pinterest.com/pin/create/button?url=http%3A%2F%2Flocalhost%3A8000%2Ftests%2Ffixtures%2Fmeta.html&media=http%3A%2F%2Fcarrot.is%2Fimg%2Ffb-share.jpg&description=test%20description'
3 | 'https://twitter.com/intent/tweet?text=test%20description&url=http%3A%2F%2Flocalhost%3A8000%2Ftests%2Ffixtures%2Fmeta.html'
4 | 'https://www.facebook.com/sharer/sharer.php?u=http%3A%2F%2Flocalhost%3A8000%2Ftests%2Ffixtures%2Fmeta.html'
5 | 'whatsapp://send?text=test%20description%20http%3A%2F%2Flocalhost%3A8000%2Ftests%2Ffixtures%2Fmeta.html'
6 | 'https://plus.google.com/share?url=http%3A%2F%2Flocalhost%3A8000%2Ftests%2Ffixtures%2Fmeta.html'
7 | 'http://www.reddit.com/submit?url=http%3A%2F%2Flocalhost%3A8000%2Ftests%2Ffixtures%2Fmeta.html&title=test%20title'
8 | 'https://www.linkedin.com/shareArticle?mini=true&url=http%3A%2F%2Flocalhost%3A8000%2Ftests%2Ffixtures%2Fmeta.html&title=test%20title&summary=test%20description'
9 | 'mailto:?subject=test%20title&body=test%20description'
10 | ]
11 |
12 | module.exports = ->
13 | @Given /^I create and click a meta tag Share Button$/, () ->
14 | @driver.visit("file:///#{process.cwd()}/tests/fixtures/meta.html")
15 | new @Widgets.ShareButton().click()
16 |
17 | @When /^I click all the network buttons$/, () ->
18 | new @Widgets
19 | .ShareButtonNetworks()
20 | .each (item, index) ->
21 | unless (item.hasClass('whatsapp').then (tf) -> return tf)
22 | item.click('a')
23 |
24 | @Then /^All buttons should have valid URLs$/, () ->
25 | new @Widgets
26 | .ShareButtonNetworks()
27 | .each (item, index) ->
28 | unless (item.hasClass('whatsapp').then (tf) -> return tf)
29 | item.getAttribute(
30 | selector: 'a',
31 | attribute: 'href'
32 | ).should.eventually.eq(hrefs[index])
33 |
--------------------------------------------------------------------------------
/tests/steps/ordering.coffee:
--------------------------------------------------------------------------------
1 | module.exports = ->
2 | @Given /^I create a network ordering Share Button$/, () ->
3 | @driver.visit("file:///#{process.cwd()}/tests/fixtures/ordering.html")
4 |
5 | @When /^I click the network ordering Share Button$/, () ->
6 | new @Widgets.ShareButton().click()
7 |
8 | @Then /^I should see the correct number of networks$/, () ->
9 | new @Widgets
10 | .ShareButtonNetworks()
11 | .filter( (item) ->
12 | item.hasClass('enabled')
13 | )
14 | .should.eventually.have.length(3)
15 |
16 | @Then /^They should be in the correct order$/, () ->
17 | new @Widgets
18 | .ShareButtonNetworks()
19 | .filter( (item) ->
20 | item.hasClass('enabled')
21 | )
22 | .then (list) ->
23 | list[0].hasClass('facebook').should.eventually.be.true
24 | list[1].hasClass('googlePlus').should.eventually.be.true
25 | list[2].hasClass('twitter').should.eventually.be.true
26 |
--------------------------------------------------------------------------------
/tests/steps/pinterest.coffee:
--------------------------------------------------------------------------------
1 | module.exports = ->
2 | @Given /^I create a Pinterest Share Button$/, () ->
3 | @driver.visit("file:///#{process.cwd()}/tests/fixtures/pinterest.html")
4 |
5 | @When /^I click the Pinterest Share Button$/, () ->
6 | new @Widgets.ShareButton().click()
7 |
8 | @Then /^I should see the Pinterest button$/, () ->
9 | new @Widgets
10 | .ShareButtonNetworks()
11 | .filter( (item) ->
12 | item.hasClass('pinterest')
13 | .then (class1) ->
14 | return class1
15 | .then (class1) ->
16 | item.hasClass('enabled')
17 | .then (class2) ->
18 | return (class1 && class2)
19 | )
20 | .should.eventually.have.length(1)
21 |
22 | @When /^I click the Pinterest button$/, () ->
23 | new @Widgets
24 | .ShareButtonNetworks()
25 | .filter( (item) ->
26 | item.hasClass('pinterest')
27 | )
28 | .then (list) ->
29 | list[0].click('a')
30 |
31 | @Then /^I should have a correct Pinterest share url$/, () ->
32 | new @Widgets
33 | .ShareButtonNetworks()
34 | .filter( (item) ->
35 | item.hasClass('pinterest')
36 | )
37 | .then (list) ->
38 | list[0].getAttribute(
39 | selector: 'a',
40 | attribute: 'href'
41 | ).should.eventually.eq('https://www.pinterest.com/pin/create/button?url=http%3A%2F%2Fwww.example.com&media=http%3A%2F%2Fcarrot.is%2Fimg%2Ffb-share.jpg&description=pinterest%20discription')
42 |
--------------------------------------------------------------------------------
/tests/steps/reddit.coffee:
--------------------------------------------------------------------------------
1 | module.exports = ->
2 | @Given /^I create a Reddit Share Button$/, () ->
3 | @driver.visit("file:///#{process.cwd()}/tests/fixtures/reddit.html")
4 |
5 | @When /^I click the Reddit Share Button$/, () ->
6 | new @Widgets.ShareButton().click()
7 |
8 | @Then /^I should see the Reddit button$/, () ->
9 | new @Widgets
10 | .ShareButtonNetworks()
11 | .filter( (item) ->
12 | item.hasClass('reddit')
13 | .then (class1) ->
14 | return class1
15 | .then (class1) ->
16 | item.hasClass('enabled')
17 | .then (class2) ->
18 | return (class1 && class2)
19 | )
20 | .should.eventually.have.length(1)
21 |
22 | @When /^I click the Reddit button$/, () ->
23 | new @Widgets
24 | .ShareButtonNetworks()
25 | .filter( (item) ->
26 | item.hasClass('reddit')
27 | )
28 | .then (list) ->
29 | list[0].click('a')
30 |
31 | @Then /^I should have a correct Reddit share url$/, () ->
32 | new @Widgets
33 | .ShareButtonNetworks()
34 | .filter( (item) ->
35 | item.hasClass('reddit')
36 | )
37 | .then (list) ->
38 | list[0].getAttribute(
39 | selector: 'a',
40 | attribute: 'href'
41 | ).should.eventually.eq('http://www.reddit.com/submit?url=http%3A%2F%2Fwww.example.com&title=reddit%20title')
42 |
--------------------------------------------------------------------------------
/tests/steps/twitter.coffee:
--------------------------------------------------------------------------------
1 | module.exports = ->
2 | @Given /^I create a Twitter Share Button$/, () ->
3 | @driver.visit("file:///#{process.cwd()}/tests/fixtures/twitter.html")
4 |
5 | @When /^I click the Twitter Share Button$/, () ->
6 | new @Widgets.ShareButton().click()
7 |
8 | @Then /^I should see the Twitter button$/, () ->
9 | new @Widgets
10 | .ShareButtonNetworks()
11 | .filter( (item) ->
12 | item.hasClass('twitter')
13 | .then (class1) ->
14 | return class1
15 | .then (class1) ->
16 | item.hasClass('enabled')
17 | .then (class2) ->
18 | return (class1 && class2)
19 | )
20 | .should.eventually.have.length(1)
21 |
22 | @When /^I click the Twitter button$/, () ->
23 | new @Widgets
24 | .ShareButtonNetworks()
25 | .filter( (item) ->
26 | item.hasClass('twitter')
27 | )
28 | .then (list) ->
29 | list[0].click('a')
30 |
31 | @Then /^I should have a correct Twitter share url$/, () ->
32 | new @Widgets
33 | .ShareButtonNetworks()
34 | .filter( (item) ->
35 | item.hasClass('twitter')
36 | )
37 | .then (list) ->
38 | list[0].getAttribute(
39 | selector: 'a',
40 | attribute: 'href'
41 | ).should.eventually.eq('https://twitter.com/intent/tweet?text=twitter%20description&url=http%3A%2F%2Fwww.example.com')
42 |
--------------------------------------------------------------------------------
/tests/steps/ui.coffee:
--------------------------------------------------------------------------------
1 | module.exports = ->
2 | @Given /^I create a UI Share Button$/, () ->
3 | @driver.visit("file:///#{process.cwd()}/tests/fixtures/ui.html")
4 |
5 | @Then /^The Share Button should have the correct text$/, () ->
6 | new @Widgets
7 | .ShareButton().getText()
8 | .should.eventually.eq('TEST TEXT')
9 |
10 | @Then /^The buttons should have the correct classes$/, () ->
11 | social = new @Widgets.ShareButtonTestSocial()
12 | social.hasClass('test-bottom').should.eventually.be.true
13 | social.hasClass('test-left').should.eventually.be.true
14 |
--------------------------------------------------------------------------------
/tests/steps/whatsapp.coffee:
--------------------------------------------------------------------------------
1 | module.exports = ->
2 | @Given /^I create a Whatsapp Share Button$/, () ->
3 | @driver.visit("file:///#{process.cwd()}/tests/fixtures/whatsapp.html")
4 |
5 | @When /^I click the Whatsapp Share Button$/, () ->
6 | new @Widgets.ShareButton().click()
7 |
8 | @Then /^I should see the Whatsapp button$/, () ->
9 | new @Widgets
10 | .ShareButtonNetworks()
11 | .filter( (item) ->
12 | item.hasClass('whatsapp')
13 | .then (class1) ->
14 | return class1
15 | .then (class1) ->
16 | item.hasClass('enabled')
17 | .then (class2) ->
18 | return (class1 && class2)
19 | )
20 | .should.eventually.have.length(1)
21 |
22 | @When /^I click the Whatsapp button$/, () ->
23 | new @Widgets
24 | .ShareButtonNetworks()
25 | .filter( (item) ->
26 | item.hasClass('whatsapp')
27 | )
28 | .then (list) ->
29 | list[0].click('a')
30 |
31 | @Then /^I should have a correct Whatsapp share url$/, () ->
32 | new @Widgets
33 | .ShareButtonNetworks()
34 | .filter( (item) ->
35 | item.hasClass('whatsapp')
36 | )
37 | .then (list) ->
38 | list[0].getAttribute(
39 | selector: 'a',
40 | attribute: 'href'
41 | ).should.eventually.eq('whatsapp://send?text=whatsapp%20description%20http%3A%2F%2Fwww.example.com')
42 |
--------------------------------------------------------------------------------
/tests/widgets/shareButton.coffee:
--------------------------------------------------------------------------------
1 | path = require 'path'
2 |
3 | module.exports = ->
4 |
5 | @Widgets = @Widgets || {}
6 |
7 | @Widgets.ShareButton = @Widget.extend
8 | root: 'share-button'
9 |
10 | switchToPopup: ->
11 | popUpWindowHandle = @driver.getAllWindowHandles()
12 | .then (res) -> return res[res.length - 1]
13 | @driver.switchTo().window(popUpWindowHandle)
14 |
15 | addAnimate: ->
16 | @addClass('animate')
17 |
18 | removeFacebookSDK: ->
19 | @driver.executeScript('window.FB = null')
20 |
21 | @Widgets.SBDiv = @Widget.extend
22 | root: 'div'
23 |
24 | @Widgets.ShareButtonSocial = @Widget.extend
25 | root: '.sb-social'
26 |
27 | @Widgets.ShareButtonTestSocial = @Widget.extend
28 | root: '.test-social'
29 |
30 | @Widgets.ShareButtonNetworks = @Widget.List.extend
31 | root: 'share-button ul'
32 | itemSelector: 'li'
33 |
--------------------------------------------------------------------------------