Skip to content

Commit

Permalink
rewrite extend
Browse files Browse the repository at this point in the history
  • Loading branch information
chemerisuk committed Sep 24, 2017
1 parent f122132 commit e254107
Show file tree
Hide file tree
Showing 11 changed files with 188 additions and 248 deletions.
22 changes: 16 additions & 6 deletions gulpfile.js
Expand Up @@ -66,12 +66,22 @@ gulp.task("test", ["compile", "lint-test"], function(done) {
dir: "coverage/"
}
};
}

config.configFile = karmaConfig;

new karma.Server(config, function(resultCode) {
done(resultCode ? new gutil.PluginError("karma", "Tests failed") : null);
}).start();
});

gulp.task("browsers", ["compile", "lint-test"], function(done) {
var config = {preprocessors: []};

if (argv.ie10 || argv.ie11) {
config.browsers = ["IE" + (argv.ie10 ? "10" : "11") + " - Win7"];
} else {
if (argv.all || process.env.npm_package_version) {
config.browsers = ["ChromeHeadless", "Chrome", "Opera", "Safari", "Firefox"];
} else if (argv.ie10 || argv.ie11) {
config.browsers = ["IE" + (argv.ie10 ? "10" : "11") + " - Win7"];
}
config.browsers = ["Chrome", "Safari", "Firefox"];
}

config.configFile = karmaConfig;
Expand Down Expand Up @@ -128,7 +138,7 @@ gulp.task("bower", function() {
.pipe(gulp.dest("./"));
});

gulp.task("dist", ["test", "bower"], function(done) {
gulp.task("dist", ["browsers", "bower"], function(done) {
gulp.src("build/better-dom.js")
// clienup multiline comments: jsdocs, directives etc.
.pipe(replace(/\/\*([\s\S]*?)\*\/\s+/gm, ""))
Expand Down
1 change: 1 addition & 0 deletions src/const.js
Expand Up @@ -13,6 +13,7 @@ export const RETURN_THIS = function() { return this };
export const RETURN_TRUE = () => true;
export const RETURN_FALSE = () => false;
export const VENDOR_PREFIXES = ["Webkit", "O", "Moz", "ms"];
export const FAKE_ANIMATION_NAME = "<%= prop('v') %>";

// feature checks
export const WEBKIT_PREFIX = WINDOW.WebKitAnimationEvent ? "-webkit-" : "";
113 changes: 81 additions & 32 deletions src/document/extend.js
@@ -1,17 +1,33 @@
import { $Document } from "../document/index";
import { $Element } from "../element/index";
import { each, keys } from "../util/index";
import { WEBKIT_PREFIX, WINDOW } from "../const";
import { keys, each } from "../util/index";
import { WEBKIT_PREFIX, WINDOW, FAKE_ANIMATION_NAME } from "../const";
import { DocumentTypeError } from "../errors";
import ExtensionHandler from "../util/extensionhandler";
import SelectorMatcher from "../util/selectormatcher";

// Inspired by trick discovered by Daniel Buchner:
// https://github.com/csuwldcat/SelectorListener

var cssText;
const extensions = [];
const EVENT_TYPE = WEBKIT_PREFIX ? "webkitAnimationStart" : "animationstart";
const CSS_IMPORT_TEXT = [
WEBKIT_PREFIX + "animation-name:" + FAKE_ANIMATION_NAME + " !important",
WEBKIT_PREFIX + "animation-duration:1ms !important"
].join(";");

cssText = WEBKIT_PREFIX + "animation-name:<%= prop('DOM') %> !important;";
cssText += WEBKIT_PREFIX + "animation-duration:1ms !important";
function applyLiveExtension(definition, node) {
const el = $Element(node);
const ctr = definition.constructor;
// apply all element mixins
Object.keys(definition).forEach((mixinName) => {
const mixinProperty = definition[mixinName];
if (mixinProperty !== ctr) {
el[mixinName] = mixinProperty;
}
});

if (ctr) ctr.call(el);
}

/**
* Declare a live extension
Expand Down Expand Up @@ -59,32 +75,65 @@ $Document.prototype.extend = function(selector, definition) {
throw new DocumentTypeError("extend", arguments);
}

var mappings = this["<%= prop('mappings') %>"];

if (!mappings) {
this["<%= prop('mappings') %>"] = mappings = [];
// declare the fake animation on the first DOM.extend method call
this.importStyles("@" + WEBKIT_PREFIX + "keyframes <%= prop('DOM') %>", "from {opacity:.99} to {opacity:1}");
// use capturing to suppress internal animationstart events
node.addEventListener(WEBKIT_PREFIX ? "webkitAnimationStart" : "animationstart", (e) => {
if (e.animationName === "<%= prop('DOM') %>") {
mappings.forEach((ext) => { ext(e.target) });
// this is an internal event - stop it immediately
e.stopPropagation();
}
}, true);
extensions.push([SelectorMatcher(selector), definition]);
// use capturing to suppress internal animationstart events
node.addEventListener(EVENT_TYPE, (e) => {
if (e.animationName === FAKE_ANIMATION_NAME) {
e.stopPropagation(); // this is an internal event
// prevent any future events
e.target.style.setProperty(WEBKIT_PREFIX + "animation-name", "none", "important");

applyLiveExtension(definition, e.target);
}
}, true);

// initialize extension manually to make sure that all elements
// have appropriate methods before they are used in other DOM.extend
// also fix cases when a matched element already has another LE
each.call(node.querySelectorAll(selector), (node) => {
// prevent any future events
node.style.setProperty(WEBKIT_PREFIX + "animation-name", "none", "important");
// use timeout to invoke constructor safe and async
WINDOW.setTimeout(() => {
applyLiveExtension(definition, node);
}, 0);
});

// subscribe selector to a fake animation
this.importStyles(selector, CSS_IMPORT_TEXT);
};

/**
* Return {@link $Element} initialized with all existing live extensions.
* Also exposes private functions that do not usually exist. Accepts the
* same arguments as {@link DOM.create}
* @memberof $Document#
* @alias $Document#mock
* @param {String} value EmmetString or HTMLString
* @param {Object|Array} [varMap] key/value map of variables
* @return {$Element} a mocked instance
* @see $Document#create
*/
$Document.prototype.mock = function(content) {
if (!content) return new $Element();

var result = this.create(content),
applyExtensions = (node) => {
extensions.forEach((args) => {
const matcher = args[0];
const definition = args[1];

if (matcher(node)) {
applyLiveExtension(definition, node);
}
});

each.call(node.children, applyExtensions);
};

if (extensions.length) {
applyExtensions(result["<%= prop() %>"]);
}

var ext = ExtensionHandler(selector, definition, mappings.length);

mappings.push(ext);
// live extensions are always async - append CSS asynchronously
WINDOW.setTimeout(() => {
// initialize extension manually to make sure that all elements
// have appropriate methods before they are used in other DOM.extend.
// Also fixes legacy IEs when the HTC behavior is already attached
each.call(node.querySelectorAll(selector), ext);
// MUST be after querySelectorAll because of legacy IEs quirks
this.importStyles(selector, cssText);
}, 0);
return result;
};
9 changes: 8 additions & 1 deletion src/document/index.js
@@ -1,5 +1,8 @@
import { $Node } from "../node/index";
import { UNKNOWN_NODE, DOCUMENT_NODE } from "../const";
import { WINDOW, UNKNOWN_NODE, DOCUMENT_NODE, WEBKIT_PREFIX, FAKE_ANIMATION_NAME } from "../const";

const LIVE_EXTENSION_KEYFRAMES = "@" + WEBKIT_PREFIX + "keyframes " + FAKE_ANIMATION_NAME;
const LIVE_EXTENSION_KEYFRAMES_BODY = "from {opacity:.99} to {opacity:1}";

/**
* Used to represent a document in better-dom
Expand All @@ -9,6 +12,10 @@ import { UNKNOWN_NODE, DOCUMENT_NODE } from "../const";
export function $Document(node) {
if (this instanceof $Document) {
$Node.call(this, node);
// declare fake animation for live extensions
WINDOW.setTimeout(() => {
this.importStyles(LIVE_EXTENSION_KEYFRAMES, LIVE_EXTENSION_KEYFRAMES_BODY);
}, 0);
} else if (node) {
// create a wrapper only once for each native element
return node["<%= prop() %>"] || new $Document(node);
Expand Down
32 changes: 0 additions & 32 deletions src/document/mock.js

This file was deleted.

5 changes: 1 addition & 4 deletions src/element/visibility.js
@@ -1,11 +1,8 @@
import { WINDOW } from "../const";
import { MethodError } from "../errors";
import { $Element } from "../element/index";
import { computeStyle } from "../util/index";
import { computeStyle, raf } from "../util/index";
import AnimationHandler from "../util/animationhandler";

const raf = WINDOW.requestAnimationFrame;

function makeMethod(methodName, condition) {
return function(animationName, callback) {
if (typeof animationName !== "string") {
Expand Down
5 changes: 2 additions & 3 deletions src/index.js
Expand Up @@ -9,7 +9,8 @@ import { $Element } from "./element/index";
* @namespace DOM
* @extends {$Document}
*/
export var DOM = new $Document(WINDOW.document);
const DOM = new $Document(WINDOW.document);
const _DOM = WINDOW.DOM;

/**
* Create an instance of {@link $Element} or {@link $Document} for a native element
Expand All @@ -34,8 +35,6 @@ DOM.$ = (node) => {
}
};

var _DOM = WINDOW.DOM;

/**
* Restore previous DOM namespace
* @memberof DOM
Expand Down
36 changes: 0 additions & 36 deletions src/util/extensionhandler.js

This file was deleted.

12 changes: 12 additions & 0 deletions test/animation.html
Expand Up @@ -78,6 +78,7 @@
</style>
</head>
<body>
<h1>test foo</h1>
<h2>With removing from the DOM</h2>
<div class="bar">
<div class="bar-item" style="background: yellow"></div>
Expand Down Expand Up @@ -135,6 +136,17 @@ <h2>Animation test</h2>
animatableBar.hide("fade");
}
}, 1000);

DOM.extend("h1", {
constructor() {
console.log(this, this.test());

debugger;
},
test() {
return 12345;
}
})
</script>
</body>
</html>
61 changes: 0 additions & 61 deletions test/legacy.html

This file was deleted.

0 comments on commit e254107

Please sign in to comment.