├── .gitignore ├── .meteor ├── .finished-upgraders ├── .gitignore ├── .id ├── packages ├── platforms ├── release └── versions ├── README.md ├── client ├── compatibility │ └── tether.js ├── imports │ └── my.css ├── main.css ├── main.html ├── main.js └── my2.import.css ├── package.json ├── public ├── arch.jpg ├── nature.jpg └── people.jpg └── server └── main.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /.meteor/.finished-upgraders: -------------------------------------------------------------------------------- 1 | # This file contains information which helps Meteor properly upgrade your 2 | # app when you run 'meteor update'. You should check it into version control 3 | # with your project. 4 | 5 | notices-for-0.9.0 6 | notices-for-0.9.1 7 | 0.9.4-platform-file 8 | notices-for-facebook-graph-api-2 9 | 1.2.0-standard-minifiers-package 10 | 1.2.0-meteor-platform-split 11 | 1.2.0-cordova-changes 12 | 1.2.0-breaking-changes 13 | 1.3.0-split-minifiers-package 14 | -------------------------------------------------------------------------------- /.meteor/.gitignore: -------------------------------------------------------------------------------- 1 | local 2 | -------------------------------------------------------------------------------- /.meteor/.id: -------------------------------------------------------------------------------- 1 | # This file contains a token that is unique to your project. 2 | # Check it into your repository along with the rest of this directory. 3 | # It can be used for purposes such as: 4 | # - ensuring you don't accidentally deploy one app on top of another 5 | # - providing package authors with aggregated statistics 6 | 7 | ykqrrvdl2h3s145oxrp 8 | -------------------------------------------------------------------------------- /.meteor/packages: -------------------------------------------------------------------------------- 1 | # Meteor packages used by this project, one per line. 2 | # Check this file (and the other files in this directory) into your repository. 3 | # 4 | # 'meteor add' and 'meteor remove' will edit this file for you, 5 | # but you can also edit it by hand. 6 | 7 | meteor-base # Packages every Meteor app needs to have 8 | mobile-experience # Packages for a great mobile UX 9 | mongo # The database Meteor supports right now 10 | blaze-html-templates # Compile .html files into Meteor Blaze views 11 | reactive-var # Reactive variable for tracker 12 | jquery # Helpful client-side library 13 | tracker # Meteor's client-side reactive programming library 14 | 15 | standard-minifier-js # JS minifier run for production mode 16 | es5-shim # ECMAScript 5 compatibility for older browsers. 17 | ecmascript # Enable ECMAScript2015+ syntax in app code 18 | 19 | autopublish # Publish all data to the clients (for prototyping) 20 | insecure # Allow all DB writes from clients (for prototyping) 21 | juliancwirko:postcss@1.0.0-rc.12 22 | -------------------------------------------------------------------------------- /.meteor/platforms: -------------------------------------------------------------------------------- 1 | server 2 | browser 3 | -------------------------------------------------------------------------------- /.meteor/release: -------------------------------------------------------------------------------- 1 | METEOR@1.3-rc.12 2 | -------------------------------------------------------------------------------- /.meteor/versions: -------------------------------------------------------------------------------- 1 | allow-deny@1.0.2-rc.12 2 | autopublish@1.0.5-rc.12 3 | autoupdate@1.2.6-rc.12 4 | babel-compiler@6.5.2-rc.12 5 | babel-runtime@0.1.6-rc.12 6 | base64@1.0.6-rc.12 7 | binary-heap@1.0.6-rc.12 8 | blaze@2.1.5-rc.12 9 | blaze-html-templates@1.0.2-rc.12 10 | blaze-tools@1.0.6-rc.12 11 | boilerplate-generator@1.0.6-rc.12 12 | caching-compiler@1.0.2-rc.12 13 | caching-html-compiler@1.0.4-rc.12 14 | callback-hook@1.0.6-rc.12 15 | check@1.1.2-rc.12 16 | ddp@1.2.3-rc.12 17 | ddp-client@1.2.3-rc.12 18 | ddp-common@1.2.3-rc.12 19 | ddp-server@1.2.4-rc.12 20 | deps@1.0.10-rc.12 21 | diff-sequence@1.0.3-rc.12 22 | ecmascript@0.4.1-rc.12 23 | ecmascript-runtime@0.2.8-rc.12 24 | ejson@1.0.9-rc.12 25 | es5-shim@4.5.8-rc.12 26 | fastclick@1.0.9-rc.12 27 | geojson-utils@1.0.6-rc.12 28 | hot-code-push@1.0.2-rc.12 29 | html-tools@1.0.7-rc.12 30 | htmljs@1.0.7-rc.12 31 | http@1.1.3-rc.12 32 | id-map@1.0.5-rc.12 33 | insecure@1.0.5-rc.12 34 | jquery@1.11.6-rc.12 35 | juliancwirko:postcss@1.0.0-rc.12 36 | launch-screen@1.0.8-rc.12 37 | livedata@1.0.16-rc.12 38 | logging@1.0.10-rc.12 39 | meteor@1.1.12-rc.12 40 | meteor-base@1.0.2-rc.12 41 | minifier-css@1.1.9-rc.12 42 | minifier-js@1.1.9-rc.12 43 | minimongo@1.0.12-rc.12 44 | mobile-experience@1.0.2-rc.12 45 | mobile-status-bar@1.0.10-rc.12 46 | modules@0.5.1-rc.12 47 | modules-runtime@0.6.1-rc.12 48 | mongo@1.1.5-rc.12 49 | mongo-id@1.0.2-rc.12 50 | npm-mongo@1.4.41-rc.12 51 | observe-sequence@1.0.9-rc.12 52 | ordered-dict@1.0.5-rc.12 53 | promise@0.6.5-rc.12 54 | random@1.0.7-rc.12 55 | reactive-var@1.0.7-rc.12 56 | reload@1.1.6-rc.12 57 | retry@1.0.5-rc.12 58 | routepolicy@1.0.8-rc.12 59 | spacebars@1.0.9-rc.12 60 | spacebars-compiler@1.0.9-rc.12 61 | standard-minifier-js@1.0.4-rc.12 62 | templating@1.1.7-rc.12 63 | templating-tools@1.0.2-rc.12 64 | tracker@1.0.11-rc.12 65 | ui@1.0.9-rc.12 66 | underscore@1.0.6-rc.12 67 | url@1.0.7-rc.12 68 | webapp@1.2.6-rc.12 69 | webapp-hashing@1.0.7-rc.12 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Meteor Bootstrap 4 Scss from Npm with PostCSS - playground 2 | 3 | This is just a quick test and demo project which uses [PostCSS package](https://github.com/juliancwirko/meteor-postcss/tree/meteor-1.3-in-progress) and some PostCSS plugins to compile Scss provided by Bootstrap 4 from Npm package. 4 | 5 | **For now with Meteor 1.3 you are not able to import and compile .scss files from `node_modules`. Here we use PostCSS and couple of its plugins to be able to install bootstrap from Npm and then import its .scss files from `node_modules` folder.** 6 | 7 | ### Run it locally 8 | 9 | ```bash 10 | $ git clone https://github.com/juliancwirko/meteor-bootstrap-postcss-test.git 11 | $ cd meteor-bootstrap-postcss-test 12 | $ npm install 13 | $ meteor 14 | ``` 15 | 16 | ### What I've done here: 17 | 18 | 1. I use [juliancwirko:postcss](https://github.com/juliancwirko/meteor-postcss/tree/meteor-1.3-in-progress) PostCSS integration package for Meteor (in version 1.0.0-rc.12) 19 | 2. I use some PostCSS plugins. Take a look at `package.json` file at 'postcss' key. Here is the list: ["autoprefixer": "^6.3.4", "postcss-easy-import": "^1.0.1", "postcss-nested": "^1.0.0", "postcss-sassy-mixins": "^2.0.0", "postcss-simple-vars": "^1.2.0"] 20 | 3. I import all bootstrap .scss files in `client/main.css` file - yes it should be .css file not .scss 21 | 4. I import all Bootstrap javascript in `client/main.js` file (ES6 modules). 22 | 5. All is installed by `npm install bootstrap@4.0.0-alpha.2 --save` 23 | 6. We don't need `fourseven:scss` here! 24 | 25 | ### Benefits 26 | 27 | Scss imports and compilation from `node_modules` without any additional work. (You can't do this now with Meteor 1.3) Bootstrap here is just a working example, there can be more other libs with styles attached. 28 | 29 | This could be also a very good transition from projects based on Scss to full PostCSS stack without preprocessors. I think that Bootstrap will be written in PostCSS in the future too. 30 | 31 | Besides Scss and Bootstrap you can use plenty other [PostCSS plugins](http://postcss.parts/). 32 | 33 | ### What doesn't work 34 | 35 | I've tried it with Foundation 6 too. Unfortunatelly because Foundation 6 uses complicated mixins structures the PostCSS plugin responsible for [sassy mixins](https://github.com/andyjansson/postcss-sassy-mixins) can't manage it. We don't use a standard Scss compiler here, so it could be problematic in complicated structures. We use basic stuff like Sass-like variables, nesting, mixins, imports. 36 | 37 | **This solution will not work in complicated use cases. But I'm sure it could be worked out somehow.** 38 | 39 | ### Please play with it 40 | 41 | Let me know if you'll find something interesting. You can also comment here in the GitHub issues. 42 | I'll try to do some more research too. I'll try to run Foundation 6 from Npm. 43 | 44 | ### ... 45 | 46 | 47 | -------------------------------------------------------------------------------- /client/compatibility/tether.js: -------------------------------------------------------------------------------- 1 | /*! tether 1.2.0 */ 2 | 3 | (function(root, factory) { 4 | if (typeof define === 'function' && define.amd) { 5 | define(factory); 6 | } else if (typeof exports === 'object') { 7 | module.exports = factory(require, exports, module); 8 | } else { 9 | root.Tether = factory(); 10 | } 11 | }(this, function(require, exports, module) { 12 | 13 | 'use strict'; 14 | 15 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 16 | 17 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 18 | 19 | var TetherBase = undefined; 20 | if (typeof TetherBase === 'undefined') { 21 | TetherBase = { modules: [] }; 22 | } 23 | 24 | function getScrollParent(el) { 25 | // In firefox if the el is inside an iframe with display: none; window.getComputedStyle() will return null; 26 | // https://bugzilla.mozilla.org/show_bug.cgi?id=548397 27 | var computedStyle = getComputedStyle(el) || {}; 28 | var position = computedStyle.position; 29 | 30 | if (position === 'fixed') { 31 | return el; 32 | } 33 | 34 | var parent = el; 35 | while (parent = parent.parentNode) { 36 | var style = undefined; 37 | try { 38 | style = getComputedStyle(parent); 39 | } catch (err) {} 40 | 41 | if (typeof style === 'undefined' || style === null) { 42 | return parent; 43 | } 44 | 45 | var _style = style; 46 | var overflow = _style.overflow; 47 | var overflowX = _style.overflowX; 48 | var overflowY = _style.overflowY; 49 | 50 | if (/(auto|scroll)/.test(overflow + overflowY + overflowX)) { 51 | if (position !== 'absolute' || ['relative', 'absolute', 'fixed'].indexOf(style.position) >= 0) { 52 | return parent; 53 | } 54 | } 55 | } 56 | 57 | return document.body; 58 | } 59 | 60 | var uniqueId = (function () { 61 | var id = 0; 62 | return function () { 63 | return ++id; 64 | }; 65 | })(); 66 | 67 | var zeroPosCache = {}; 68 | var getOrigin = function getOrigin(doc) { 69 | // getBoundingClientRect is unfortunately too accurate. It introduces a pixel or two of 70 | // jitter as the user scrolls that messes with our ability to detect if two positions 71 | // are equivilant or not. We place an element at the top left of the page that will 72 | // get the same jitter, so we can cancel the two out. 73 | var node = doc._tetherZeroElement; 74 | if (typeof node === 'undefined') { 75 | node = doc.createElement('div'); 76 | node.setAttribute('data-tether-id', uniqueId()); 77 | extend(node.style, { 78 | top: 0, 79 | left: 0, 80 | position: 'absolute' 81 | }); 82 | 83 | doc.body.appendChild(node); 84 | 85 | doc._tetherZeroElement = node; 86 | } 87 | 88 | var id = node.getAttribute('data-tether-id'); 89 | if (typeof zeroPosCache[id] === 'undefined') { 90 | zeroPosCache[id] = {}; 91 | 92 | var rect = node.getBoundingClientRect(); 93 | for (var k in rect) { 94 | // Can't use extend, as on IE9, elements don't resolve to be hasOwnProperty 95 | zeroPosCache[id][k] = rect[k]; 96 | } 97 | 98 | // Clear the cache when this position call is done 99 | defer(function () { 100 | delete zeroPosCache[id]; 101 | }); 102 | } 103 | 104 | return zeroPosCache[id]; 105 | }; 106 | 107 | function getBounds(el) { 108 | var doc = undefined; 109 | if (el === document) { 110 | doc = document; 111 | el = document.documentElement; 112 | } else { 113 | doc = el.ownerDocument; 114 | } 115 | 116 | var docEl = doc.documentElement; 117 | 118 | var box = {}; 119 | // The original object returned by getBoundingClientRect is immutable, so we clone it 120 | // We can't use extend because the properties are not considered part of the object by hasOwnProperty in IE9 121 | var rect = el.getBoundingClientRect(); 122 | for (var k in rect) { 123 | box[k] = rect[k]; 124 | } 125 | 126 | var origin = getOrigin(doc); 127 | 128 | box.top -= origin.top; 129 | box.left -= origin.left; 130 | 131 | if (typeof box.width === 'undefined') { 132 | box.width = document.body.scrollWidth - box.left - box.right; 133 | } 134 | if (typeof box.height === 'undefined') { 135 | box.height = document.body.scrollHeight - box.top - box.bottom; 136 | } 137 | 138 | box.top = box.top - docEl.clientTop; 139 | box.left = box.left - docEl.clientLeft; 140 | box.right = doc.body.clientWidth - box.width - box.left; 141 | box.bottom = doc.body.clientHeight - box.height - box.top; 142 | 143 | return box; 144 | } 145 | 146 | function getOffsetParent(el) { 147 | return el.offsetParent || document.documentElement; 148 | } 149 | 150 | function getScrollBarSize() { 151 | var inner = document.createElement('div'); 152 | inner.style.width = '100%'; 153 | inner.style.height = '200px'; 154 | 155 | var outer = document.createElement('div'); 156 | extend(outer.style, { 157 | position: 'absolute', 158 | top: 0, 159 | left: 0, 160 | pointerEvents: 'none', 161 | visibility: 'hidden', 162 | width: '200px', 163 | height: '150px', 164 | overflow: 'hidden' 165 | }); 166 | 167 | outer.appendChild(inner); 168 | 169 | document.body.appendChild(outer); 170 | 171 | var widthContained = inner.offsetWidth; 172 | outer.style.overflow = 'scroll'; 173 | var widthScroll = inner.offsetWidth; 174 | 175 | if (widthContained === widthScroll) { 176 | widthScroll = outer.clientWidth; 177 | } 178 | 179 | document.body.removeChild(outer); 180 | 181 | var width = widthContained - widthScroll; 182 | 183 | return { width: width, height: width }; 184 | } 185 | 186 | function extend() { 187 | var out = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; 188 | 189 | var args = []; 190 | 191 | Array.prototype.push.apply(args, arguments); 192 | 193 | args.slice(1).forEach(function (obj) { 194 | if (obj) { 195 | for (var key in obj) { 196 | if (({}).hasOwnProperty.call(obj, key)) { 197 | out[key] = obj[key]; 198 | } 199 | } 200 | } 201 | }); 202 | 203 | return out; 204 | } 205 | 206 | function removeClass(el, name) { 207 | if (typeof el.classList !== 'undefined') { 208 | name.split(' ').forEach(function (cls) { 209 | if (cls.trim()) { 210 | el.classList.remove(cls); 211 | } 212 | }); 213 | } else { 214 | var regex = new RegExp('(^| )' + name.split(' ').join('|') + '( |$)', 'gi'); 215 | var className = getClassName(el).replace(regex, ' '); 216 | setClassName(el, className); 217 | } 218 | } 219 | 220 | function addClass(el, name) { 221 | if (typeof el.classList !== 'undefined') { 222 | name.split(' ').forEach(function (cls) { 223 | if (cls.trim()) { 224 | el.classList.add(cls); 225 | } 226 | }); 227 | } else { 228 | removeClass(el, name); 229 | var cls = getClassName(el) + (' ' + name); 230 | setClassName(el, cls); 231 | } 232 | } 233 | 234 | function hasClass(el, name) { 235 | if (typeof el.classList !== 'undefined') { 236 | return el.classList.contains(name); 237 | } 238 | var className = getClassName(el); 239 | return new RegExp('(^| )' + name + '( |$)', 'gi').test(className); 240 | } 241 | 242 | function getClassName(el) { 243 | if (el.className instanceof SVGAnimatedString) { 244 | return el.className.baseVal; 245 | } 246 | return el.className; 247 | } 248 | 249 | function setClassName(el, className) { 250 | el.setAttribute('class', className); 251 | } 252 | 253 | function updateClasses(el, add, all) { 254 | // Of the set of 'all' classes, we need the 'add' classes, and only the 255 | // 'add' classes to be set. 256 | all.forEach(function (cls) { 257 | if (add.indexOf(cls) === -1 && hasClass(el, cls)) { 258 | removeClass(el, cls); 259 | } 260 | }); 261 | 262 | add.forEach(function (cls) { 263 | if (!hasClass(el, cls)) { 264 | addClass(el, cls); 265 | } 266 | }); 267 | } 268 | 269 | var deferred = []; 270 | 271 | var defer = function defer(fn) { 272 | deferred.push(fn); 273 | }; 274 | 275 | var flush = function flush() { 276 | var fn = undefined; 277 | while (fn = deferred.pop()) { 278 | fn(); 279 | } 280 | }; 281 | 282 | var Evented = (function () { 283 | function Evented() { 284 | _classCallCheck(this, Evented); 285 | } 286 | 287 | _createClass(Evented, [{ 288 | key: 'on', 289 | value: function on(event, handler, ctx) { 290 | var once = arguments.length <= 3 || arguments[3] === undefined ? false : arguments[3]; 291 | 292 | if (typeof this.bindings === 'undefined') { 293 | this.bindings = {}; 294 | } 295 | if (typeof this.bindings[event] === 'undefined') { 296 | this.bindings[event] = []; 297 | } 298 | this.bindings[event].push({ handler: handler, ctx: ctx, once: once }); 299 | } 300 | }, { 301 | key: 'once', 302 | value: function once(event, handler, ctx) { 303 | this.on(event, handler, ctx, true); 304 | } 305 | }, { 306 | key: 'off', 307 | value: function off(event, handler) { 308 | if (typeof this.bindings !== 'undefined' && typeof this.bindings[event] !== 'undefined') { 309 | return; 310 | } 311 | 312 | if (typeof handler === 'undefined') { 313 | delete this.bindings[event]; 314 | } else { 315 | var i = 0; 316 | while (i < this.bindings[event].length) { 317 | if (this.bindings[event][i].handler === handler) { 318 | this.bindings[event].splice(i, 1); 319 | } else { 320 | ++i; 321 | } 322 | } 323 | } 324 | } 325 | }, { 326 | key: 'trigger', 327 | value: function trigger(event) { 328 | if (typeof this.bindings !== 'undefined' && this.bindings[event]) { 329 | var i = 0; 330 | 331 | for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { 332 | args[_key - 1] = arguments[_key]; 333 | } 334 | 335 | while (i < this.bindings[event].length) { 336 | var _bindings$event$i = this.bindings[event][i]; 337 | var handler = _bindings$event$i.handler; 338 | var ctx = _bindings$event$i.ctx; 339 | var once = _bindings$event$i.once; 340 | 341 | var context = ctx; 342 | if (typeof context === 'undefined') { 343 | context = this; 344 | } 345 | 346 | handler.apply(context, args); 347 | 348 | if (once) { 349 | this.bindings[event].splice(i, 1); 350 | } else { 351 | ++i; 352 | } 353 | } 354 | } 355 | } 356 | }]); 357 | 358 | return Evented; 359 | })(); 360 | 361 | TetherBase.Utils = { 362 | getScrollParent: getScrollParent, 363 | getBounds: getBounds, 364 | getOffsetParent: getOffsetParent, 365 | extend: extend, 366 | addClass: addClass, 367 | removeClass: removeClass, 368 | hasClass: hasClass, 369 | updateClasses: updateClasses, 370 | defer: defer, 371 | flush: flush, 372 | uniqueId: uniqueId, 373 | Evented: Evented, 374 | getScrollBarSize: getScrollBarSize 375 | }; 376 | /* globals TetherBase, performance */ 377 | 378 | 'use strict'; 379 | 380 | var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })(); 381 | 382 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 383 | 384 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 385 | 386 | if (typeof TetherBase === 'undefined') { 387 | throw new Error('You must include the utils.js file before tether.js'); 388 | } 389 | 390 | var _TetherBase$Utils = TetherBase.Utils; 391 | var getScrollParent = _TetherBase$Utils.getScrollParent; 392 | var getBounds = _TetherBase$Utils.getBounds; 393 | var getOffsetParent = _TetherBase$Utils.getOffsetParent; 394 | var extend = _TetherBase$Utils.extend; 395 | var addClass = _TetherBase$Utils.addClass; 396 | var removeClass = _TetherBase$Utils.removeClass; 397 | var updateClasses = _TetherBase$Utils.updateClasses; 398 | var defer = _TetherBase$Utils.defer; 399 | var flush = _TetherBase$Utils.flush; 400 | var getScrollBarSize = _TetherBase$Utils.getScrollBarSize; 401 | 402 | function within(a, b) { 403 | var diff = arguments.length <= 2 || arguments[2] === undefined ? 1 : arguments[2]; 404 | 405 | return a + diff >= b && b >= a - diff; 406 | } 407 | 408 | var transformKey = (function () { 409 | if (typeof document === 'undefined') { 410 | return ''; 411 | } 412 | var el = document.createElement('div'); 413 | 414 | var transforms = ['transform', 'webkitTransform', 'OTransform', 'MozTransform', 'msTransform']; 415 | for (var i = 0; i < transforms.length; ++i) { 416 | var key = transforms[i]; 417 | if (el.style[key] !== undefined) { 418 | return key; 419 | } 420 | } 421 | })(); 422 | 423 | var tethers = []; 424 | 425 | var position = function position() { 426 | tethers.forEach(function (tether) { 427 | tether.position(false); 428 | }); 429 | flush(); 430 | }; 431 | 432 | function now() { 433 | if (typeof performance !== 'undefined' && typeof performance.now !== 'undefined') { 434 | return performance.now(); 435 | } 436 | return +new Date(); 437 | } 438 | 439 | (function () { 440 | var lastCall = null; 441 | var lastDuration = null; 442 | var pendingTimeout = null; 443 | 444 | var tick = function tick() { 445 | if (typeof lastDuration !== 'undefined' && lastDuration > 16) { 446 | // We voluntarily throttle ourselves if we can't manage 60fps 447 | lastDuration = Math.min(lastDuration - 16, 250); 448 | 449 | // Just in case this is the last event, remember to position just once more 450 | pendingTimeout = setTimeout(tick, 250); 451 | return; 452 | } 453 | 454 | if (typeof lastCall !== 'undefined' && now() - lastCall < 10) { 455 | // Some browsers call events a little too frequently, refuse to run more than is reasonable 456 | return; 457 | } 458 | 459 | if (typeof pendingTimeout !== 'undefined') { 460 | clearTimeout(pendingTimeout); 461 | pendingTimeout = null; 462 | } 463 | 464 | lastCall = now(); 465 | position(); 466 | lastDuration = now() - lastCall; 467 | }; 468 | 469 | if (typeof window !== 'undefined') { 470 | ['resize', 'scroll', 'touchmove'].forEach(function (event) { 471 | window.addEventListener(event, tick); 472 | }); 473 | } 474 | })(); 475 | 476 | var MIRROR_LR = { 477 | center: 'center', 478 | left: 'right', 479 | right: 'left' 480 | }; 481 | 482 | var MIRROR_TB = { 483 | middle: 'middle', 484 | top: 'bottom', 485 | bottom: 'top' 486 | }; 487 | 488 | var OFFSET_MAP = { 489 | top: 0, 490 | left: 0, 491 | middle: '50%', 492 | center: '50%', 493 | bottom: '100%', 494 | right: '100%' 495 | }; 496 | 497 | var autoToFixedAttachment = function autoToFixedAttachment(attachment, relativeToAttachment) { 498 | var left = attachment.left; 499 | var top = attachment.top; 500 | 501 | if (left === 'auto') { 502 | left = MIRROR_LR[relativeToAttachment.left]; 503 | } 504 | 505 | if (top === 'auto') { 506 | top = MIRROR_TB[relativeToAttachment.top]; 507 | } 508 | 509 | return { left: left, top: top }; 510 | }; 511 | 512 | var attachmentToOffset = function attachmentToOffset(attachment) { 513 | var left = attachment.left; 514 | var top = attachment.top; 515 | 516 | if (typeof OFFSET_MAP[attachment.left] !== 'undefined') { 517 | left = OFFSET_MAP[attachment.left]; 518 | } 519 | 520 | if (typeof OFFSET_MAP[attachment.top] !== 'undefined') { 521 | top = OFFSET_MAP[attachment.top]; 522 | } 523 | 524 | return { left: left, top: top }; 525 | }; 526 | 527 | function addOffset() { 528 | var out = { top: 0, left: 0 }; 529 | 530 | for (var _len = arguments.length, offsets = Array(_len), _key = 0; _key < _len; _key++) { 531 | offsets[_key] = arguments[_key]; 532 | } 533 | 534 | offsets.forEach(function (_ref) { 535 | var top = _ref.top; 536 | var left = _ref.left; 537 | 538 | if (typeof top === 'string') { 539 | top = parseFloat(top, 10); 540 | } 541 | if (typeof left === 'string') { 542 | left = parseFloat(left, 10); 543 | } 544 | 545 | out.top += top; 546 | out.left += left; 547 | }); 548 | 549 | return out; 550 | } 551 | 552 | function offsetToPx(offset, size) { 553 | if (typeof offset.left === 'string' && offset.left.indexOf('%') !== -1) { 554 | offset.left = parseFloat(offset.left, 10) / 100 * size.width; 555 | } 556 | if (typeof offset.top === 'string' && offset.top.indexOf('%') !== -1) { 557 | offset.top = parseFloat(offset.top, 10) / 100 * size.height; 558 | } 559 | 560 | return offset; 561 | } 562 | 563 | var parseOffset = function parseOffset(value) { 564 | var _value$split = value.split(' '); 565 | 566 | var _value$split2 = _slicedToArray(_value$split, 2); 567 | 568 | var top = _value$split2[0]; 569 | var left = _value$split2[1]; 570 | 571 | return { top: top, left: left }; 572 | }; 573 | var parseAttachment = parseOffset; 574 | 575 | var TetherClass = (function () { 576 | function TetherClass(options) { 577 | var _this = this; 578 | 579 | _classCallCheck(this, TetherClass); 580 | 581 | this.position = this.position.bind(this); 582 | 583 | tethers.push(this); 584 | 585 | this.history = []; 586 | 587 | this.setOptions(options, false); 588 | 589 | TetherBase.modules.forEach(function (module) { 590 | if (typeof module.initialize !== 'undefined') { 591 | module.initialize.call(_this); 592 | } 593 | }); 594 | 595 | this.position(); 596 | } 597 | 598 | _createClass(TetherClass, [{ 599 | key: 'getClass', 600 | value: function getClass() { 601 | var key = arguments.length <= 0 || arguments[0] === undefined ? '' : arguments[0]; 602 | var classes = this.options.classes; 603 | 604 | if (typeof classes !== 'undefined' && classes[key]) { 605 | return this.options.classes[key]; 606 | } else if (this.options.classPrefix) { 607 | return this.options.classPrefix + '-' + key; 608 | } else { 609 | return key; 610 | } 611 | } 612 | }, { 613 | key: 'setOptions', 614 | value: function setOptions(options) { 615 | var _this2 = this; 616 | 617 | var pos = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1]; 618 | 619 | var defaults = { 620 | offset: '0 0', 621 | targetOffset: '0 0', 622 | targetAttachment: 'auto auto', 623 | classPrefix: 'tether' 624 | }; 625 | 626 | this.options = extend(defaults, options); 627 | 628 | var _options = this.options; 629 | var element = _options.element; 630 | var target = _options.target; 631 | var targetModifier = _options.targetModifier; 632 | 633 | this.element = element; 634 | this.target = target; 635 | this.targetModifier = targetModifier; 636 | 637 | if (this.target === 'viewport') { 638 | this.target = document.body; 639 | this.targetModifier = 'visible'; 640 | } else if (this.target === 'scroll-handle') { 641 | this.target = document.body; 642 | this.targetModifier = 'scroll-handle'; 643 | } 644 | 645 | ['element', 'target'].forEach(function (key) { 646 | if (typeof _this2[key] === 'undefined') { 647 | throw new Error('Tether Error: Both element and target must be defined'); 648 | } 649 | 650 | if (typeof _this2[key].jquery !== 'undefined') { 651 | _this2[key] = _this2[key][0]; 652 | } else if (typeof _this2[key] === 'string') { 653 | _this2[key] = document.querySelector(_this2[key]); 654 | } 655 | }); 656 | 657 | addClass(this.element, this.getClass('element')); 658 | if (!(this.options.addTargetClasses === false)) { 659 | addClass(this.target, this.getClass('target')); 660 | } 661 | 662 | if (!this.options.attachment) { 663 | throw new Error('Tether Error: You must provide an attachment'); 664 | } 665 | 666 | this.targetAttachment = parseAttachment(this.options.targetAttachment); 667 | this.attachment = parseAttachment(this.options.attachment); 668 | this.offset = parseOffset(this.options.offset); 669 | this.targetOffset = parseOffset(this.options.targetOffset); 670 | 671 | if (typeof this.scrollParent !== 'undefined') { 672 | this.disable(); 673 | } 674 | 675 | if (this.targetModifier === 'scroll-handle') { 676 | this.scrollParent = this.target; 677 | } else { 678 | this.scrollParent = getScrollParent(this.target); 679 | } 680 | 681 | if (!(this.options.enabled === false)) { 682 | this.enable(pos); 683 | } 684 | } 685 | }, { 686 | key: 'getTargetBounds', 687 | value: function getTargetBounds() { 688 | if (typeof this.targetModifier !== 'undefined') { 689 | if (this.targetModifier === 'visible') { 690 | if (this.target === document.body) { 691 | return { top: pageYOffset, left: pageXOffset, height: innerHeight, width: innerWidth }; 692 | } else { 693 | var bounds = getBounds(this.target); 694 | 695 | var out = { 696 | height: bounds.height, 697 | width: bounds.width, 698 | top: bounds.top, 699 | left: bounds.left 700 | }; 701 | 702 | out.height = Math.min(out.height, bounds.height - (pageYOffset - bounds.top)); 703 | out.height = Math.min(out.height, bounds.height - (bounds.top + bounds.height - (pageYOffset + innerHeight))); 704 | out.height = Math.min(innerHeight, out.height); 705 | out.height -= 2; 706 | 707 | out.width = Math.min(out.width, bounds.width - (pageXOffset - bounds.left)); 708 | out.width = Math.min(out.width, bounds.width - (bounds.left + bounds.width - (pageXOffset + innerWidth))); 709 | out.width = Math.min(innerWidth, out.width); 710 | out.width -= 2; 711 | 712 | if (out.top < pageYOffset) { 713 | out.top = pageYOffset; 714 | } 715 | if (out.left < pageXOffset) { 716 | out.left = pageXOffset; 717 | } 718 | 719 | return out; 720 | } 721 | } else if (this.targetModifier === 'scroll-handle') { 722 | var bounds = undefined; 723 | var target = this.target; 724 | if (target === document.body) { 725 | target = document.documentElement; 726 | 727 | bounds = { 728 | left: pageXOffset, 729 | top: pageYOffset, 730 | height: innerHeight, 731 | width: innerWidth 732 | }; 733 | } else { 734 | bounds = getBounds(target); 735 | } 736 | 737 | var style = getComputedStyle(target); 738 | 739 | var hasBottomScroll = target.scrollWidth > target.clientWidth || [style.overflow, style.overflowX].indexOf('scroll') >= 0 || this.target !== document.body; 740 | 741 | var scrollBottom = 0; 742 | if (hasBottomScroll) { 743 | scrollBottom = 15; 744 | } 745 | 746 | var height = bounds.height - parseFloat(style.borderTopWidth) - parseFloat(style.borderBottomWidth) - scrollBottom; 747 | 748 | var out = { 749 | width: 15, 750 | height: height * 0.975 * (height / target.scrollHeight), 751 | left: bounds.left + bounds.width - parseFloat(style.borderLeftWidth) - 15 752 | }; 753 | 754 | var fitAdj = 0; 755 | if (height < 408 && this.target === document.body) { 756 | fitAdj = -0.00011 * Math.pow(height, 2) - 0.00727 * height + 22.58; 757 | } 758 | 759 | if (this.target !== document.body) { 760 | out.height = Math.max(out.height, 24); 761 | } 762 | 763 | var scrollPercentage = this.target.scrollTop / (target.scrollHeight - height); 764 | out.top = scrollPercentage * (height - out.height - fitAdj) + bounds.top + parseFloat(style.borderTopWidth); 765 | 766 | if (this.target === document.body) { 767 | out.height = Math.max(out.height, 24); 768 | } 769 | 770 | return out; 771 | } 772 | } else { 773 | return getBounds(this.target); 774 | } 775 | } 776 | }, { 777 | key: 'clearCache', 778 | value: function clearCache() { 779 | this._cache = {}; 780 | } 781 | }, { 782 | key: 'cache', 783 | value: function cache(k, getter) { 784 | // More than one module will often need the same DOM info, so 785 | // we keep a cache which is cleared on each position call 786 | if (typeof this._cache === 'undefined') { 787 | this._cache = {}; 788 | } 789 | 790 | if (typeof this._cache[k] === 'undefined') { 791 | this._cache[k] = getter.call(this); 792 | } 793 | 794 | return this._cache[k]; 795 | } 796 | }, { 797 | key: 'enable', 798 | value: function enable() { 799 | var pos = arguments.length <= 0 || arguments[0] === undefined ? true : arguments[0]; 800 | 801 | if (!(this.options.addTargetClasses === false)) { 802 | addClass(this.target, this.getClass('enabled')); 803 | } 804 | addClass(this.element, this.getClass('enabled')); 805 | this.enabled = true; 806 | 807 | if (this.scrollParent !== document) { 808 | this.scrollParent.addEventListener('scroll', this.position); 809 | } 810 | 811 | if (pos) { 812 | this.position(); 813 | } 814 | } 815 | }, { 816 | key: 'disable', 817 | value: function disable() { 818 | removeClass(this.target, this.getClass('enabled')); 819 | removeClass(this.element, this.getClass('enabled')); 820 | this.enabled = false; 821 | 822 | if (typeof this.scrollParent !== 'undefined') { 823 | this.scrollParent.removeEventListener('scroll', this.position); 824 | } 825 | } 826 | }, { 827 | key: 'destroy', 828 | value: function destroy() { 829 | var _this3 = this; 830 | 831 | this.disable(); 832 | 833 | tethers.forEach(function (tether, i) { 834 | if (tether === _this3) { 835 | tethers.splice(i, 1); 836 | return; 837 | } 838 | }); 839 | } 840 | }, { 841 | key: 'updateAttachClasses', 842 | value: function updateAttachClasses(elementAttach, targetAttach) { 843 | var _this4 = this; 844 | 845 | elementAttach = elementAttach || this.attachment; 846 | targetAttach = targetAttach || this.targetAttachment; 847 | var sides = ['left', 'top', 'bottom', 'right', 'middle', 'center']; 848 | 849 | if (typeof this._addAttachClasses !== 'undefined' && this._addAttachClasses.length) { 850 | // updateAttachClasses can be called more than once in a position call, so 851 | // we need to clean up after ourselves such that when the last defer gets 852 | // ran it doesn't add any extra classes from previous calls. 853 | this._addAttachClasses.splice(0, this._addAttachClasses.length); 854 | } 855 | 856 | if (typeof this._addAttachClasses === 'undefined') { 857 | this._addAttachClasses = []; 858 | } 859 | var add = this._addAttachClasses; 860 | 861 | if (elementAttach.top) { 862 | add.push(this.getClass('element-attached') + '-' + elementAttach.top); 863 | } 864 | if (elementAttach.left) { 865 | add.push(this.getClass('element-attached') + '-' + elementAttach.left); 866 | } 867 | if (targetAttach.top) { 868 | add.push(this.getClass('target-attached') + '-' + targetAttach.top); 869 | } 870 | if (targetAttach.left) { 871 | add.push(this.getClass('target-attached') + '-' + targetAttach.left); 872 | } 873 | 874 | var all = []; 875 | sides.forEach(function (side) { 876 | all.push(_this4.getClass('element-attached') + '-' + side); 877 | all.push(_this4.getClass('target-attached') + '-' + side); 878 | }); 879 | 880 | defer(function () { 881 | if (!(typeof _this4._addAttachClasses !== 'undefined')) { 882 | return; 883 | } 884 | 885 | updateClasses(_this4.element, _this4._addAttachClasses, all); 886 | if (!(_this4.options.addTargetClasses === false)) { 887 | updateClasses(_this4.target, _this4._addAttachClasses, all); 888 | } 889 | 890 | delete _this4._addAttachClasses; 891 | }); 892 | } 893 | }, { 894 | key: 'position', 895 | value: function position() { 896 | var _this5 = this; 897 | 898 | var flushChanges = arguments.length <= 0 || arguments[0] === undefined ? true : arguments[0]; 899 | 900 | // flushChanges commits the changes immediately, leave true unless you are positioning multiple 901 | // tethers (in which case call Tether.Utils.flush yourself when you're done) 902 | 903 | if (!this.enabled) { 904 | return; 905 | } 906 | 907 | this.clearCache(); 908 | 909 | // Turn 'auto' attachments into the appropriate corner or edge 910 | var targetAttachment = autoToFixedAttachment(this.targetAttachment, this.attachment); 911 | 912 | this.updateAttachClasses(this.attachment, targetAttachment); 913 | 914 | var elementPos = this.cache('element-bounds', function () { 915 | return getBounds(_this5.element); 916 | }); 917 | 918 | var width = elementPos.width; 919 | var height = elementPos.height; 920 | 921 | if (width === 0 && height === 0 && typeof this.lastSize !== 'undefined') { 922 | var _lastSize = this.lastSize; 923 | 924 | // We cache the height and width to make it possible to position elements that are 925 | // getting hidden. 926 | width = _lastSize.width; 927 | height = _lastSize.height; 928 | } else { 929 | this.lastSize = { width: width, height: height }; 930 | } 931 | 932 | var targetPos = this.cache('target-bounds', function () { 933 | return _this5.getTargetBounds(); 934 | }); 935 | var targetSize = targetPos; 936 | 937 | // Get an actual px offset from the attachment 938 | var offset = offsetToPx(attachmentToOffset(this.attachment), { width: width, height: height }); 939 | var targetOffset = offsetToPx(attachmentToOffset(targetAttachment), targetSize); 940 | 941 | var manualOffset = offsetToPx(this.offset, { width: width, height: height }); 942 | var manualTargetOffset = offsetToPx(this.targetOffset, targetSize); 943 | 944 | // Add the manually provided offset 945 | offset = addOffset(offset, manualOffset); 946 | targetOffset = addOffset(targetOffset, manualTargetOffset); 947 | 948 | // It's now our goal to make (element position + offset) == (target position + target offset) 949 | var left = targetPos.left + targetOffset.left - offset.left; 950 | var top = targetPos.top + targetOffset.top - offset.top; 951 | 952 | for (var i = 0; i < TetherBase.modules.length; ++i) { 953 | var _module2 = TetherBase.modules[i]; 954 | var ret = _module2.position.call(this, { 955 | left: left, 956 | top: top, 957 | targetAttachment: targetAttachment, 958 | targetPos: targetPos, 959 | elementPos: elementPos, 960 | offset: offset, 961 | targetOffset: targetOffset, 962 | manualOffset: manualOffset, 963 | manualTargetOffset: manualTargetOffset, 964 | scrollbarSize: scrollbarSize, 965 | attachment: this.attachment 966 | }); 967 | 968 | if (ret === false) { 969 | return false; 970 | } else if (typeof ret === 'undefined' || typeof ret !== 'object') { 971 | continue; 972 | } else { 973 | top = ret.top; 974 | left = ret.left; 975 | } 976 | } 977 | 978 | // We describe the position three different ways to give the optimizer 979 | // a chance to decide the best possible way to position the element 980 | // with the fewest repaints. 981 | var next = { 982 | // It's position relative to the page (absolute positioning when 983 | // the element is a child of the body) 984 | page: { 985 | top: top, 986 | left: left 987 | }, 988 | 989 | // It's position relative to the viewport (fixed positioning) 990 | viewport: { 991 | top: top - pageYOffset, 992 | bottom: pageYOffset - top - height + innerHeight, 993 | left: left - pageXOffset, 994 | right: pageXOffset - left - width + innerWidth 995 | } 996 | }; 997 | 998 | var scrollbarSize = undefined; 999 | if (document.body.scrollWidth > window.innerWidth) { 1000 | scrollbarSize = this.cache('scrollbar-size', getScrollBarSize); 1001 | next.viewport.bottom -= scrollbarSize.height; 1002 | } 1003 | 1004 | if (document.body.scrollHeight > window.innerHeight) { 1005 | scrollbarSize = this.cache('scrollbar-size', getScrollBarSize); 1006 | next.viewport.right -= scrollbarSize.width; 1007 | } 1008 | 1009 | if (['', 'static'].indexOf(document.body.style.position) === -1 || ['', 'static'].indexOf(document.body.parentElement.style.position) === -1) { 1010 | // Absolute positioning in the body will be relative to the page, not the 'initial containing block' 1011 | next.page.bottom = document.body.scrollHeight - top - height; 1012 | next.page.right = document.body.scrollWidth - left - width; 1013 | } 1014 | 1015 | if (typeof this.options.optimizations !== 'undefined' && this.options.optimizations.moveElement !== false && !(typeof this.targetModifier !== 'undefined')) { 1016 | (function () { 1017 | var offsetParent = _this5.cache('target-offsetparent', function () { 1018 | return getOffsetParent(_this5.target); 1019 | }); 1020 | var offsetPosition = _this5.cache('target-offsetparent-bounds', function () { 1021 | return getBounds(offsetParent); 1022 | }); 1023 | var offsetParentStyle = getComputedStyle(offsetParent); 1024 | var offsetParentSize = offsetPosition; 1025 | 1026 | var offsetBorder = {}; 1027 | ['Top', 'Left', 'Bottom', 'Right'].forEach(function (side) { 1028 | offsetBorder[side.toLowerCase()] = parseFloat(offsetParentStyle['border' + side + 'Width']); 1029 | }); 1030 | 1031 | offsetPosition.right = document.body.scrollWidth - offsetPosition.left - offsetParentSize.width + offsetBorder.right; 1032 | offsetPosition.bottom = document.body.scrollHeight - offsetPosition.top - offsetParentSize.height + offsetBorder.bottom; 1033 | 1034 | if (next.page.top >= offsetPosition.top + offsetBorder.top && next.page.bottom >= offsetPosition.bottom) { 1035 | if (next.page.left >= offsetPosition.left + offsetBorder.left && next.page.right >= offsetPosition.right) { 1036 | // We're within the visible part of the target's scroll parent 1037 | var scrollTop = offsetParent.scrollTop; 1038 | var scrollLeft = offsetParent.scrollLeft; 1039 | 1040 | // It's position relative to the target's offset parent (absolute positioning when 1041 | // the element is moved to be a child of the target's offset parent). 1042 | next.offset = { 1043 | top: next.page.top - offsetPosition.top + scrollTop - offsetBorder.top, 1044 | left: next.page.left - offsetPosition.left + scrollLeft - offsetBorder.left 1045 | }; 1046 | } 1047 | } 1048 | })(); 1049 | } 1050 | 1051 | // We could also travel up the DOM and try each containing context, rather than only 1052 | // looking at the body, but we're gonna get diminishing returns. 1053 | 1054 | this.move(next); 1055 | 1056 | this.history.unshift(next); 1057 | 1058 | if (this.history.length > 3) { 1059 | this.history.pop(); 1060 | } 1061 | 1062 | if (flushChanges) { 1063 | flush(); 1064 | } 1065 | 1066 | return true; 1067 | } 1068 | 1069 | // THE ISSUE 1070 | }, { 1071 | key: 'move', 1072 | value: function move(pos) { 1073 | var _this6 = this; 1074 | 1075 | if (!(typeof this.element.parentNode !== 'undefined')) { 1076 | return; 1077 | } 1078 | 1079 | var same = {}; 1080 | 1081 | for (var type in pos) { 1082 | same[type] = {}; 1083 | 1084 | for (var key in pos[type]) { 1085 | var found = false; 1086 | 1087 | for (var i = 0; i < this.history.length; ++i) { 1088 | var point = this.history[i]; 1089 | if (typeof point[type] !== 'undefined' && !within(point[type][key], pos[type][key])) { 1090 | found = true; 1091 | break; 1092 | } 1093 | } 1094 | 1095 | if (!found) { 1096 | same[type][key] = true; 1097 | } 1098 | } 1099 | } 1100 | 1101 | var css = { top: '', left: '', right: '', bottom: '' }; 1102 | 1103 | var transcribe = function transcribe(_same, _pos) { 1104 | var hasOptimizations = typeof _this6.options.optimizations !== 'undefined'; 1105 | var gpu = hasOptimizations ? _this6.options.optimizations.gpu : null; 1106 | if (gpu !== false) { 1107 | var yPos = undefined, 1108 | xPos = undefined; 1109 | if (_same.top) { 1110 | css.top = 0; 1111 | yPos = _pos.top; 1112 | } else { 1113 | css.bottom = 0; 1114 | yPos = -_pos.bottom; 1115 | } 1116 | 1117 | if (_same.left) { 1118 | css.left = 0; 1119 | xPos = _pos.left; 1120 | } else { 1121 | css.right = 0; 1122 | xPos = -_pos.right; 1123 | } 1124 | 1125 | css[transformKey] = 'translateX(' + Math.round(xPos) + 'px) translateY(' + Math.round(yPos) + 'px)'; 1126 | 1127 | if (transformKey !== 'msTransform') { 1128 | // The Z transform will keep this in the GPU (faster, and prevents artifacts), 1129 | // but IE9 doesn't support 3d transforms and will choke. 1130 | css[transformKey] += " translateZ(0)"; 1131 | } 1132 | } else { 1133 | if (_same.top) { 1134 | css.top = _pos.top + 'px'; 1135 | } else { 1136 | css.bottom = _pos.bottom + 'px'; 1137 | } 1138 | 1139 | if (_same.left) { 1140 | css.left = _pos.left + 'px'; 1141 | } else { 1142 | css.right = _pos.right + 'px'; 1143 | } 1144 | } 1145 | }; 1146 | 1147 | var moved = false; 1148 | if ((same.page.top || same.page.bottom) && (same.page.left || same.page.right)) { 1149 | css.position = 'absolute'; 1150 | transcribe(same.page, pos.page); 1151 | } else if ((same.viewport.top || same.viewport.bottom) && (same.viewport.left || same.viewport.right)) { 1152 | css.position = 'fixed'; 1153 | transcribe(same.viewport, pos.viewport); 1154 | } else if (typeof same.offset !== 'undefined' && same.offset.top && same.offset.left) { 1155 | (function () { 1156 | css.position = 'absolute'; 1157 | var offsetParent = _this6.cache('target-offsetparent', function () { 1158 | return getOffsetParent(_this6.target); 1159 | }); 1160 | 1161 | if (getOffsetParent(_this6.element) !== offsetParent) { 1162 | defer(function () { 1163 | _this6.element.parentNode.removeChild(_this6.element); 1164 | offsetParent.appendChild(_this6.element); 1165 | }); 1166 | } 1167 | 1168 | transcribe(same.offset, pos.offset); 1169 | moved = true; 1170 | })(); 1171 | } else { 1172 | css.position = 'absolute'; 1173 | transcribe({ top: true, left: true }, pos.page); 1174 | } 1175 | 1176 | if (!moved) { 1177 | var offsetParentIsBody = true; 1178 | var currentNode = this.element.parentNode; 1179 | while (currentNode && currentNode.tagName !== 'BODY') { 1180 | if (getComputedStyle(currentNode).position !== 'static') { 1181 | offsetParentIsBody = false; 1182 | break; 1183 | } 1184 | 1185 | currentNode = currentNode.parentNode; 1186 | } 1187 | 1188 | if (!offsetParentIsBody) { 1189 | this.element.parentNode.removeChild(this.element); 1190 | document.body.appendChild(this.element); 1191 | } 1192 | } 1193 | 1194 | // Any css change will trigger a repaint, so let's avoid one if nothing changed 1195 | var writeCSS = {}; 1196 | var write = false; 1197 | for (var key in css) { 1198 | var val = css[key]; 1199 | var elVal = this.element.style[key]; 1200 | 1201 | if (elVal !== '' && val !== '' && ['top', 'left', 'bottom', 'right'].indexOf(key) >= 0) { 1202 | elVal = parseFloat(elVal); 1203 | val = parseFloat(val); 1204 | } 1205 | 1206 | if (elVal !== val) { 1207 | write = true; 1208 | writeCSS[key] = val; 1209 | } 1210 | } 1211 | 1212 | if (write) { 1213 | defer(function () { 1214 | extend(_this6.element.style, writeCSS); 1215 | }); 1216 | } 1217 | } 1218 | }]); 1219 | 1220 | return TetherClass; 1221 | })(); 1222 | 1223 | TetherClass.modules = []; 1224 | 1225 | TetherBase.position = position; 1226 | 1227 | var Tether = extend(TetherClass, TetherBase); 1228 | /* globals TetherBase */ 1229 | 1230 | 'use strict'; 1231 | 1232 | var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })(); 1233 | 1234 | var _TetherBase$Utils = TetherBase.Utils; 1235 | var getBounds = _TetherBase$Utils.getBounds; 1236 | var extend = _TetherBase$Utils.extend; 1237 | var updateClasses = _TetherBase$Utils.updateClasses; 1238 | var defer = _TetherBase$Utils.defer; 1239 | 1240 | var BOUNDS_FORMAT = ['left', 'top', 'right', 'bottom']; 1241 | 1242 | function getBoundingRect(tether, to) { 1243 | if (to === 'scrollParent') { 1244 | to = tether.scrollParent; 1245 | } else if (to === 'window') { 1246 | to = [pageXOffset, pageYOffset, innerWidth + pageXOffset, innerHeight + pageYOffset]; 1247 | } 1248 | 1249 | if (to === document) { 1250 | to = to.documentElement; 1251 | } 1252 | 1253 | if (typeof to.nodeType !== 'undefined') { 1254 | (function () { 1255 | var size = getBounds(to); 1256 | var pos = size; 1257 | var style = getComputedStyle(to); 1258 | 1259 | to = [pos.left, pos.top, size.width + pos.left, size.height + pos.top]; 1260 | 1261 | BOUNDS_FORMAT.forEach(function (side, i) { 1262 | side = side[0].toUpperCase() + side.substr(1); 1263 | if (side === 'Top' || side === 'Left') { 1264 | to[i] += parseFloat(style['border' + side + 'Width']); 1265 | } else { 1266 | to[i] -= parseFloat(style['border' + side + 'Width']); 1267 | } 1268 | }); 1269 | })(); 1270 | } 1271 | 1272 | return to; 1273 | } 1274 | 1275 | TetherBase.modules.push({ 1276 | position: function position(_ref) { 1277 | var _this = this; 1278 | 1279 | var top = _ref.top; 1280 | var left = _ref.left; 1281 | var targetAttachment = _ref.targetAttachment; 1282 | 1283 | if (!this.options.constraints) { 1284 | return true; 1285 | } 1286 | 1287 | var _cache = this.cache('element-bounds', function () { 1288 | return getBounds(_this.element); 1289 | }); 1290 | 1291 | var height = _cache.height; 1292 | var width = _cache.width; 1293 | 1294 | if (width === 0 && height === 0 && typeof this.lastSize !== 'undefined') { 1295 | var _lastSize = this.lastSize; 1296 | 1297 | // Handle the item getting hidden as a result of our positioning without glitching 1298 | // the classes in and out 1299 | width = _lastSize.width; 1300 | height = _lastSize.height; 1301 | } 1302 | 1303 | var targetSize = this.cache('target-bounds', function () { 1304 | return _this.getTargetBounds(); 1305 | }); 1306 | 1307 | var targetHeight = targetSize.height; 1308 | var targetWidth = targetSize.width; 1309 | 1310 | var allClasses = [this.getClass('pinned'), this.getClass('out-of-bounds')]; 1311 | 1312 | this.options.constraints.forEach(function (constraint) { 1313 | var outOfBoundsClass = constraint.outOfBoundsClass; 1314 | var pinnedClass = constraint.pinnedClass; 1315 | 1316 | if (outOfBoundsClass) { 1317 | allClasses.push(outOfBoundsClass); 1318 | } 1319 | if (pinnedClass) { 1320 | allClasses.push(pinnedClass); 1321 | } 1322 | }); 1323 | 1324 | allClasses.forEach(function (cls) { 1325 | ['left', 'top', 'right', 'bottom'].forEach(function (side) { 1326 | allClasses.push(cls + '-' + side); 1327 | }); 1328 | }); 1329 | 1330 | var addClasses = []; 1331 | 1332 | var tAttachment = extend({}, targetAttachment); 1333 | var eAttachment = extend({}, this.attachment); 1334 | 1335 | this.options.constraints.forEach(function (constraint) { 1336 | var to = constraint.to; 1337 | var attachment = constraint.attachment; 1338 | var pin = constraint.pin; 1339 | 1340 | if (typeof attachment === 'undefined') { 1341 | attachment = ''; 1342 | } 1343 | 1344 | var changeAttachX = undefined, 1345 | changeAttachY = undefined; 1346 | if (attachment.indexOf(' ') >= 0) { 1347 | var _attachment$split = attachment.split(' '); 1348 | 1349 | var _attachment$split2 = _slicedToArray(_attachment$split, 2); 1350 | 1351 | changeAttachY = _attachment$split2[0]; 1352 | changeAttachX = _attachment$split2[1]; 1353 | } else { 1354 | changeAttachX = changeAttachY = attachment; 1355 | } 1356 | 1357 | var bounds = getBoundingRect(_this, to); 1358 | 1359 | if (changeAttachY === 'target' || changeAttachY === 'both') { 1360 | if (top < bounds[1] && tAttachment.top === 'top') { 1361 | top += targetHeight; 1362 | tAttachment.top = 'bottom'; 1363 | } 1364 | 1365 | if (top + height > bounds[3] && tAttachment.top === 'bottom') { 1366 | top -= targetHeight; 1367 | tAttachment.top = 'top'; 1368 | } 1369 | } 1370 | 1371 | if (changeAttachY === 'together') { 1372 | if (top < bounds[1] && tAttachment.top === 'top') { 1373 | if (eAttachment.top === 'bottom') { 1374 | top += targetHeight; 1375 | tAttachment.top = 'bottom'; 1376 | 1377 | top += height; 1378 | eAttachment.top = 'top'; 1379 | } else if (eAttachment.top === 'top') { 1380 | top += targetHeight; 1381 | tAttachment.top = 'bottom'; 1382 | 1383 | top -= height; 1384 | eAttachment.top = 'bottom'; 1385 | } 1386 | } 1387 | 1388 | if (top + height > bounds[3] && tAttachment.top === 'bottom') { 1389 | if (eAttachment.top === 'top') { 1390 | top -= targetHeight; 1391 | tAttachment.top = 'top'; 1392 | 1393 | top -= height; 1394 | eAttachment.top = 'bottom'; 1395 | } else if (eAttachment.top === 'bottom') { 1396 | top -= targetHeight; 1397 | tAttachment.top = 'top'; 1398 | 1399 | top += height; 1400 | eAttachment.top = 'top'; 1401 | } 1402 | } 1403 | 1404 | if (tAttachment.top === 'middle') { 1405 | if (top + height > bounds[3] && eAttachment.top === 'top') { 1406 | top -= height; 1407 | eAttachment.top = 'bottom'; 1408 | } else if (top < bounds[1] && eAttachment.top === 'bottom') { 1409 | top += height; 1410 | eAttachment.top = 'top'; 1411 | } 1412 | } 1413 | } 1414 | 1415 | if (changeAttachX === 'target' || changeAttachX === 'both') { 1416 | if (left < bounds[0] && tAttachment.left === 'left') { 1417 | left += targetWidth; 1418 | tAttachment.left = 'right'; 1419 | } 1420 | 1421 | if (left + width > bounds[2] && tAttachment.left === 'right') { 1422 | left -= targetWidth; 1423 | tAttachment.left = 'left'; 1424 | } 1425 | } 1426 | 1427 | if (changeAttachX === 'together') { 1428 | if (left < bounds[0] && tAttachment.left === 'left') { 1429 | if (eAttachment.left === 'right') { 1430 | left += targetWidth; 1431 | tAttachment.left = 'right'; 1432 | 1433 | left += width; 1434 | eAttachment.left = 'left'; 1435 | } else if (eAttachment.left === 'left') { 1436 | left += targetWidth; 1437 | tAttachment.left = 'right'; 1438 | 1439 | left -= width; 1440 | eAttachment.left = 'right'; 1441 | } 1442 | } else if (left + width > bounds[2] && tAttachment.left === 'right') { 1443 | if (eAttachment.left === 'left') { 1444 | left -= targetWidth; 1445 | tAttachment.left = 'left'; 1446 | 1447 | left -= width; 1448 | eAttachment.left = 'right'; 1449 | } else if (eAttachment.left === 'right') { 1450 | left -= targetWidth; 1451 | tAttachment.left = 'left'; 1452 | 1453 | left += width; 1454 | eAttachment.left = 'left'; 1455 | } 1456 | } else if (tAttachment.left === 'center') { 1457 | if (left + width > bounds[2] && eAttachment.left === 'left') { 1458 | left -= width; 1459 | eAttachment.left = 'right'; 1460 | } else if (left < bounds[0] && eAttachment.left === 'right') { 1461 | left += width; 1462 | eAttachment.left = 'left'; 1463 | } 1464 | } 1465 | } 1466 | 1467 | if (changeAttachY === 'element' || changeAttachY === 'both') { 1468 | if (top < bounds[1] && eAttachment.top === 'bottom') { 1469 | top += height; 1470 | eAttachment.top = 'top'; 1471 | } 1472 | 1473 | if (top + height > bounds[3] && eAttachment.top === 'top') { 1474 | top -= height; 1475 | eAttachment.top = 'bottom'; 1476 | } 1477 | } 1478 | 1479 | if (changeAttachX === 'element' || changeAttachX === 'both') { 1480 | if (left < bounds[0]) { 1481 | if (eAttachment.left === 'right') { 1482 | left += width; 1483 | eAttachment.left = 'left'; 1484 | } else if (eAttachment.left === 'center') { 1485 | left += width / 2; 1486 | eAttachment.left = 'left'; 1487 | } 1488 | } 1489 | 1490 | if (left + width > bounds[2]) { 1491 | if (eAttachment.left === 'left') { 1492 | left -= width; 1493 | eAttachment.left = 'right'; 1494 | } else if (eAttachment.left === 'center') { 1495 | left -= width / 2; 1496 | eAttachment.left = 'right'; 1497 | } 1498 | } 1499 | } 1500 | 1501 | if (typeof pin === 'string') { 1502 | pin = pin.split(',').map(function (p) { 1503 | return p.trim(); 1504 | }); 1505 | } else if (pin === true) { 1506 | pin = ['top', 'left', 'right', 'bottom']; 1507 | } 1508 | 1509 | pin = pin || []; 1510 | 1511 | var pinned = []; 1512 | var oob = []; 1513 | 1514 | if (top < bounds[1]) { 1515 | if (pin.indexOf('top') >= 0) { 1516 | top = bounds[1]; 1517 | pinned.push('top'); 1518 | } else { 1519 | oob.push('top'); 1520 | } 1521 | } 1522 | 1523 | if (top + height > bounds[3]) { 1524 | if (pin.indexOf('bottom') >= 0) { 1525 | top = bounds[3] - height; 1526 | pinned.push('bottom'); 1527 | } else { 1528 | oob.push('bottom'); 1529 | } 1530 | } 1531 | 1532 | if (left < bounds[0]) { 1533 | if (pin.indexOf('left') >= 0) { 1534 | left = bounds[0]; 1535 | pinned.push('left'); 1536 | } else { 1537 | oob.push('left'); 1538 | } 1539 | } 1540 | 1541 | if (left + width > bounds[2]) { 1542 | if (pin.indexOf('right') >= 0) { 1543 | left = bounds[2] - width; 1544 | pinned.push('right'); 1545 | } else { 1546 | oob.push('right'); 1547 | } 1548 | } 1549 | 1550 | if (pinned.length) { 1551 | (function () { 1552 | var pinnedClass = undefined; 1553 | if (typeof _this.options.pinnedClass !== 'undefined') { 1554 | pinnedClass = _this.options.pinnedClass; 1555 | } else { 1556 | pinnedClass = _this.getClass('pinned'); 1557 | } 1558 | 1559 | addClasses.push(pinnedClass); 1560 | pinned.forEach(function (side) { 1561 | addClasses.push(pinnedClass + '-' + side); 1562 | }); 1563 | })(); 1564 | } 1565 | 1566 | if (oob.length) { 1567 | (function () { 1568 | var oobClass = undefined; 1569 | if (typeof _this.options.outOfBoundsClass !== 'undefined') { 1570 | oobClass = _this.options.outOfBoundsClass; 1571 | } else { 1572 | oobClass = _this.getClass('out-of-bounds'); 1573 | } 1574 | 1575 | addClasses.push(oobClass); 1576 | oob.forEach(function (side) { 1577 | addClasses.push(oobClass + '-' + side); 1578 | }); 1579 | })(); 1580 | } 1581 | 1582 | if (pinned.indexOf('left') >= 0 || pinned.indexOf('right') >= 0) { 1583 | eAttachment.left = tAttachment.left = false; 1584 | } 1585 | if (pinned.indexOf('top') >= 0 || pinned.indexOf('bottom') >= 0) { 1586 | eAttachment.top = tAttachment.top = false; 1587 | } 1588 | 1589 | if (tAttachment.top !== targetAttachment.top || tAttachment.left !== targetAttachment.left || eAttachment.top !== _this.attachment.top || eAttachment.left !== _this.attachment.left) { 1590 | _this.updateAttachClasses(eAttachment, tAttachment); 1591 | } 1592 | }); 1593 | 1594 | defer(function () { 1595 | if (!(_this.options.addTargetClasses === false)) { 1596 | updateClasses(_this.target, addClasses, allClasses); 1597 | } 1598 | updateClasses(_this.element, addClasses, allClasses); 1599 | }); 1600 | 1601 | return { top: top, left: left }; 1602 | } 1603 | }); 1604 | /* globals TetherBase */ 1605 | 1606 | 'use strict'; 1607 | 1608 | var _TetherBase$Utils = TetherBase.Utils; 1609 | var getBounds = _TetherBase$Utils.getBounds; 1610 | var updateClasses = _TetherBase$Utils.updateClasses; 1611 | var defer = _TetherBase$Utils.defer; 1612 | 1613 | TetherBase.modules.push({ 1614 | position: function position(_ref) { 1615 | var _this = this; 1616 | 1617 | var top = _ref.top; 1618 | var left = _ref.left; 1619 | 1620 | var _cache = this.cache('element-bounds', function () { 1621 | return getBounds(_this.element); 1622 | }); 1623 | 1624 | var height = _cache.height; 1625 | var width = _cache.width; 1626 | 1627 | var targetPos = this.getTargetBounds(); 1628 | 1629 | var bottom = top + height; 1630 | var right = left + width; 1631 | 1632 | var abutted = []; 1633 | if (top <= targetPos.bottom && bottom >= targetPos.top) { 1634 | ['left', 'right'].forEach(function (side) { 1635 | var targetPosSide = targetPos[side]; 1636 | if (targetPosSide === left || targetPosSide === right) { 1637 | abutted.push(side); 1638 | } 1639 | }); 1640 | } 1641 | 1642 | if (left <= targetPos.right && right >= targetPos.left) { 1643 | ['top', 'bottom'].forEach(function (side) { 1644 | var targetPosSide = targetPos[side]; 1645 | if (targetPosSide === top || targetPosSide === bottom) { 1646 | abutted.push(side); 1647 | } 1648 | }); 1649 | } 1650 | 1651 | var allClasses = []; 1652 | var addClasses = []; 1653 | 1654 | var sides = ['left', 'top', 'right', 'bottom']; 1655 | allClasses.push(this.getClass('abutted')); 1656 | sides.forEach(function (side) { 1657 | allClasses.push(_this.getClass('abutted') + '-' + side); 1658 | }); 1659 | 1660 | if (abutted.length) { 1661 | addClasses.push(this.getClass('abutted')); 1662 | } 1663 | 1664 | abutted.forEach(function (side) { 1665 | addClasses.push(_this.getClass('abutted') + '-' + side); 1666 | }); 1667 | 1668 | defer(function () { 1669 | if (!(_this.options.addTargetClasses === false)) { 1670 | updateClasses(_this.target, addClasses, allClasses); 1671 | } 1672 | updateClasses(_this.element, addClasses, allClasses); 1673 | }); 1674 | 1675 | return true; 1676 | } 1677 | }); 1678 | /* globals TetherBase */ 1679 | 1680 | 'use strict'; 1681 | 1682 | var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })(); 1683 | 1684 | TetherBase.modules.push({ 1685 | position: function position(_ref) { 1686 | var top = _ref.top; 1687 | var left = _ref.left; 1688 | 1689 | if (!this.options.shift) { 1690 | return; 1691 | } 1692 | 1693 | var shift = this.options.shift; 1694 | if (typeof this.options.shift === 'function') { 1695 | shift = this.options.shift.call(this, { top: top, left: left }); 1696 | } 1697 | 1698 | var shiftTop = undefined, 1699 | shiftLeft = undefined; 1700 | if (typeof shift === 'string') { 1701 | shift = shift.split(' '); 1702 | shift[1] = shift[1] || shift[0]; 1703 | 1704 | var _shift = shift; 1705 | 1706 | var _shift2 = _slicedToArray(_shift, 2); 1707 | 1708 | shiftTop = _shift2[0]; 1709 | shiftLeft = _shift2[1]; 1710 | 1711 | shiftTop = parseFloat(shiftTop, 10); 1712 | shiftLeft = parseFloat(shiftLeft, 10); 1713 | } else { 1714 | shiftTop = shift.top; 1715 | shiftLeft = shift.left; 1716 | } 1717 | 1718 | top += shiftTop; 1719 | left += shiftLeft; 1720 | 1721 | return { top: top, left: left }; 1722 | } 1723 | }); 1724 | return Tether; 1725 | 1726 | })); 1727 | -------------------------------------------------------------------------------- /client/imports/my.css: -------------------------------------------------------------------------------- 1 | .my-import-test { 2 | color: red; 3 | } -------------------------------------------------------------------------------- /client/main.css: -------------------------------------------------------------------------------- 1 | // here we import bootstrap.scss from node_modules/bootstrap/scss 2 | // we can ommit .scss extension because of postcss-easy-import extensions option 3 | // see package.json file for details 4 | @import 'bootstrap'; 5 | 6 | // you can still import standard css files 7 | // just add .import.css extension or place it in 'imports' directory 8 | @import './imports/my'; 9 | @import './my2'; 10 | 11 | // test of a scss-like comment 12 | // this is possible because of postcss-scss parser 13 | .carousel { 14 | max-width: 640px; 15 | margin: 0 auto; 16 | } 17 | 18 | -------------------------------------------------------------------------------- /client/main.html: -------------------------------------------------------------------------------- 1 | 2 | simple 3 | 4 | 5 | 6 |

Bootstrap 4 with Scss and PostCSS - demo

7 | 8 | {{> hello}} 9 | {{> info}} 10 | 11 | 12 | 18 | 19 | -------------------------------------------------------------------------------- /client/main.js: -------------------------------------------------------------------------------- 1 | import { Template } from 'meteor/templating'; 2 | import { ReactiveVar } from 'meteor/reactive-var'; 3 | 4 | // import all Bootstrap js plugins 5 | import 'bootstrap'; 6 | 7 | import './main.html'; 8 | 9 | Template.hello.onCreated(function helloOnCreated() { 10 | // counter starts at 0 11 | this.counter = new ReactiveVar(0); 12 | }); 13 | 14 | Template.hello.helpers({ 15 | counter() { 16 | return Template.instance().counter.get(); 17 | }, 18 | }); 19 | 20 | Template.hello.events({ 21 | 'click button'(event, instance) { 22 | // increment the counter when button is clicked 23 | instance.counter.set(instance.counter.get() + 1); 24 | }, 25 | }); 26 | -------------------------------------------------------------------------------- /client/my2.import.css: -------------------------------------------------------------------------------- 1 | .my-import2 { 2 | color: blue; 3 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bootstrap_postcss_test", 3 | "private": true, 4 | "scripts": { 5 | "start": "meteor run" 6 | }, 7 | "dependencies": { 8 | "bootstrap": "^4.0.0-alpha.2", 9 | "meteor-node-stubs": "~0.2.0" 10 | }, 11 | "devDependencies": { 12 | "autoprefixer": "^6.3.4", 13 | "postcss-easy-import": "^1.0.1", 14 | "postcss-nested": "^1.0.0", 15 | "postcss-sassy-mixins": "^2.0.0", 16 | "postcss-scss": "^0.1.7", 17 | "postcss-simple-vars": "^1.2.0" 18 | }, 19 | "postcss": { 20 | "plugins": { 21 | "postcss-easy-import": { 22 | "extensions": [ 23 | ".css", 24 | ".scss", 25 | ".import.css" 26 | ], 27 | "prefix": "_" 28 | }, 29 | "postcss-simple-vars": {}, 30 | "postcss-sassy-mixins": {}, 31 | "postcss-nested": {}, 32 | "autoprefixer": { 33 | "browsers": [ 34 | "last 2 versions" 35 | ] 36 | } 37 | }, 38 | "parser": "postcss-scss" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /public/arch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juliancwirko/meteor-bootstrap-postcss-test/a3d01f1b21b18abc4dc58a1705f5bbfa3fa41eb6/public/arch.jpg -------------------------------------------------------------------------------- /public/nature.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juliancwirko/meteor-bootstrap-postcss-test/a3d01f1b21b18abc4dc58a1705f5bbfa3fa41eb6/public/nature.jpg -------------------------------------------------------------------------------- /public/people.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juliancwirko/meteor-bootstrap-postcss-test/a3d01f1b21b18abc4dc58a1705f5bbfa3fa41eb6/public/people.jpg -------------------------------------------------------------------------------- /server/main.js: -------------------------------------------------------------------------------- 1 | import { Meteor } from 'meteor/meteor'; 2 | 3 | Meteor.startup(() => { 4 | // code to run on server at startup 5 | }); 6 | --------------------------------------------------------------------------------