├── .gitignore
├── .travis.yml
├── Jakefile.js
├── LICENCE.txt
├── README.md
├── bower.json
├── build
├── build.js
├── deps.js
└── hintrc.js
├── dist
├── leaflet.pattern-src.js
└── leaflet.pattern.js
├── example
├── pattern-circle.html
├── pattern-geojson.html
├── pattern-path.html
├── pattern-rect.html
└── pattern-stripes.html
├── package.json
├── spec
├── after.js
├── expect.js
├── happen.js
├── karma.conf.js
├── sinon.js
└── suites
│ └── onAddSpec.js
└── src
├── Pattern.SVG.js
├── Pattern.js
├── PatternCircle.js
├── PatternPath.js
├── PatternRect.js
├── PatternShape.SVG.js
├── PatternShape.js
├── StripePattern.js
└── copyright.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | tmp/**/*
4 | .idea
5 | .idea/**/*
6 | *.iml
7 | *.sublime-*
8 | _site
9 | coverage/
10 | *.js.html
11 | index.html
12 | .mailmap
13 | component.json
14 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 0.10
--------------------------------------------------------------------------------
/Jakefile.js:
--------------------------------------------------------------------------------
1 | /*
2 | Leaflet.pattern building, testing and linting scripts.
3 |
4 | To use, install Node, then run the following commands in the project root:
5 |
6 | npm install -g jake
7 | npm install
8 |
9 | To check the code for errors and build Leaflet from source, run "jake".
10 | To run the tests, run "jake test".
11 |
12 | For a custom build, open build/build.html in the browser and follow the instructions.
13 | */
14 |
15 | var build = require('./build/build.js');
16 |
17 | desc('Check Leaflet.pattern source for errors with JSHint');
18 | task('lint', build.lint);
19 |
20 | desc('Combine and compress Leaflet.pattern source files');
21 | task('build', ['lint'], build.build);
22 |
23 | desc('Run PhantomJS tests');
24 | task('test', ['lint'], build.test);
25 |
26 | task('default', ['build']);
--------------------------------------------------------------------------------
/LICENCE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015, Tyler Eastman
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without modification, are
5 | permitted provided that the following conditions are met:
6 |
7 | 1. Redistributions of source code must retain the above copyright notice, this list of
8 | conditions and the following disclaimer.
9 |
10 | 2. Redistributions in binary form must reproduce the above copyright notice, this list
11 | of conditions and the following disclaimer in the documentation and/or other materials
12 | provided with the distribution.
13 |
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
15 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
16 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
17 | COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
21 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Leaflet.pattern
2 | ===============
3 |
4 | Provides the ability to use SVG patterns as backgrounds for [Leaflet](http://leafletjs.com) Paths.
5 |
6 | *Requires Leaflet 0.7.0 or newer.*
7 |
8 |
9 | Usage Examples
10 | ==============
11 |
12 | * [Circle Pattern](https://teastman.github.io/Leaflet.pattern/pattern-circle.html)
13 | * [Rectangular Pattern](https://teastman.github.io/Leaflet.pattern/pattern-rect.html)
14 | * [Stripe Pattern](https://teastman.github.io/Leaflet.pattern/pattern-stripes.html)
15 | * [Path Pattern](https://teastman.github.io/Leaflet.pattern/pattern-path.html)
16 | * [GeoJson Pattern](https://teastman.github.io/Leaflet.pattern/pattern-geojson.html)
17 |
18 | You can define a pattern in 2 ways.
19 | 1. Using a pre-defined provided pattern.
20 | 2. Creating a custom pattern.
21 |
22 |
23 | In either case the Pattern object needs to be initialized. This can be done as follows.
24 |
25 |
26 | var pattern = new L.Pattern({options});
27 |
28 |
29 | **Options**
30 | All custom and pre-defined patterns can make use of the following options.
31 |
32 | * **patternUnits**: {userSpaceOnUse | objectBoundingBox} (default: userSpaceOnUse) - Defines if the x, y, width, and height values are measured against the current user coordinate system, or are in the range of 0.0 - 1.0 as a percentage of the bounding box of their parent element.
33 | * **patternContentUnits**: {userSpaceOnUse | objectBoundingBox} (default: userSpaceOnUse) - Similar to patternUnits but applies to the shapes within the pattern.
34 | * **x**: {number} (default: 0) - The x offset of the pattern starting position.
35 | * **y**: {number} (default: 0) - The y offset of the pattern starting position.
36 | * **width**: {number} (default: 8) - The width of the pattern.
37 | * **height**: {number} (default: 8) - The height of the pattern.
38 | * **patternTransform**: {string} (default: null) - see http://www.w3.org/TR/SVG/coords.html#TransformAttribute.
39 | * **angle**: {number} (default: null) - a quick way to add rotate(angle) to the patternTransform.
40 |
41 |
42 | Pre-Built Patterns
43 | ------------------
44 |
45 | Pre-Built patterns are just an easier way to use some common patterns. Pre-Built patterns typically have their own special options, but all can use the base options mentioned above.
46 |
47 | ### Stripes
48 |
49 |
50 | var stripes = new L.StripePattern({options});
51 | stripes.addTo(map);
52 |
53 |
54 | **Options**
55 |
56 | * **weight**: {number} (default: 4) - The width of the primary stripe.
57 | * **spaceWeight**: {number} (default: 4) - The width of the secondaty stripe, typically an empty space.
58 | * **color**: {color} (default: #000000) - The color of the primary stripe.
59 | * **spaceColor**: {color} (default: #ffffff) - The color of the secondary stripe.
60 | * **opacity**: {0.0 - 1.0} (default: 1.0) - The opacity of the primary stripe.
61 | * **spaceOpacity**: {0.0 - 1.0} (default: 0.0) - The opacity of the secondary stripe.
62 |
63 | ## Usage
64 |
65 | Once the pre-built patterns are defined you can use them by adding them as the fill pattern property of any Path in leaflet.
66 |
67 |
68 | var circle = new L.Circle({LatLng}, {radius}, {
69 | fillPattern: stripes,
70 | fillOpacity: 1.0});
71 | circle.addTo(_map);
72 |
73 |
74 | Custom Patterns
75 | ---------------
76 |
77 | To create custom patterns you must first create some shapes to define what the pattern looks like.
78 |
79 | ### Shapes
80 |
81 | **Options**
82 |
83 | All shapes have the following options.
84 |
85 | * **stroke**: {boolean} (default: true) - Whether to draw along the path or not.
86 | * **color**: {color} (default: 3388ff) - Color of the stroke.
87 | * **weight**: {number} (default: 3) - Width of the stroke.
88 | * **opacity**: {0.0 - 1.0} (default: 1.0) - Opacity of the stroke.
89 | * **lineCap**: {butt | round | square | inherit} (default: round) - Defines how the stroke looks at its ends
90 | * **lineJoin**: {butt | round | square | inherit} (default: round) - Defines how the stroke looks at its corners.
91 | * **dashArray**: {dashArray} (default: null) - Defines the strokes dash pattern. ex: '5, 5'
92 | * **dashOffset**: {number} (default: null) -
93 | * **fill**: {boolean} (default: false) - Should the shape be filled.
94 | * **fillColor**: {color} (default: same as color) - Color of the fill.
95 | * **fillOpacity**: {0.0 - 1.0} (default: 0.2) - Opacity of the fill.
96 | * **fillRule**: {nonzero | evenodd | inherit} (default: evenodd) -
97 | * **fillPattern**: {L.Pattern} (default: null) - The pattern to fill the Shape with.
98 |
99 | ### Path
100 |
101 |
102 | var shape = new L.PatternPath({
103 | d: 'M10 0 L7 20 L25 20 Z',
104 | fill: true
105 | });
106 |
107 |
108 | **Options**
109 | * **d**: {path} (default: null) - The SVG path definition.
110 |
111 | ### Circle
112 |
113 |
114 | var shape = new L.PatternCircle({
115 | x: 12,
116 | y: 12,
117 | radius: 10,
118 | fill: true
119 | });
120 |
121 |
122 | **Options**
123 | * **x**: {number} (default: 0) - x offset of the circle.
124 | * **y**: {number} (default: 0) - y offset of the circle.
125 | * **radius**: {number} (default: 0) - radius of the circle.
126 |
127 | ### Rectangle
128 |
129 |
130 | var shape = new L.PatternRect({
131 | x: 5,
132 | y: 5,
133 | width: 40,
134 | height: 40,
135 | rx: 10,
136 | ry: 10,
137 | fill: true
138 | });
139 |
140 |
141 | **Options**
142 | * **x**: {number} (default: 0) - x offset of the rectangle.
143 | * **y**: {number} (default: 0) - y offset of the rectangle.
144 | * **width**: {number} (default: 10) - width of the rectangle.
145 | * **height**: {number} (default: 10) - height of the rectangle.
146 | * **rx**: {number} (default: null) - x radius for rounded corners
147 | * **ry**: {number} (default: null) - y radius for rounded corners
148 |
149 | ## Usage
150 |
151 | Once the paths are defined you can use them by adding them to a Pattern.
152 |
153 |
154 | var pattern = new L.Pattern({options});
155 | pattern.addShape(shape);
156 | pattern.addTo(map);
157 |
158 |
159 | Finally you can now use the pattern in the fill pattern property of any Path in leaflet.
160 |
161 |
162 | var circle = new L.Circle({LatLng}, {radius}, {
163 | fillPattern: pattern,
164 | fillOpacity: 1.0});
165 | circle.addTo(_map);
166 |
167 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Leaflet.pattern",
3 | "version": "0.0.0",
4 | "homepage": "https://github.com/teastman/Leaflet.pattern",
5 | "authors": [
6 | "teastman"
7 | ],
8 | "description": "Plugin for leaflet that allows for use of fill patterns in Paths",
9 | "main": "dist/leaflet.pattern.js",
10 | "keywords": [
11 | "leaflet"
12 | ],
13 | "license": "MIT"
14 | }
15 |
--------------------------------------------------------------------------------
/build/build.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs'),
2 | jshint = require('jshint'),
3 | UglifyJS = require('uglify-js'),
4 |
5 | deps = require('./deps.js').deps,
6 | hintrc = require('./hintrc.js').config;
7 |
8 | function lintFiles(files) {
9 |
10 | var errorsFound = 0,
11 | i, j, len, len2, src, errors, e;
12 |
13 | for (i = 0, len = files.length; i < len; i++) {
14 |
15 | jshint.JSHINT(fs.readFileSync(files[i], 'utf8'), hintrc, i ? {L: true} : null);
16 | errors = jshint.JSHINT.errors;
17 |
18 | for (j = 0, len2 = errors.length; j < len2; j++) {
19 | e = errors[j];
20 | console.log(files[i] + '\tline ' + e.line + '\tcol ' + e.character + '\t ' + e.reason);
21 | }
22 |
23 | errorsFound += len2;
24 | }
25 |
26 | return errorsFound;
27 | }
28 |
29 | function getFiles(compsBase32) {
30 | var memo = {},
31 | comps;
32 |
33 | if (compsBase32) {
34 | comps = parseInt(compsBase32, 32).toString(2).split('');
35 | console.log('Managing dependencies...');
36 | }
37 |
38 | function addFiles(srcs) {
39 | for (var j = 0, len = srcs.length; j < len; j++) {
40 | memo[srcs[j]] = true;
41 | }
42 | }
43 |
44 | for (var i in deps) {
45 | if (comps) {
46 | if (parseInt(comps.pop(), 2) === 1) {
47 | console.log('\t* ' + i);
48 | addFiles(deps[i].src);
49 | } else {
50 | console.log('\t ' + i);
51 | }
52 | } else {
53 | addFiles(deps[i].src);
54 | }
55 | }
56 |
57 | var files = [];
58 |
59 | for (var src in memo) {
60 | files.push('src/' + src);
61 | }
62 |
63 | return files;
64 | }
65 |
66 | exports.getFiles = getFiles;
67 |
68 | exports.lint = function () {
69 |
70 | var files = getFiles();
71 |
72 | console.log('Checking for JS errors...');
73 |
74 | var errorsFound = lintFiles(files);
75 |
76 | if (errorsFound > 0) {
77 | console.log(errorsFound + ' error(s) found.\n');
78 | fail();
79 | } else {
80 | console.log('\tCheck passed');
81 | }
82 | };
83 |
84 |
85 | function getSizeDelta(newContent, oldContent) {
86 | if (!oldContent) {
87 | return 'new';
88 | }
89 | var newLen = newContent.replace(/\r\n?/g, '\n').length,
90 | oldLen = oldContent.replace(/\r\n?/g, '\n').length,
91 | delta = newLen - oldLen;
92 |
93 | return (delta >= 0 ? '+' : '') + delta;
94 | }
95 |
96 | function loadSilently(path) {
97 | try {
98 | return fs.readFileSync(path, 'utf8');
99 | } catch (e) {
100 | return null;
101 | }
102 | }
103 |
104 | function combineFiles(files) {
105 | var content = '';
106 | for (var i = 0, len = files.length; i < len; i++) {
107 | content += fs.readFileSync(files[i], 'utf8') + '\n\n';
108 | }
109 | return content;
110 | }
111 |
112 | exports.build = function (compsBase32, buildName) {
113 |
114 | var files = getFiles(compsBase32);
115 |
116 | console.log('Concatenating ' + files.length + ' files...');
117 |
118 | var copy = fs.readFileSync('src/copyright.js', 'utf8'),
119 | intro = '(function (window, document, undefined) {',
120 | outro = '}(window, document));',
121 | newSrc = copy + intro + combineFiles(files) + outro,
122 |
123 | pathPart = 'dist/leaflet.pattern' + (buildName ? '-' + buildName : ''),
124 | srcPath = pathPart + '-src.js',
125 |
126 | oldSrc = loadSilently(srcPath),
127 | srcDelta = getSizeDelta(newSrc, oldSrc);
128 |
129 | console.log('\tUncompressed size: ' + newSrc.length + ' bytes (' + srcDelta + ')');
130 |
131 | if (newSrc === oldSrc) {
132 | console.log('\tNo changes');
133 | } else {
134 | fs.writeFileSync(srcPath, newSrc);
135 | console.log('\tSaved to ' + srcPath);
136 | }
137 |
138 | console.log('Compressing...');
139 |
140 | var path = pathPart + '.js',
141 | oldCompressed = loadSilently(path),
142 | newCompressed = copy + UglifyJS.minify(newSrc, {
143 | warnings: true,
144 | fromString: true
145 | }).code,
146 | delta = getSizeDelta(newCompressed, oldCompressed);
147 |
148 | console.log('\tCompressed size: ' + newCompressed.length + ' bytes (' + delta + ')');
149 |
150 | if (newCompressed === oldCompressed) {
151 | console.log('\tNo changes');
152 | } else {
153 | fs.writeFileSync(path, newCompressed);
154 | console.log('\tSaved to ' + path);
155 | }
156 | };
157 |
158 | exports.test = function() {
159 | var karma = require('karma'),
160 | testConfig = {configFile : __dirname + '/../spec/karma.conf.js'};
161 |
162 | testConfig.browsers = ['PhantomJS'];
163 |
164 | if (isArgv('--chrome')) {
165 | testConfig.browsers.push('Chrome');
166 | }
167 | if (isArgv('--safari')) {
168 | testConfig.browsers.push('Safari');
169 | }
170 | if (isArgv('--ff')) {
171 | testConfig.browsers.push('Firefox');
172 | }
173 | if (isArgv('--ie')) {
174 | testConfig.browsers.push('IE');
175 | }
176 |
177 | if (isArgv('--cov')) {
178 | testConfig.preprocessors = {
179 | '../src/**/*.js': 'coverage'
180 | };
181 | testConfig.coverageReporter = {
182 | type : 'html',
183 | dir : 'coverage/'
184 | };
185 | testConfig.reporters = ['coverage'];
186 | }
187 |
188 | karma.server.start(testConfig);
189 |
190 | function isArgv(optName) {
191 | return process.argv.indexOf(optName) !== -1;
192 | }
193 | };
194 |
--------------------------------------------------------------------------------
/build/deps.js:
--------------------------------------------------------------------------------
1 | var deps = {
2 |
3 | Pattern: {
4 | src: ['Pattern.js',
5 | 'Pattern.SVG.js',
6 | 'StripePattern.js'],
7 | desc: 'Pattern definitions'
8 | },
9 |
10 | Shape: {
11 | src: ['PatternShape.js',
12 | 'PatternShape.SVG.js',
13 | 'PatternPath.js',
14 | 'PatternCircle.js',
15 | 'PatternRect.js'],
16 | desc: 'Shapes for use in patterns.'
17 | }
18 | };
19 |
20 | if (typeof exports !== 'undefined') {
21 | exports.deps = deps;
22 | }
23 |
--------------------------------------------------------------------------------
/build/hintrc.js:
--------------------------------------------------------------------------------
1 | exports.config = {
2 |
3 | // environment
4 | "browser": true,
5 | "node": true,
6 | "predef": ['L', 'define'],
7 | "strict": false,
8 |
9 | // code style
10 | "bitwise": true,
11 | "camelcase": true,
12 | "curly": true,
13 | "eqeqeq": true,
14 | "forin": false,
15 | "immed": true,
16 | "latedef": true,
17 | "newcap": true,
18 | "noarg": true,
19 | "noempty": true,
20 | "nonew": true,
21 | "undef": true,
22 | "unused": true,
23 | //"quotmark": "single",
24 |
25 | // whitespace
26 | "indent": 4,
27 | "trailing": true,
28 | "white": true,
29 | "smarttabs": true
30 | //"maxlen": 120
31 |
32 | // code simplicity - not enforced but nice to check from time to time
33 | // "maxstatements": 20,
34 | // "maxcomplexity": 5
35 | // "maxparams": 4,
36 | // "maxdepth": 4
37 | };
38 |
--------------------------------------------------------------------------------
/dist/leaflet.pattern-src.js:
--------------------------------------------------------------------------------
1 | /*
2 | Leaflet.pattern, Provides tools to set the backgrounds of vector shapes in Leaflet to be patterns.
3 | https://github.com/teastman/Leaflet.pattern
4 | (c) 2015, Tyler Eastman
5 | */
6 | (function (window, document, undefined) {/*
7 | * L.Pattern is the base class for fill patterns for leaflet Paths.
8 | */
9 |
10 | L.Pattern = L.Class.extend({
11 | includes: [L.Mixin.Events],
12 |
13 | options: {
14 | x: 0,
15 | y: 0,
16 | width: 8,
17 | height: 8,
18 | patternUnits: 'userSpaceOnUse',
19 | patternContentUnits: 'userSpaceOnUse'
20 | // angle: <0 - 360>
21 | // patternTransform:
22 | },
23 |
24 | _addShapes: L.Util.falseFn,
25 | _update: L.Util.falseFn,
26 |
27 | initialize: function (options) {
28 | this._shapes = {};
29 | L.setOptions(this, options);
30 | },
31 |
32 | onAdd: function (map) {
33 | this._map = map.target ? map.target : map;
34 | this._map._initDefRoot();
35 |
36 | // Create the DOM Object for the pattern.
37 | this._initDom();
38 |
39 | // Any shapes that were added before this was added to the map need to have their onAdd called.
40 | for (var i in this._shapes) {
41 | this._shapes[i].onAdd(this);
42 | }
43 |
44 | // Call any children that want to add their own shapes.
45 | this._addShapes();
46 |
47 | // Add the DOM Object to the DOM Tree
48 | this._addDom();
49 | this.redraw();
50 |
51 | if (this.getEvents) {
52 | this._map.on(this.getEvents(), this);
53 | }
54 | this.fire('add');
55 | this._map.fire('patternadd', {pattern: this});
56 | },
57 |
58 | onRemove: function () {
59 | this._removeDom();
60 | },
61 |
62 | redraw: function () {
63 | if (this._map) {
64 | this._update();
65 | for (var i in this._shapes) {
66 | this._shapes[i].redraw();
67 | }
68 | }
69 | return this;
70 | },
71 |
72 | setStyle: function (style) {
73 | L.setOptions(this, style);
74 | if (this._map) {
75 | this._updateStyle();
76 | this.redraw();
77 | }
78 | return this;
79 | },
80 |
81 | addTo: function (map) {
82 | map.addPattern(this);
83 | return this;
84 | },
85 |
86 | remove: function () {
87 | return this.removeFrom(this._map);
88 | },
89 |
90 | removeFrom: function (map) {
91 | if (map) {
92 | map.removePattern(this);
93 | }
94 | return this;
95 | }
96 | });
97 |
98 | L.Map.addInitHook(function () {
99 | this._patterns = {};
100 | });
101 |
102 | L.Map.include({
103 | addPattern: function (pattern) {
104 | var id = L.stamp(pattern);
105 | if (this._patterns[id]) { return pattern; }
106 | this._patterns[id] = pattern;
107 |
108 | this.whenReady(pattern.onAdd, pattern);
109 | return this;
110 | },
111 |
112 | removePattern: function (pattern) {
113 | var id = L.stamp(pattern);
114 | if (!this._patterns[id]) { return this; }
115 |
116 | if (this._loaded) {
117 | pattern.onRemove(this);
118 | }
119 |
120 | if (pattern.getEvents) {
121 | this.off(pattern.getEvents(), pattern);
122 | }
123 |
124 | delete this._patterns[id];
125 |
126 | if (this._loaded) {
127 | this.fire('patternremove', {pattern: pattern});
128 | pattern.fire('remove');
129 | }
130 |
131 | pattern._map = null;
132 | return this;
133 | },
134 |
135 | hasPattern: function (pattern) {
136 | return !!pattern && (L.stamp(pattern) in this._patterns);
137 | }
138 | });
139 |
140 |
141 |
142 | L.Pattern.SVG_NS = 'http://www.w3.org/2000/svg';
143 |
144 | L.Pattern = L.Pattern.extend({
145 | _createElement: function (name) {
146 | return document.createElementNS(L.Pattern.SVG_NS, name);
147 | },
148 |
149 | _initDom: function () {
150 | this._dom = this._createElement('pattern');
151 | if (this.options.className) {
152 | L.DomUtil.addClass(this._dom, this.options.className);
153 | }
154 | this._updateStyle();
155 | },
156 |
157 | _addDom: function () {
158 | this._map._defRoot.appendChild(this._dom);
159 | },
160 |
161 | _removeDom: function () {
162 | L.DomUtil.remove(this._dom);
163 | },
164 |
165 | _updateStyle: function () {
166 | var dom = this._dom,
167 | options = this.options;
168 |
169 | if (!dom) { return; }
170 |
171 | dom.setAttribute('id', L.stamp(this));
172 | dom.setAttribute('x', options.x);
173 | dom.setAttribute('y', options.y);
174 | dom.setAttribute('width', options.width);
175 | dom.setAttribute('height', options.height);
176 | dom.setAttribute('patternUnits', options.patternUnits);
177 | dom.setAttribute('patternContentUnits', options.patternContentUnits);
178 |
179 | if (options.patternTransform || options.angle) {
180 | var transform = options.patternTransform ? options.patternTransform + " " : "";
181 | transform += options.angle ? "rotate(" + options.angle + ") " : "";
182 | dom.setAttribute('patternTransform', transform);
183 | }
184 | else {
185 | dom.removeAttribute('patternTransform');
186 | }
187 |
188 | for (var i in this._shapes) {
189 | this._shapes[i]._updateStyle();
190 | }
191 | }
192 | });
193 |
194 | L.Map.include({
195 | _initDefRoot: function () {
196 | if (!this._defRoot) {
197 | if (typeof this.getRenderer === 'function') {
198 | var renderer = this.getRenderer(this);
199 | this._defRoot = L.Pattern.prototype._createElement('defs');
200 | renderer._container.appendChild(this._defRoot);
201 | } else {
202 | if (!this._pathRoot) {
203 | this._initPathRoot();
204 | }
205 | this._defRoot = L.Pattern.prototype._createElement('defs');
206 | this._pathRoot.appendChild(this._defRoot);
207 | }
208 | }
209 | }
210 | });
211 |
212 | if (L.SVG) {
213 | L.SVG.include({
214 | _superUpdateStyle: L.SVG.prototype._updateStyle,
215 |
216 | _updateStyle: function (layer) {
217 | this._superUpdateStyle(layer);
218 |
219 | if (layer.options.fill && layer.options.fillPattern) {
220 | layer._path.setAttribute('fill', 'url(#' + L.stamp(layer.options.fillPattern) + ")");
221 | }
222 | }
223 | });
224 | }
225 | else {
226 | L.Path.include({
227 | _superUpdateStyle: L.Path.prototype._updateStyle,
228 |
229 | _updateStyle: function () {
230 | this._superUpdateStyle();
231 |
232 | if (this.options.fill && this.options.fillPattern) {
233 | this._path.setAttribute('fill', 'url(#' + L.stamp(this.options.fillPattern) + ")");
234 | }
235 | }
236 | });
237 | }
238 |
239 |
240 | /*
241 | * L.StripePattern is an implementation of Pattern that creates stripes.
242 | */
243 |
244 | L.StripePattern = L.Pattern.extend({
245 |
246 | options: {
247 | weight: 4,
248 | spaceWeight: 4,
249 | color: '#000000',
250 | spaceColor: '#ffffff',
251 | opacity: 1.0,
252 | spaceOpacity: 0.0
253 | },
254 |
255 | _addShapes: function () {
256 | this._stripe = new L.PatternPath({
257 | stroke: true,
258 | weight: this.options.weight,
259 | color: this.options.color,
260 | opacity: this.options.opacity
261 | });
262 |
263 | this._space = new L.PatternPath({
264 | stroke: true,
265 | weight: this.options.spaceWeight,
266 | color: this.options.spaceColor,
267 | opacity: this.options.spaceOpacity
268 | });
269 |
270 | this.addShape(this._stripe);
271 | this.addShape(this._space);
272 |
273 | this._update();
274 | },
275 |
276 | _update: function () {
277 | this._stripe.options.d = 'M0 ' + this._stripe.options.weight / 2 + ' H ' + this.options.width;
278 | this._space.options.d = 'M0 ' + (this._stripe.options.weight + this._space.options.weight / 2) + ' H ' + this.options.width;
279 | },
280 |
281 | setStyle: L.Pattern.prototype.setStyle
282 | });
283 |
284 | L.stripePattern = function (options) {
285 | return new L.StripePattern(options);
286 | };
287 |
288 | /*
289 | * L.PatternShape is the base class that is used to define the shapes in Patterns.
290 | */
291 |
292 | L.PatternShape = L.Class.extend({
293 |
294 | options: {
295 | stroke: true,
296 | color: '#3388ff',
297 | weight: 3,
298 | opacity: 1,
299 | lineCap: 'round',
300 | lineJoin: 'round',
301 | // dashArray: null
302 | // dashOffset: null
303 |
304 | // fill: false
305 | // fillColor: same as color by default
306 | fillOpacity: 0.2,
307 | fillRule: 'evenodd',
308 | // fillPattern: L.Pattern
309 | },
310 |
311 | initialize: function (options) {
312 | L.setOptions(this, options);
313 | },
314 |
315 | // Called when the parent Pattern get's added to the map,
316 | // or when added to a Pattern that is already on the map.
317 | onAdd: function (pattern) {
318 | this._pattern = pattern;
319 | if (this._pattern._dom) {
320 | this._initDom(); // This function is implemented by it's children.
321 | this._addDom();
322 | }
323 | },
324 |
325 | addTo: function (pattern) {
326 | pattern.addShape(this);
327 | return this;
328 | },
329 |
330 | redraw: function () {
331 | if (this._pattern) {
332 | this._updateShape(); // This function is implemented by it's children.
333 | }
334 | return this;
335 | },
336 |
337 | setStyle: function (style) {
338 | L.setOptions(this, style);
339 | if (this._pattern) {
340 | this._updateStyle();
341 | }
342 | return this;
343 | },
344 |
345 | setShape: function (shape) {
346 | this.options = L.extend({}, this.options, shape);
347 | this._updateShape();
348 | },
349 | });
350 |
351 | L.Pattern.include({
352 | addShape: function (shape) {
353 | var id = L.stamp(shape);
354 | if (this._shapes[id]) { return shape; }
355 | this._shapes[id] = shape;
356 | shape.onAdd(this);
357 | }
358 | });
359 |
360 |
361 |
362 | L.PatternShape.SVG_NS = 'http://www.w3.org/2000/svg';
363 |
364 | L.PatternShape = L.PatternShape.extend({
365 | _createElement: function (name) {
366 | return document.createElementNS(L.PatternShape.SVG_NS, name);
367 | },
368 |
369 | _initDom: L.Util.falseFn,
370 | _updateShape: L.Util.falseFn,
371 |
372 | _initDomElement: function (type) {
373 | this._dom = this._createElement(type);
374 | if (this.options.className) {
375 | L.DomUtil.addClass(this._dom, this.options.className);
376 | }
377 | this._updateStyle();
378 | },
379 |
380 | _addDom: function () {
381 | this._pattern._dom.appendChild(this._dom);
382 | },
383 |
384 | _updateStyle: function () {
385 | var dom = this._dom,
386 | options = this.options;
387 |
388 | if (!dom) { return; }
389 |
390 | if (options.stroke) {
391 | dom.setAttribute('stroke', options.color);
392 | dom.setAttribute('stroke-opacity', options.opacity);
393 | dom.setAttribute('stroke-width', options.weight);
394 | dom.setAttribute('stroke-linecap', options.lineCap);
395 | dom.setAttribute('stroke-linejoin', options.lineJoin);
396 |
397 | if (options.dashArray) {
398 | dom.setAttribute('stroke-dasharray', options.dashArray);
399 | } else {
400 | dom.removeAttribute('stroke-dasharray');
401 | }
402 |
403 | if (options.dashOffset) {
404 | dom.setAttribute('stroke-dashoffset', options.dashOffset);
405 | } else {
406 | dom.removeAttribute('stroke-dashoffset');
407 | }
408 | } else {
409 | dom.setAttribute('stroke', 'none');
410 | }
411 |
412 | if (options.fill) {
413 | if (options.fillPattern) {
414 | dom.setAttribute('fill', 'url(#' + L.stamp(options.fillPattern) + ")");
415 | }
416 | else {
417 | dom.setAttribute('fill', options.fillColor || options.color);
418 | }
419 | dom.setAttribute('fill-opacity', options.fillOpacity);
420 | dom.setAttribute('fill-rule', options.fillRule || 'evenodd');
421 | } else {
422 | dom.setAttribute('fill', 'none');
423 | }
424 |
425 | dom.setAttribute('pointer-events', options.pointerEvents || (options.interactive ? 'visiblePainted' : 'none'));
426 | }
427 | });
428 |
429 |
430 |
431 | /*
432 | * L.PatternPath is the implementation of PatternShape for adding Paths
433 | */
434 |
435 | L.PatternPath = L.PatternShape.extend({
436 | // options: {
437 | // d: