├── .eslintrc.js
├── .gitignore
├── .stylelintrc
├── README.md
├── gulpfile.js
├── package-lock.json
├── package.json
└── src
├── index-src.html
├── js
└── script.js
├── scss
└── styles.scss
└── translate
├── en.json
├── pt.json
├── ru.json
├── tr.json
├── zh-cn.json
└── zh-tw.json
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | es6: true
5 | },
6 | extends: [`standard`],
7 | globals: {
8 | Atomics: `readonly`,
9 | SharedArrayBuffer: `readonly`
10 | },
11 | parser: `babel-eslint`,
12 | parserOptions: {
13 | ecmaVersion: 2018
14 | },
15 | rules: {
16 | semi: [`error`, `always`],
17 | indent: [`error`, 2],
18 | quotes: [2, `backtick`]
19 | }
20 | };
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | build
4 |
--------------------------------------------------------------------------------
/.stylelintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "stylelint-config-standard",
3 | "rules": {
4 | "at-rule-no-unknown": [
5 | true,
6 | ignoreAtRules: [
7 | "mixin", "include"
8 | ]
9 | ],
10 | "indentation": 2,
11 | "string-quotes": "double",
12 | "no-duplicate-selectors": true,
13 | "color-hex-case": "upper",
14 | "color-hex-length": "short",
15 | "selector-max-id": 0,
16 | "selector-combinator-space-after": "always",
17 | "selector-attribute-quotes": "always",
18 | "selector-type-case": "upper",
19 | "declaration-block-trailing-semicolon": "always",
20 | "declaration-no-important": true,
21 | "declaration-colon-space-before": "never",
22 | "declaration-colon-space-after": "always-single-line",
23 | "declaration-colon-newline-after": null,
24 | "property-no-vendor-prefix": true,
25 | "value-no-vendor-prefix": true,
26 | "number-leading-zero": "never",
27 | "function-url-quotes": "always",
28 | "font-family-name-quotes": "always-where-required",
29 | "comment-whitespace-inside": "always",
30 | "comment-empty-line-before": null,
31 | "custom-property-empty-line-before": null,
32 | "at-rule-no-vendor-prefix": true,
33 | "selector-pseudo-element-colon-notation": "double",
34 | "selector-pseudo-class-parentheses-space-inside": "never",
35 | "selector-no-vendor-prefix": true,
36 | "media-feature-range-operator-space-before": "always",
37 | "media-feature-range-operator-space-after": "always",
38 | "media-feature-parentheses-space-inside": "always",
39 | "media-feature-colon-space-before": "never",
40 | "media-feature-colon-space-after": "always"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Url encoder for SVG
2 |
3 | http://yoksel.github.io/url-encoder
4 |
5 | ## Usage
6 |
7 | 1. Take `svg`.
8 | 2. Encode it with [url-encoder](http://yoksel.github.io/url-encoder/).
9 | 3. Use code as `background-image` or `border-image` or `mask`.
10 | 4. Enjoy!
11 |
12 | Demo: https://codepen.io/yoksel/full/WNrLpYW.
13 |
14 | ## How to add translate
15 |
16 | 1. Create translation file (use `src/translate/en.json` as example) and place it to `src/translate/`
17 | 2. Add new language to section `langs` in all translation files
18 | 3. Add item with your language to section `translates` in `gulpfile.js`.
19 | 4. Run `npm start` in console (wait until project will be opened in browser) and check all pages:
20 | * Link to new page must appear in all pages
21 | * In the page with new translate new language in list must looks like current (bold and black)
22 | 5. Send pull request
23 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | const gulp = require(`gulp`);
2 | const sass = require('gulp-sass')(require('sass'));
3 | const sync = require(`browser-sync`).create();
4 | const reload = sync.reload;
5 | const colors = require(`colors/safe`);
6 | const del = require(`del`);
7 | const mustache = require(`gulp-mustache`);
8 | const rename = require(`gulp-rename`);
9 |
10 | const SERVER_ROOT = `build/`;
11 |
12 | const translates = [
13 | {
14 | dest: SERVER_ROOT,
15 | url: `./src/translate/en.json`
16 | },
17 | {
18 | dest: `${SERVER_ROOT}pt`,
19 | url: `./src/translate/pt.json`
20 | },
21 | {
22 | dest: `${SERVER_ROOT}ru`,
23 | url: `./src/translate/ru.json`
24 | },
25 | {
26 | dest: `${SERVER_ROOT}zh-cn`,
27 | url: `./src/translate/zh-cn.json`
28 | },
29 | {
30 | dest: `${SERVER_ROOT}zh-tw`,
31 | url: `./src/translate/zh-tw.json`
32 | },
33 | {
34 | dest: `${SERVER_ROOT}tr`,
35 | url: `./src/translate/tr.json`
36 | }
37 | ];
38 |
39 | // TEMPLATES
40 | const tmplTasks = translates.map(({ dest, url }) => {
41 | return (done) => {
42 | gulp.src(`./src/index-src.html`)
43 | .pipe(mustache(url))
44 | .pipe(rename(`index.html`))
45 | .pipe(gulp.dest(dest))
46 | .pipe(reload({ stream: true }));
47 | done();
48 | };
49 | });
50 |
51 | gulp.task(`tmpl`, gulp.series(...tmplTasks));
52 |
53 | // SCSS
54 | gulp.task(`scss`, function () {
55 | return gulp.src(`src/scss/**/styles.scss`)
56 | .pipe(sass().on(`error`, sass.logError))
57 | .pipe(gulp.dest(`${SERVER_ROOT}assets/css`))
58 | .pipe(reload({ stream: true }));
59 | });
60 |
61 | // JS
62 | gulp.task(`js`, function () {
63 | return gulp.src(`src/js/**/*.js`)
64 | .pipe(gulp.dest(`${SERVER_ROOT}assets/js`))
65 | .pipe(reload({ stream: true }));
66 | });
67 |
68 | // CLEAN BUILD
69 | gulp.task(`clean`, function (done) {
70 | del([`${SERVER_ROOT}*`]).then(paths => {
71 | console.log(`⬤ Deleted files and folders:\n`, paths.join(`\n`));
72 | });
73 |
74 | done();
75 | });
76 |
77 | // CLEAN BUILD & COPY FILES TO IT
78 | gulp.task(`build`, gulp.series([`scss`, `js`, `tmpl`]));
79 |
80 | // WATCH FILES
81 | function watchTasks () {
82 | sync.init({
83 | ui: false,
84 | notify: false,
85 | server: {
86 | baseDir: SERVER_ROOT
87 | }
88 | });
89 |
90 | gulp.watch([`src/scss/**/*.scss`], gulp.series(`scss`));
91 | gulp.watch([`src/index-src.html`, `src/translate/**/*`], gulp.series(`tmpl`));
92 | gulp.watch([`src/js/**/*`], gulp.series(`js`));
93 | }
94 |
95 | gulp.task(`serve`, gulp.series([`build`], watchTasks));
96 |
97 | gulp.task(`default`, function () {
98 | console.log(colors.rainbow(`⬤ ================================ ⬤\n`));
99 | console.log(` AVAILABLE COMMANDS:`);
100 | console.log(` ` + colors.cyan(`-------------------\n`));
101 | console.log(` ` + colors.yellow(`npm start`) +
102 | ` — run local server with watcher`);
103 | console.log(` ` + colors.green(`npm run build`) +
104 | ` — make build of the project`);
105 | console.log(` ` + colors.cyan(`npm run deploy`) +
106 | ` — make build and publish project to Github Pages`);
107 | console.log(colors.rainbow(`\n⬤ ================================ ⬤`));
108 | });
109 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "url-encoder",
3 | "version": "1.0.1",
4 | "description": "http://yoksel.github.io/url-encoder",
5 | "main": ".eslintrc.js",
6 | "scripts": {
7 | "start": "gulp clean && gulp serve",
8 | "build": "gulp clean && gulp build",
9 | "deploy": "npm run build && gh-pages -d build -b gh-pages",
10 | "lint": "npm run js:lint && npm run css:lint",
11 | "lint:fix": "npm run js:fix && npm run css:fix",
12 | "js:lint": "eslint src",
13 | "js:fix": "eslint src --fix",
14 | "css:lint": "stylelint **/*.scss",
15 | "css:fix": "stylelint **/*.scss --fix"
16 | },
17 | "repository": {
18 | "type": "git",
19 | "url": "git+https://github.com/yoksel/url-encoder.git"
20 | },
21 | "keywords": [],
22 | "author": "",
23 | "license": "ISC",
24 | "bugs": {
25 | "url": "https://github.com/yoksel/url-encoder/issues"
26 | },
27 | "homepage": "https://github.com/yoksel/url-encoder#readme",
28 | "devDependencies": {
29 | "babel-eslint": "10.1.0",
30 | "browser-sync": "^2.26.10",
31 | "colors": "^1.1.2",
32 | "del": "^2.2.0",
33 | "eslint": "7.5.0",
34 | "eslint-config-standard": "14.1.1",
35 | "eslint-plugin-import": "2.22.0",
36 | "eslint-plugin-node": "^11.1.0",
37 | "eslint-plugin-promise": "4.2.1",
38 | "eslint-plugin-standard": "4.0.1",
39 | "gh-pages": "^3.1.0",
40 | "gulp": "^4.0.2",
41 | "gulp-mustache": "^5.0.0",
42 | "gulp-rename": "2.0.0",
43 | "gulp-sass": "^5.1.0",
44 | "sass": "^1.58.3",
45 | "stylelint": "^13.0.0",
46 | "stylelint-config-standard": "^19.0.0"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/index-src.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{title}}
4 |
5 |
6 |
7 |
8 |
19 |
20 |
21 |
58 |
59 |
60 |
61 |
{{insertSVG}}:
62 |
63 |
64 |
65 |
70 |
71 |
72 |
73 |
{{takeEncoded}}:
74 |
75 |
79 |
80 |
85 |
86 |
{{decodeTip}}
87 |
88 |
89 |
90 |
91 |
92 |
{{readyForCSS}}:
93 |
94 |
96 |
97 |
102 |
103 |
104 |
105 |
{{preview}}:
106 |
107 |
{{background}}:
108 |
115 |
116 |
123 |
124 |
131 |
132 |
133 |
136 |
137 |
138 |
139 |
140 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
--------------------------------------------------------------------------------
/src/js/script.js:
--------------------------------------------------------------------------------
1 | const doc = document;
2 |
3 | const initTextarea = doc.querySelector(`#init`);
4 | const resultTextarea = doc.querySelector(`#result`);
5 | const resultCssTextarea = doc.querySelector(`#result-css`);
6 | const resultDemo = doc.querySelector(`#demo`);
7 | const demoWrapper = doc.querySelector(`.demo-wrapper`);
8 | const contrastButtons = doc.querySelectorAll(`.contrast-button`);
9 | const dropzoneEl = doc.querySelector(`#dropzone`);
10 | let contrastButtonCurrent = null;
11 | let backgroundColor = ``;
12 |
13 | const expanders = doc.querySelectorAll(`.expander`);
14 | const expandedClass = `expanded`;
15 | const symbols = /[\r\n%#()<>?[\\\]^`{|}]/g;
16 |
17 | const quotesInputs = doc.querySelectorAll(`.options__input`);
18 | let externalQuotesValue = doc.querySelector(`.options__input:checked`).value;
19 | let quotes = getQuotes();
20 |
21 | const buttonExample = doc.querySelector(`.button-example`);
22 |
23 | // Textarea Actions
24 | // ----------------------------------------
25 |
26 | initTextarea.oninput = function () {
27 | getResults();
28 | };
29 |
30 | resultTextarea.oninput = function () {
31 | const value = resultTextarea.value.trim()
32 | .replace(/background-image:\s{0,}url\(/, ``)
33 | .replace(/["']{0,}data:image\/svg\+xml,/, ``)
34 | .replace(/["']\);{0,}$/, ``);
35 | initTextarea.value = decodeURIComponent(value);
36 | getResults();
37 | };
38 |
39 | function getResults () {
40 | if (!initTextarea.value) {
41 | resultCssTextarea.value = ``;
42 | resultDemo.setAttribute(`style`, ``);
43 | return;
44 | }
45 |
46 | const namespaced = addNameSpace(initTextarea.value);
47 | const escaped = encodeSVG(namespaced);
48 | resultTextarea.value = escaped;
49 | const resultCss = `background-image: url(${quotes.level1}data:image/svg+xml,${escaped}${quotes.level1});`;
50 | resultCssTextarea.value = resultCss;
51 | resultDemo.setAttribute(`style`, resultCss);
52 | }
53 |
54 | // Tabs Actions
55 | // ----------------------------------------
56 |
57 | for (let i = 0; i < expanders.length; i++) {
58 | const expander = expanders[i];
59 |
60 | expander.onclick = function () {
61 | const parent = this.parentNode;
62 | const expanded = parent.querySelector(`.` + expandedClass);
63 | expanded.classList.toggle(`hidden`);
64 | this.classList.toggle(`opened`);
65 | };
66 | }
67 |
68 | // Switch quotes
69 | // ----------------------------------------
70 |
71 | quotesInputs.forEach(input => {
72 | input.addEventListener(`input`, function () {
73 | externalQuotesValue = this.value;
74 | quotes = getQuotes();
75 | getResults();
76 | });
77 | });
78 |
79 | // Set example
80 | // ----------------------------------------
81 |
82 | buttonExample.addEventListener(`click`, () => {
83 | initTextarea.value = ``;
91 | getResults();
92 | });
93 |
94 | // Demo Background Switch
95 | // ----------------------------------------
96 |
97 | function contrastButtonsSetCurrent (button) {
98 | const classCurrent = `contrast-button--current`;
99 |
100 | if (contrastButtonCurrent) {
101 | contrastButtonCurrent.classList.remove(classCurrent);
102 | }
103 |
104 | backgroundColor = button.dataset.color;
105 | contrastButtonCurrent = button;
106 | button.classList.add(classCurrent);
107 | }
108 |
109 | contrastButtons.forEach(button => {
110 | if (!backgroundColor) {
111 | contrastButtonsSetCurrent(button);
112 | }
113 |
114 | button.addEventListener(`click`, function () {
115 | contrastButtonsSetCurrent(this);
116 | demoWrapper.style.background = backgroundColor;
117 | });
118 | });
119 |
120 | // Namespace
121 | // ----------------------------------------
122 |
123 | function addNameSpace (data) {
124 | if (data.indexOf(`http://www.w3.org/2000/svg`) < 0) {
125 | data = data.replace(/