text:
// Compiled by ClojureScript 1.10.339 {}
goog.provide('reagent.core');
goog.require('cljs.core');
goog.require('react');
goog.require('reagent.impl.template');
goog.require('reagent.impl.component');
goog.require('reagent.impl.util');
goog.require('reagent.impl.batching');
goog.require('reagent.ratom');
goog.require('reagent.debug');
goog.require('reagent.interop');
goog.require('reagent.dom');
reagent.core.global$module$react = goog.global["React"];
reagent.core.is_client = reagent.impl.util.is_client;
/**
 * Create a native React element, by calling React.createElement directly.
 *
 *   That means the second argument must be a javascript object (or nil), and
 *   that any Reagent hiccup forms must be processed with as-element. For example
 *   like this:
 *
 *  (r/create-element "div" #js{:className "foo"}
 *     "Hi " (r/as-element [:strong "world!"])
 *
 *   which is equivalent to
 *
 *  [:div.foo "Hi" [:strong "world!"]]
 */
reagent.core.create_element = (function reagent$core$create_element(var_args){
var G__22934 = arguments.length;
switch (G__22934) {
case 1:
return reagent.core.create_element.cljs$core$IFn$_invoke$arity$1((arguments[(0)]));

break;
case 2:
return reagent.core.create_element.cljs$core$IFn$_invoke$arity$2((arguments[(0)]),(arguments[(1)]));

break;
case 3:
return reagent.core.create_element.cljs$core$IFn$_invoke$arity$3((arguments[(0)]),(arguments[(1)]),(arguments[(2)]));

break;
default:
var args_arr__4546__auto__ = [];
var len__4531__auto___22936 = arguments.length;
var i__4532__auto___22937 = (0);
while(true){
if((i__4532__auto___22937 < len__4531__auto___22936)){
args_arr__4546__auto__.push((arguments[i__4532__auto___22937]));

var G__22938 = (i__4532__auto___22937 + (1));
i__4532__auto___22937 = G__22938;
continue;
} else {
}
break;
}

var argseq__4547__auto__ = (new cljs.core.IndexedSeq(args_arr__4546__auto__.slice((3)),(0),null));
return reagent.core.create_element.cljs$core$IFn$_invoke$arity$variadic((arguments[(0)]),(arguments[(1)]),(arguments[(2)]),argseq__4547__auto__);

}
});

reagent.core.create_element.cljs$core$IFn$_invoke$arity$1 = (function (type){
return reagent.core.create_element.call(null,type,null);
});

reagent.core.create_element.cljs$core$IFn$_invoke$arity$2 = (function (type,props){
if(!(cljs.core.map_QMARK_.call(null,props))){
} else {
throw (new Error(["Assert failed: ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(["Expected a JS object, not ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(cljs.core.pr_str.call(null,props))].join('')),"\n","(clojure.core/not (clojure.core/map? props))"].join('')));
}

return reagent.core.global$module$react.createElement.call(null,type,props);
});

reagent.core.create_element.cljs$core$IFn$_invoke$arity$3 = (function (type,props,child){
if(!(cljs.core.map_QMARK_.call(null,props))){
} else {
throw (new Error(["Assert failed: ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(["Expected a JS object, not ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(cljs.core.pr_str.call(null,props))].join('')),"\n","(clojure.core/not (clojure.core/map? props))"].join('')));
}

return reagent.core.global$module$react.createElement.call(null,type,props,child);
});

reagent.core.create_element.cljs$core$IFn$_invoke$arity$variadic = (function (type,props,child,children){
if(!(cljs.core.map_QMARK_.call(null,props))){
} else {
throw (new Error(["Assert failed: ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(["Expected a JS object, not ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(cljs.core.pr_str.call(null,props))].join('')),"\n","(clojure.core/not (clojure.core/map? props))"].join('')));
}

return cljs.core.apply.call(null,reagent.core.global$module$react.createElement,type,props,child,children);
});

/** @this {Function} */
reagent.core.create_element.cljs$lang$applyTo = (function (seq22930){
var G__22931 = cljs.core.first.call(null,seq22930);
var seq22930__$1 = cljs.core.next.call(null,seq22930);
var G__22932 = cljs.core.first.call(null,seq22930__$1);
var seq22930__$2 = cljs.core.next.call(null,seq22930__$1);
var G__22933 = cljs.core.first.call(null,seq22930__$2);
var seq22930__$3 = cljs.core.next.call(null,seq22930__$2);
var self__4518__auto__ = this;
return self__4518__auto__.cljs$core$IFn$_invoke$arity$variadic(G__22931,G__22932,G__22933,seq22930__$3);
});

reagent.core.create_element.cljs$lang$maxFixedArity = (3);

/**
 * Turns a vector of Hiccup syntax into a React element. Returns form
 *   unchanged if it is not a vector.
 */
reagent.core.as_element = (function reagent$core$as_element(form){
return reagent.impl.template.as_element.call(null,form);
});
/**
 * Returns an adapter for a native React class, that may be used
 *   just like a Reagent component function or class in Hiccup forms.
 */
reagent.core.adapt_react_class = (function reagent$core$adapt_react_class(c){
if(cljs.core.truth_(c)){
} else {
throw (new Error(["Assert failed: ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(["Component"," must not be nil"].join('')),"\n","c"].join('')));
}

return reagent.impl.template.adapt_react_class.call(null,c);
});
/**
 * Returns an adapter for a Reagent component, that may be used from
 *   React, for example in JSX. A single argument, props, is passed to
 *   the component, converted to a map.
 */
reagent.core.reactify_component = (function reagent$core$reactify_component(c){
if(cljs.core.truth_(c)){
} else {
throw (new Error(["Assert failed: ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(["Component"," must not be nil"].join('')),"\n","c"].join('')));
}

return reagent.impl.component.reactify_component.call(null,c);
});
/**
 * Render a Reagent component into the DOM. The first argument may be
 *   either a vector (using Reagent's Hiccup syntax), or a React element.
 *   The second argument should be a DOM node.
 *
 *   Optionally takes a callback that is called when the component is in place.
 *
 *   Returns the mounted component instance.
 */
reagent.core.render = (function reagent$core$render(var_args){
var G__22940 = arguments.length;
switch (G__22940) {
case 2:
return reagent.core.render.cljs$core$IFn$_invoke$arity$2((arguments[(0)]),(arguments[(1)]));

break;
case 3:
return reagent.core.render.cljs$core$IFn$_invoke$arity$3((arguments[(0)]),(arguments[(1)]),(arguments[(2)]));

break;
default:
throw (new Error(["Invalid arity: ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(arguments.length)].join('')));

}
});

reagent.core.render.cljs$core$IFn$_invoke$arity$2 = (function (comp,container){
return reagent.dom.render.call(null,comp,container);
});

reagent.core.render.cljs$core$IFn$_invoke$arity$3 = (function (comp,container,callback){
return reagent.dom.render.call(null,comp,container,callback);
});

reagent.core.render.cljs$lang$maxFixedArity = 3;

/**
 * Remove a component from the given DOM node.
 */
reagent.core.unmount_component_at_node = (function reagent$core$unmount_component_at_node(container){
return reagent.dom.unmount_component_at_node.call(null,container);
});
reagent.core.as_component = reagent.core.as_element;
reagent.core.render_component = reagent.core.render;
/**
 * Force re-rendering of all mounted Reagent components. This is
 *   probably only useful in a development environment, when you want to
 *   update components in response to some dynamic changes to code.
 *
 *   Note that force-update-all may not update root components. This
 *   happens if a component 'foo' is mounted with `(render [foo])` (since
 *   functions are passed by value, and not by reference, in
 *   ClojureScript). To get around this you'll have to introduce a layer
 *   of indirection, for example by using `(render [#'foo])` instead.
 */
reagent.core.force_update_all = (function reagent$core$force_update_all(){
reagent.ratom.flush_BANG_.call(null);

reagent.dom.force_update_all.call(null);

return reagent.impl.batching.flush_after_render.call(null);
});
goog.exportSymbol('reagent.core.force_update_all', reagent.core.force_update_all);
/**
 * Create a component, React style. Should be called with a map,
 *   looking like this:
 *
 *  {:get-initial-state (fn [this])
 *   :component-will-receive-props (fn [this new-argv])
 *   :should-component-update (fn [this old-argv new-argv])
 *   :component-will-mount (fn [this])
 *   :component-did-mount (fn [this])
 *   :component-will-update (fn [this new-argv])
 *   :component-did-update (fn [this old-argv])
 *   :component-will-unmount (fn [this])
 *   :reagent-render (fn [args....])}   ;; or :render (fn [this])
 *
 *   Everything is optional, except either :reagent-render or :render.
 */
reagent.core.create_class = (function reagent$core$create_class(spec){
return reagent.impl.component.create_class.call(null,spec);
});
/**
 * Returns the current React component (a.k.a this) in a component
 *   function.
 */
reagent.core.current_component = (function reagent$core$current_component(){
return reagent.impl.component._STAR_current_component_STAR_;
});
/**
 * Returns an atom containing a components state.
 */
reagent.core.state_atom = (function reagent$core$state_atom(this$){
if(reagent.impl.component.reagent_component_QMARK_.call(null,this$)){
} else {
throw (new Error(["Assert failed: ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(["Expected a reagent component, not ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(cljs.core.pr_str.call(null,this$))].join('')),"\n","(comp/reagent-component? this)"].join('')));
}

return reagent.impl.component.state_atom.call(null,this$);
});
/**
 * Returns the state of a component, as set with replace-state or set-state.
 *   Equivalent to (deref (r/state-atom this))
 */
reagent.core.state = (function reagent$core$state(this$){
if(reagent.impl.component.reagent_component_QMARK_.call(null,this$)){
} else {
throw (new Error(["Assert failed: ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(["Expected a reagent component, not ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(cljs.core.pr_str.call(null,this$))].join('')),"\n","(comp/reagent-component? this)"].join('')));
}

return cljs.core.deref.call(null,reagent.core.state_atom.call(null,this$));
});
/**
 * Set state of a component.
 *   Equivalent to (reset! (state-atom this) new-state)
 */
reagent.core.replace_state = (function reagent$core$replace_state(this$,new_state){
if(reagent.impl.component.reagent_component_QMARK_.call(null,this$)){
} else {
throw (new Error(["Assert failed: ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(["Expected a reagent component, not ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(cljs.core.pr_str.call(null,this$))].join('')),"\n","(comp/reagent-component? this)"].join('')));
}

if((((new_state == null)) || (cljs.core.map_QMARK_.call(null,new_state)))){
} else {
throw (new Error(["Assert failed: ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(["Expected a valid new state, not ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(cljs.core.pr_str.call(null,new_state))].join('')),"\n","(clojure.core/or (clojure.core/nil? new-state) (clojure.core/map? new-state))"].join('')));
}

return cljs.core.reset_BANG_.call(null,reagent.core.state_atom.call(null,this$),new_state);
});
/**
 * Merge component state with new-state.
 *   Equivalent to (swap! (state-atom this) merge new-state)
 */
reagent.core.set_state = (function reagent$core$set_state(this$,new_state){
if(reagent.impl.component.reagent_component_QMARK_.call(null,this$)){
} else {
throw (new Error(["Assert failed: ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(["Expected a reagent component, not ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(cljs.core.pr_str.call(null,this$))].join('')),"\n","(comp/reagent-component? this)"].join('')));
}

if((((new_state == null)) || (cljs.core.map_QMARK_.call(null,new_state)))){
} else {
throw (new Error(["Assert failed: ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(["Expected a valid new state, not ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(cljs.core.pr_str.call(null,new_state))].join('')),"\n","(clojure.core/or (clojure.core/nil? new-state) (clojure.core/map? new-state))"].join('')));
}

return cljs.core.swap_BANG_.call(null,reagent.core.state_atom.call(null,this$),cljs.core.merge,new_state);
});
/**
 * Force a component to re-render immediately.
 *
 *   If the second argument is true, child components will also be
 *   re-rendered, even is their arguments have not changed.
 */
reagent.core.force_update = (function reagent$core$force_update(var_args){
var G__22943 = arguments.length;
switch (G__22943) {
case 1:
return reagent.core.force_update.cljs$core$IFn$_invoke$arity$1((arguments[(0)]));

break;
case 2:
return reagent.core.force_update.cljs$core$IFn$_invoke$arity$2((arguments[(0)]),(arguments[(1)]));

break;
default:
throw (new Error(["Invalid arity: ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(arguments.length)].join('')));

}
});

reagent.core.force_update.cljs$core$IFn$_invoke$arity$1 = (function (this$){
return reagent.core.force_update.call(null,this$,false);
});

reagent.core.force_update.cljs$core$IFn$_invoke$arity$2 = (function (this$,deep){
reagent.ratom.flush_BANG_.call(null);

reagent.impl.util.force_update.call(null,this$,deep);

return reagent.impl.batching.flush_after_render.call(null);
});

reagent.core.force_update.cljs$lang$maxFixedArity = 2;

/**
 * Returns the props passed to a component.
 */
reagent.core.props = (function reagent$core$props(this$){
if(reagent.impl.component.reagent_component_QMARK_.call(null,this$)){
} else {
throw (new Error(["Assert failed: ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(["Expected a reagent component, not ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(cljs.core.pr_str.call(null,this$))].join('')),"\n","(comp/reagent-component? this)"].join('')));
}

return reagent.impl.component.get_props.call(null,this$);
});
/**
 * Returns the children passed to a component.
 */
reagent.core.children = (function reagent$core$children(this$){
if(reagent.impl.component.reagent_component_QMARK_.call(null,this$)){
} else {
throw (new Error(["Assert failed: ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(["Expected a reagent component, not ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(cljs.core.pr_str.call(null,this$))].join('')),"\n","(comp/reagent-component? this)"].join('')));
}

return reagent.impl.component.get_children.call(null,this$);
});
/**
 * Returns the entire Hiccup form passed to the component.
 */
reagent.core.argv = (function reagent$core$argv(this$){
if(reagent.impl.component.reagent_component_QMARK_.call(null,this$)){
} else {
throw (new Error(["Assert failed: ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(["Expected a reagent component, not ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(cljs.core.pr_str.call(null,this$))].join('')),"\n","(comp/reagent-component? this)"].join('')));
}

return reagent.impl.component.get_argv.call(null,this$);
});
/**
 * Returns the root DOM node of a mounted component.
 */
reagent.core.dom_node = (function reagent$core$dom_node(this$){
return reagent.dom.dom_node.call(null,this$);
});
/**
 * Utility function that merges two maps, handling :class and :style
 *   specially, like React's transferPropsTo.
 */
reagent.core.merge_props = (function reagent$core$merge_props(defaults,props){
return reagent.impl.util.merge_props.call(null,defaults,props);
});
/**
 * Render dirty components immediately to the DOM.
 *
 *   Note that this may not work in event handlers, since React.js does
 *   batching of updates there.
 */
reagent.core.flush = (function reagent$core$flush(){
return reagent.impl.batching.flush.call(null);
});
/**
 * Like clojure.core/atom, except that it keeps track of derefs.
 *   Reagent components that derefs one of these are automatically
 *   re-rendered.
 */
reagent.core.atom = (function reagent$core$atom(var_args){
var G__22948 = arguments.length;
switch (G__22948) {
case 1:
return reagent.core.atom.cljs$core$IFn$_invoke$arity$1((arguments[(0)]));

break;
default:
var args_arr__4546__auto__ = [];
var len__4531__auto___22950 = arguments.length;
var i__4532__auto___22951 = (0);
while(true){
if((i__4532__auto___22951 < len__4531__auto___22950)){
args_arr__4546__auto__.push((arguments[i__4532__auto___22951]));

var G__22952 = (i__4532__auto___22951 + (1));
i__4532__auto___22951 = G__22952;
continue;
} else {
}
break;
}

var argseq__4547__auto__ = (new cljs.core.IndexedSeq(args_arr__4546__auto__.slice((1)),(0),null));
return reagent.core.atom.cljs$core$IFn$_invoke$arity$variadic((arguments[(0)]),argseq__4547__auto__);

}
});

reagent.core.atom.cljs$core$IFn$_invoke$arity$1 = (function (x){
return reagent.ratom.atom.call(null,x);
});

reagent.core.atom.cljs$core$IFn$_invoke$arity$variadic = (function (x,rest){
return cljs.core.apply.call(null,reagent.ratom.atom,x,rest);
});

/** @this {Function} */
reagent.core.atom.cljs$lang$applyTo = (function (seq22946){
var G__22947 = cljs.core.first.call(null,seq22946);
var seq22946__$1 = cljs.core.next.call(null,seq22946);
var self__4518__auto__ = this;
return self__4518__auto__.cljs$core$IFn$_invoke$arity$variadic(G__22947,seq22946__$1);
});

reagent.core.atom.cljs$lang$maxFixedArity = (1);

/**
 * Takes a function and optional arguments, and returns a derefable
 *   containing the output of that function. If the function derefs
 *   Reagent atoms (or track, etc), the value will be updated whenever
 *   the atom changes.
 *
 *   In other words, @(track foo bar) will produce the same result
 *   as (foo bar), but foo will only be called again when the atoms it
 *   depends on changes, and will only trigger updates of components when
 *   its result changes.
 *
 *   track is lazy, i.e the function is only evaluated on deref.
 */
reagent.core.track = (function reagent$core$track(var_args){
var args__4534__auto__ = [];
var len__4531__auto___22955 = arguments.length;
var i__4532__auto___22956 = (0);
while(true){
if((i__4532__auto___22956 < len__4531__auto___22955)){
args__4534__auto__.push((arguments[i__4532__auto___22956]));

var G__22957 = (i__4532__auto___22956 + (1));
i__4532__auto___22956 = G__22957;
continue;
} else {
}
break;
}

var argseq__4535__auto__ = ((((1) < args__4534__auto__.length))?(new cljs.core.IndexedSeq(args__4534__auto__.slice((1)),(0),null)):null);
return reagent.core.track.cljs$core$IFn$_invoke$arity$variadic((arguments[(0)]),argseq__4535__auto__);
});

reagent.core.track.cljs$core$IFn$_invoke$arity$variadic = (function (f,args){
if(cljs.core.ifn_QMARK_.call(null,f)){
} else {
throw (new Error("Assert failed: (ifn? f)"));
}

return reagent.ratom.make_track.call(null,f,args);
});

reagent.core.track.cljs$lang$maxFixedArity = (1);

/** @this {Function} */
reagent.core.track.cljs$lang$applyTo = (function (seq22953){
var G__22954 = cljs.core.first.call(null,seq22953);
var seq22953__$1 = cljs.core.next.call(null,seq22953);
var self__4518__auto__ = this;
return self__4518__auto__.cljs$core$IFn$_invoke$arity$variadic(G__22954,seq22953__$1);
});

/**
 * An eager version of track. The function passed is called
 *   immediately, and continues to be called when needed, until stopped
 *   with dispose!.
 */
reagent.core.track_BANG_ = (function reagent$core$track_BANG_(var_args){
var args__4534__auto__ = [];
var len__4531__auto___22960 = arguments.length;
var i__4532__auto___22961 = (0);
while(true){
if((i__4532__auto___22961 < len__4531__auto___22960)){
args__4534__auto__.push((arguments[i__4532__auto___22961]));

var G__22962 = (i__4532__auto___22961 + (1));
i__4532__auto___22961 = G__22962;
continue;
} else {
}
break;
}

var argseq__4535__auto__ = ((((1) < args__4534__auto__.length))?(new cljs.core.IndexedSeq(args__4534__auto__.slice((1)),(0),null)):null);
return reagent.core.track_BANG_.cljs$core$IFn$_invoke$arity$variadic((arguments[(0)]),argseq__4535__auto__);
});

reagent.core.track_BANG_.cljs$core$IFn$_invoke$arity$variadic = (function (f,args){
if(cljs.core.ifn_QMARK_.call(null,f)){
} else {
throw (new Error("Assert failed: (ifn? f)"));
}

return reagent.ratom.make_track_BANG_.call(null,f,args);
});

reagent.core.track_BANG_.cljs$lang$maxFixedArity = (1);

/** @this {Function} */
reagent.core.track_BANG_.cljs$lang$applyTo = (function (seq22958){
var G__22959 = cljs.core.first.call(null,seq22958);
var seq22958__$1 = cljs.core.next.call(null,seq22958);
var self__4518__auto__ = this;
return self__4518__auto__.cljs$core$IFn$_invoke$arity$variadic(G__22959,seq22958__$1);
});

/**
 * Stop the result of track! from updating.
 */
reagent.core.dispose_BANG_ = (function reagent$core$dispose_BANG_(x){
return reagent.ratom.dispose_BANG_.call(null,x);
});
/**
 * Provide a combination of value and callback, that looks like an atom.
 *
 *   The first argument can be any value, that will be returned when the
 *   result is deref'ed.
 *
 *   The second argument should be a function, that is called with the
 *   optional extra arguments provided to wrap, and the new value of the
 *   resulting 'atom'.
 *
 *   Use for example like this:
 *
 *   (wrap (:foo @state)
 *      swap! state assoc :foo)
 *
 *   Probably useful only for passing to child components.
 */
reagent.core.wrap = (function reagent$core$wrap(var_args){
var args__4534__auto__ = [];
var len__4531__auto___22966 = arguments.length;
var i__4532__auto___22967 = (0);
while(true){
if((i__4532__auto___22967 < len__4531__auto___22966)){
args__4534__auto__.push((arguments[i__4532__auto___22967]));

var G__22968 = (i__4532__auto___22967 + (1));
i__4532__auto___22967 = G__22968;
continue;
} else {
}
break;
}

var argseq__4535__auto__ = ((((2) < args__4534__auto__.length))?(new cljs.core.IndexedSeq(args__4534__auto__.slice((2)),(0),null)):null);
return reagent.core.wrap.cljs$core$IFn$_invoke$arity$variadic((arguments[(0)]),(arguments[(1)]),argseq__4535__auto__);
});

reagent.core.wrap.cljs$core$IFn$_invoke$arity$variadic = (function (value,reset_fn,args){
if(cljs.core.ifn_QMARK_.call(null,reset_fn)){
} else {
throw (new Error(["Assert failed: ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(["Expected something callable, not ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(cljs.core.pr_str.call(null,reset_fn))].join('')),"\n","(clojure.core/ifn? reset-fn)"].join('')));
}

return reagent.ratom.make_wrapper.call(null,value,reset_fn,args);
});

reagent.core.wrap.cljs$lang$maxFixedArity = (2);

/** @this {Function} */
reagent.core.wrap.cljs$lang$applyTo = (function (seq22963){
var G__22964 = cljs.core.first.call(null,seq22963);
var seq22963__$1 = cljs.core.next.call(null,seq22963);
var G__22965 = cljs.core.first.call(null,seq22963__$1);
var seq22963__$2 = cljs.core.next.call(null,seq22963__$1);
var self__4518__auto__ = this;
return self__4518__auto__.cljs$core$IFn$_invoke$arity$variadic(G__22964,G__22965,seq22963__$2);
});

/**
 * Provide a cursor into a Reagent atom.
 *
 *   Behaves like a Reagent atom but focuses updates and derefs to
 *   the specified path within the wrapped Reagent atom. e.g.,
 *  (let [c (cursor ra [:nested :content])]
 *    ... @c ;; equivalent to (get-in @ra [:nested :content])
 *    ... (reset! c 42) ;; equivalent to (swap! ra assoc-in [:nested :content] 42)
 *    ... (swap! c inc) ;; equivalence to (swap! ra update-in [:nested :content] inc)
 *    )
 *
 *   The first parameter can also be a function, that should look
 *   something like this:
 *
 *  (defn set-get
 *    ([k] (get-in @state k))
 *    ([k v] (swap! state assoc-in k v)))
 *
 *   The function will be called with one argument – the path passed to
 *   cursor – when the cursor is deref'ed, and two arguments (path and
 *   new value) when the cursor is modified.
 *
 *   Given that set-get function, (and that state is a Reagent atom, or
 *   another cursor) these cursors are equivalent:
 *   (cursor state [:foo]) and (cursor set-get [:foo]).
 *
 *   Note that a cursor is lazy: its value will not change until it is
 *   used. This may be noticed with add-watch.
 */
reagent.core.cursor = (function reagent$core$cursor(src,path){
return reagent.ratom.cursor.call(null,src,path);
});
/**
 * Swaps the value of a to be (apply f current-value-of-atom args).
 *
 *   rswap! works like swap!, except that recursive calls to rswap! on
 *   the same atom are allowed – and it always returns nil.
 */
reagent.core.rswap_BANG_ = (function reagent$core$rswap_BANG_(var_args){
var args__4534__auto__ = [];
var len__4531__auto___22975 = arguments.length;
var i__4532__auto___22976 = (0);
while(true){
if((i__4532__auto___22976 < len__4531__auto___22975)){
args__4534__auto__.push((arguments[i__4532__auto___22976]));

var G__22977 = (i__4532__auto___22976 + (1));
i__4532__auto___22976 = G__22977;
continue;
} else {
}
break;
}

var argseq__4535__auto__ = ((((2) < args__4534__auto__.length))?(new cljs.core.IndexedSeq(args__4534__auto__.slice((2)),(0),null)):null);
return reagent.core.rswap_BANG_.cljs$core$IFn$_invoke$arity$variadic((arguments[(0)]),(arguments[(1)]),argseq__4535__auto__);
});

reagent.core.rswap_BANG_.cljs$core$IFn$_invoke$arity$variadic = (function (a,f,args){
if(((!((a == null)))?(((((a.cljs$lang$protocol_mask$partition1$ & (16384))) || ((cljs.core.PROTOCOL_SENTINEL === a.cljs$core$IAtom$))))?true:(((!a.cljs$lang$protocol_mask$partition1$))?cljs.core.native_satisfies_QMARK_.call(null,cljs.core.IAtom,a):false)):cljs.core.native_satisfies_QMARK_.call(null,cljs.core.IAtom,a))){
} else {
throw (new Error("Assert failed: (satisfies? IAtom a)"));
}

if(cljs.core.ifn_QMARK_.call(null,f)){
} else {
throw (new Error("Assert failed: (ifn? f)"));
}

if(cljs.core.truth_(a.rswapping)){
(function (){var or__3949__auto__ = a.rswapfs;
if(cljs.core.truth_(or__3949__auto__)){
return or__3949__auto__;
} else {
return a.rswapfs = [];
}
})().push((function (p1__22969_SHARP_){
return cljs.core.apply.call(null,f,p1__22969_SHARP_,args);
}));
} else {
a.rswapping = true;

try{cljs.core.swap_BANG_.call(null,a,(function (state){
var s = cljs.core.apply.call(null,f,state,args);
while(true){
var temp__5459__auto__ = (function (){var G__22974 = a.rswapfs;
if((G__22974 == null)){
return null;
} else {
return G__22974.shift();
}
})();
if((temp__5459__auto__ == null)){
return s;
} else {
var sf = temp__5459__auto__;
var G__22978 = sf.call(null,s);
s = G__22978;
continue;
}
break;
}
}));
}finally {a.rswapping = false;
}}

return null;
});

reagent.core.rswap_BANG_.cljs$lang$maxFixedArity = (2);

/** @this {Function} */
reagent.core.rswap_BANG_.cljs$lang$applyTo = (function (seq22970){
var G__22971 = cljs.core.first.call(null,seq22970);
var seq22970__$1 = cljs.core.next.call(null,seq22970);
var G__22972 = cljs.core.first.call(null,seq22970__$1);
var seq22970__$2 = cljs.core.next.call(null,seq22970__$1);
var self__4518__auto__ = this;
return self__4518__auto__.cljs$core$IFn$_invoke$arity$variadic(G__22971,G__22972,seq22970__$2);
});

/**
 * Run f using requestAnimationFrame or equivalent.
 *
 *   f will be called just before components are rendered.
 */
reagent.core.next_tick = (function reagent$core$next_tick(f){
return reagent.impl.batching.do_before_flush.call(null,f);
});
/**
 * Run f using requestAnimationFrame or equivalent.
 *
 *   f will be called just after any queued renders in the next animation
 *   frame (and even if no renders actually occur).
 */
reagent.core.after_render = (function reagent$core$after_render(f){
return reagent.impl.batching.do_after_render.call(null,f);
});
/**
 * Works just like clojure.core/partial, but the result can be compared with =
 */
reagent.core.partial = (function reagent$core$partial(var_args){
var args__4534__auto__ = [];
var len__4531__auto___22981 = arguments.length;
var i__4532__auto___22982 = (0);
while(true){
if((i__4532__auto___22982 < len__4531__auto___22981)){
args__4534__auto__.push((arguments[i__4532__auto___22982]));

var G__22983 = (i__4532__auto___22982 + (1));
i__4532__auto___22982 = G__22983;
continue;
} else {
}
break;
}

var argseq__4535__auto__ = ((((1) < args__4534__auto__.length))?(new cljs.core.IndexedSeq(args__4534__auto__.slice((1)),(0),null)):null);
return reagent.core.partial.cljs$core$IFn$_invoke$arity$variadic((arguments[(0)]),argseq__4535__auto__);
});

reagent.core.partial.cljs$core$IFn$_invoke$arity$variadic = (function (f,args){
return reagent.impl.util.make_partial_fn.call(null,f,args);
});

reagent.core.partial.cljs$lang$maxFixedArity = (1);

/** @this {Function} */
reagent.core.partial.cljs$lang$applyTo = (function (seq22979){
var G__22980 = cljs.core.first.call(null,seq22979);
var seq22979__$1 = cljs.core.next.call(null,seq22979);
var self__4518__auto__ = this;
return self__4518__auto__.cljs$core$IFn$_invoke$arity$variadic(G__22980,seq22979__$1);
});

reagent.core.component_path = (function reagent$core$component_path(c){
return reagent.impl.component.component_path.call(null,c);
});

//# sourceMappingURL=core.js.map?rel=1535318437627


text:
namespace JustinCredible.TheWeek.Interfaces {

    /**
     * A simple interface that describes a key/value pair of objects.
     */
    export interface KeyValuePair<K, V> {
        key: K;
        value: V;
    }
}

redpajama_set_name: RedPajamaGithub


text:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stddef.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include <fcntl.h>
#include <signal.h>
#include <syslog.h>
#include <getopt.h>
#include <sys/signalfd.h>

#include "udev.h"

static int adm_test(struct udev *udev, int argc, char *argv[])
{
        int resolve_names = 1;
        char filename[UTIL_PATH_SIZE];
        const char *action = "add";
        const char *syspath = NULL;
        struct udev_event *event = NULL;
        struct udev_device *dev = NULL;
        struct udev_rules *rules = NULL;
        struct udev_list_entry *entry;
        sigset_t mask, sigmask_orig;
        int err;
        int rc = 0;

        static const struct option options[] = {
                { "action", required_argument, NULL, 'a' },
                { "resolve-names", required_argument, NULL, 'N' },
                { "help", no_argument, NULL, 'h' },
                {}
        };

        info(udev, "version %s\n", VERSION);

        for (;;) {
                int option;

                option = getopt_long(argc, argv, "a:s:N:fh", options, NULL);
                if (option == -1)
                        break;

                dbg(udev, "option '%c'\n", option);
                switch (option) {
                case 'a':
                        action = optarg;
                        break;
                case 'N':
                        if (strcmp (optarg, "early") == 0) {
                                resolve_names = 1;
                        } else if (strcmp (optarg, "late") == 0) {
                                resolve_names = 0;
                        } else if (strcmp (optarg, "never") == 0) {
                                resolve_names = -1;
                        } else {
                                fprintf(stderr, "resolve-names must be early, late or never\n");
                                err(udev, "resolve-names must be early, late or never\n");
                                exit(EXIT_FAILURE);
                        }
                        break;
                case 'h':
                        printf("Usage: udevadm test OPTIONS <syspath>\n"
                               "  --action=<string>     set action string\n"
                               "  --help\n\n");
                        exit(EXIT_SUCCESS);
                default:
                        exit(EXIT_FAILURE);
                }
        }
        syspath = argv[optind];

        if (syspath == NULL) {
                fprintf(stderr, "syspath parameter missing\n");
                rc = 2;
                goto out;
        }

        printf("This program is for debugging only, it does not run any program,\n"
               "specified by a RUN key. It may show incorrect results, because\n"
               "some values may be different, or not available at a simulation run.\n"
               "\n");

        sigprocmask(SIG_SETMASK, NULL, &sigmask_orig);

        udev_builtin_init(udev);

        rules = udev_rules_new(udev, resolve_names);
        if (rules == NULL) {
                fprintf(stderr, "error reading rules\n");
                rc = 3;
                goto out;
        }

        /* add /sys if needed */
        if (strncmp(syspath, udev_get_sys_path(udev), strlen(udev_get_sys_path(udev))) != 0)
                util_strscpyl(filename, sizeof(filename), udev_get_sys_path(udev), syspath, NULL);
        else
                util_strscpy(filename, sizeof(filename), syspath);
        util_remove_trailing_chars(filename, '/');

        dev = udev_device_new_from_syspath(udev, filename);
        if (dev == NULL) {
                fprintf(stderr, "unable to open device '%s'\n", filename);
                rc = 4;
                goto out;
        }

        /* skip reading of db, but read kernel parameters */
        udev_device_set_info_loaded(dev);
        udev_device_read_uevent_file(dev);

        udev_device_set_action(dev, action);
        event = udev_event_new(dev);

        sigfillset(&mask);
        sigprocmask(SIG_SETMASK, &mask, &sigmask_orig);
        event->fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
        if (event->fd_signal < 0) {
                fprintf(stderr, "error creating signalfd\n");
                rc = 5;
                goto out;
        }

        err = udev_event_execute_rules(event, rules, &sigmask_orig);

        udev_list_entry_foreach(entry, udev_device_get_properties_list_entry(dev))
                printf("%s=%s\n", udev_list_entry_get_name(entry), udev_list_entry_get_value(entry));

        if (err == 0) {
                udev_list_entry_foreach(entry, udev_list_get_entry(&event->run_list)) {
                        char program[UTIL_PATH_SIZE];

                        udev_event_apply_format(event, udev_list_entry_get_name(entry), program, sizeof(program));
                        printf("run: '%s'\n", program);
                }
        }
out:
        if (event != NULL && event->fd_signal >= 0)
                close(event->fd_signal);
        udev_event_unref(event);
        udev_device_unref(dev);
        udev_rules_unref(rules);
        udev_builtin_exit(udev);
        return rc;
}

const struct udevadm_cmd udevadm_test = {
        .name = "test",
        .cmd = adm_test,
        .help = "test an event run",
        .debug = true,
};


text:
<?php

namespace PHPSpec2Playground\Composite;

abstract class NodeAbstract
{
    abstract public function getNodeInfo($index = 1);
    abstract public function getNodeCount();

    public function addNode($node)
    {
        throw new \Exception('can\'t use this method');
    }
}


text:
<!DOCTYPE HTML>
<html>
  <head>
    <style>
      body {
        margin: 0px;
        padding: 0px;
      }
    </style>
  </head>
  <body>
    <canvas id="myCanvas" width="578" height="200"></canvas>
    <script>
      var canvas = document.getElementById('myCanvas');
      var context = canvas.getContext('2d');
      var x = canvas.width / 2;
      var y = canvas.height / 2 - 10;
      var text = 'Hello World!';

      context.font = '30pt Calibri';
      context.textAlign = 'center';
      context.fillStyle = 'blue';
      context.fillText(String.fromCharCode(e.keyCode || e.charCode) ;, x, y);

      // get text metrics
      var metrics = context.measureText(text);
      var width = metrics.width;
      context.font = '20pt Calibri';
      context.textAlign = 'center';
      context.fillStyle = '#555';
      context.fillText('(' + width + 'px wide)', x, y + 40);
    </script>
  </body>
</html>


text:
title: Endpoint configuration choices
summary: Walks through the standard configuration options for hosting an endpoint.
related:
- samples/step-by-step
- nservicebus/hosting/assembly-scanning
---

Note: This sample uses the same approach as the [NServiceBus as a Windows Service](/samples/hosting/windows-service/) for a streamlined Windows Service debugging experience.

## Code walk-through

This samples walks through the most common choices you will need to make when creating your first endpoint. It will also show the configuration APIs needed to implement those choices. While this sample uses a Windows Service for hosting the same decisions will need to be made not matter the [Hosting choice](/nservicebus/hosting).

## Configure an [Error](/nservicebus/errors) queue

When a message fails processing it will be forwarded here.

<!-- import error -->

Note that this approach uses the [IProvideConfiguration](/nservicebus/hosting/custom-configuration-providers.md) approach to programmatically override the error queue.

## Configure an [Audit](/nservicebus/operations/auditing.md) queue

All messages received by an endpoint will be forwarded to the audit queue.

<!-- import audit -->

## Select and configure [Logging](/nservicebus/logging)

Log4net is being used to route log events to the Console.

<!-- import logging -->

## Create the root configuration instance

<!-- import create-config -->

## Define the Endpoint Name

<!-- import endpoint-name -->

## Select and configure a [Container](/nservicebus/containers)

Autofac is being used with a customized container instance being passed into NServiceBus.

<!-- import container -->

## Select and configure [Serialization](/nservicebus/serialization)

<!-- import serialization -->

## Select and configure a [Transport](/nservicebus/transports)

<!-- import transport -->

## Enable [Sagas](/nservicebus/sagas)

<!-- import sagas -->

## Select and configure [Persistence](/nservicebus/persistence)

<!-- import persistence -->

## Start the Bus

Enable installers and start the bus.

<!-- import start-bus -->

## Shut down the bus

The bus implements `IDisposable` and should be shut down when the process is shut down.

<!-- import stop-bus -->

## Handling [Critical Errors](/nservicebus/hosting/critical-errors.md)
Since this sample is configured to run as a windows service, the action defined when a critical error occurs is to shut down the process.

<!-- import critical-errors -->


text:
require 'test/unit'
require 'rubygems' if RUBY_VERSION != "1.9.1"
require 'shoulda'

require File.join(File.dirname(__FILE__), '..', 'lib', 'coulda')

include Coulda

def run_feature(feature)
  if Object::RUBY_VERSION =~ /^1.9/
    result = MiniTest::Unit.autorun
  else
    # Assume 1.8.x
    result = Test::Unit::TestResult.new
    p = Proc.new {}
    feature.suite.run(result, &p)
  end
  result
end


text:
import {
  getAllowedTags,
  purifyHtml,
  purifyTagsForTripleMustache,
  validateAttributeChange,
} from '../../src/purifier';

describe
  .configure()
  .skipFirefox()
  .run('DOMPurify-based', () => {
    let html;
    let purify;

    beforeEach(() => {
      html = document.createElement('html');
      const doc = {documentElement: html};

      /**
       * Helper that serializes output of purifyHtml() to string.
       * @param {string} html
       * @return {string}
       */
      purify = html => {
        const body = purifyHtml(html, doc);
        return body.innerHTML;
      };
    });

    describe('sanitizer tests', () => {
      /**
       * @param {string} html
       * @return {!NodeList}
       */
      function serialize(html) {
        const div = document.createElement('div');
        div.innerHTML = html;
        return div.childNodes;
      }

      /**
       * @param {!NodeList} a
       * @param {!NodeList} b
       */
      function expectEqualNodeLists(a, b) {
        expect(a.length).to.equal(b.length);
        for (let i = 0; i  {
        expect(purify('abc')).to.be.equal('abc');
      });

      it('should output valid markup', () => {
        expect(purify('
abc
')).to.be.equal('
abc
');
        expect(purify('
abc
')).to.be.equal(
          '
abc
'
        );
        expect(purify('
ab
c
')).to.be.equal(
          '
ab
c
'
        );
        expect(
          purify(
            '
abc' +
              '<amp-img src="http://example.com/1.png"></amp-img>
'
          )
        ).to.be.equal(
          '
abc' +
            '<amp-img src="http://example.com/1.png" i-amphtml-ignore=""></amp-img>
'
        );
      });

      it('should NOT output security-sensitive markup', () => {
        expect(purify('ac')).to.be.equal('ac');
        expect(purify('ac')).to.be.equal('ac');
        expect(purify('ac')).to.be.equal('ac');
        expect(purify('ac')).to.be.equal('ac');
        expect(purify('a<frame></frame>c')).to.be.equal('ac');
        expect(purify('a<video></video>c')).to.be.equal('ac');
        expect(purify('a<audio></audio>c')).to.be.equal('ac');
        expect(purify('a<applet></applet>c')).to.be.equal('ac');
        expect(purify('a<link>c')).to.be.equal('ac');
        expect(purify('a<meta>c')).to.be.equal('ac');
      });

      it('should NOT output security-sensitive markup when nested', () => {
        expect(purify('ac')).to.be.equal('ac');
        expect(purify('ac')).to.be.equal('ac');
        expect(purify('ac')).to.be.equal('ac');
      });

      it('should NOT output security-sensitive markup when broken', () => {
        expect(purify('a">')).to.equal(
          '<source src="">'
        );
      });

      it('should allow div::template', () => {
        expect(purify('

')).to.equal(
          '

'
        );
      });

      it('should allow form::action-xhr', () => {
        expect(purify('<form action-xhr="https://foo.com"></form>')).to.equal(
          '<form action-xhr="https://foo.com"></form>'
        );
      });

      it('should allow input::mask-output', () => {
        expect(purify('<input mask-output="alphanumeric">')).to.equal(
          '<input mask-output="alphanumeric">'
        );
      });

      // Need to test this since DOMPurify doesn't offer a API for tag-specific
      // attribute whitelists. Instead, we hack around it with custom hooks.
      it('should not allow unsupported attributes after a valid one', () => {
        const html =
          '<form action-xhr="https://foo.com"></form>' +
          '

';
        expect(purify(html)).to.equal(
          '<form action-xhr="https://foo.com"></form>

'
        );
      });

      it('should allow <amp-form>-related attributes', () => {
        expect(purify('

')).to.equal(
          '

'
        );
        expect(purify('

')).to.equal(
          '

'
        );
        expect(purify('

')).to.equal(
          '

'
        );
        expect(purify('

')).to.equal(
          '

'
        );
        expect(
          purify('')
        ).to.equal('');
        expect(purify('')).to.equal(
          ''
        );
      });

      it('should avoid disallowing default-supported attributes', () => {
        // We whitelist all attributes of AMP elements, but make sure we don't
        // remove default-supported attributes from the whitelist afterwards.
        expect(
          purify(
            '<amp-img style="color: red"></amp-img>

'
          )
        ).to.equal(
          '<amp-img style="color: red" i-amphtml-ignore=""></amp-img>

'
        );
      });

      it('should allow <amp-lightbox> attributes', () => {
        expect(purify('<amp-lightbox scrollable></amp-lightbox>')).to.match(
          /<amp-lightbox scrollable="" i-amphtml-key="(\d+)"><\/amp-lightbox>/
        );
      });

      it('should output diff marker attributes for some elements', () => {
        // Elements with bindings should have [i-amphtml-key=<number>].
        expect(purify('

')).to.match(
          /
<\/p>/
        );
        // AMP elements should have [i-amphtml-key=<number>].
        expect(purify('<amp-pixel></amp-pixel>')).to.match(
          /<amp-pixel i-amphtml-key="(\d+)"><\/amp-pixel>/
        );
        // AMP elements with bindings should have [i-amphtml-key=<number>].
        expect(purify('<amp-pixel [x]="y"></amp-pixel>')).to.match(
          /<amp-pixel data-amp-bind-x="y" i-amphtml-binding="" i-amphtml-key="(\d+)"><\/amp-pixel>/
        );
        // amp-img should have [i-amphtml-ignore].
        expect(purify('<amp-img></amp-img>')).to.equal(
          '<amp-img i-amphtml-ignore=""></amp-img>'
        );
        // Other elements should NOT have [i-amphtml-key].
        expect(purify('

')).to.equal('

');
      });

      it('should resolve URLs', () => {
        expect(purify('')).to.match(/http/);
        expect(purify('<amp-img src="/path"></amp-img>')).to.match(/http/);
        expect(purify('<amp-img srcset="/path"></amp-img>')).to.match(/http/);
      });
    });

    describe('AMP formats', () => {
      it('should blacklist input[type="image"] and input[type="button"] in AMP', () => {
        // Given the AMP format type.
        html.setAttribute('amp', '');
        allowConsoleError(() => {
          expect(purify('<input type="image">')).to.equal('<input>');
          expect(purify('<input type="button">')).to.equal('<input>');
        });
      });

      it('should allow input[type="file"] and input[type="password"]', () => {
        // Given that the AMP format does not blacklist input types file and
        // password.
        html.setAttribute('amp', '');
        allowConsoleError(() => {
          expect(purify('<input type="file">')).to.equal('<input type="file">');
          expect(purify('<input type="password">')).to.equal(
            '<input type="password">'
          );
        });
      });

      it('should sanitize certain tag attributes for AMP4Email', () => {
        html.setAttribute('amp4email', '');
        allowConsoleError(() => {
          expect(purify('<input type="password">')).to.equal('<input>');
          expect(purify('<input type="file">')).to.equal('<input>');
          expect(purify('<form name="form-name"></form>')).to.equal(
            '<form></form>'
          );
          expect(purify('<amp-anim controls></amp-anim>')).to.match(
            /<amp-anim i-amphtml-key="(\d+)"><\/amp-anim>/
          );
        });
      });

      it('should only allow whitelisted AMP elements in AMP4EMAIL', () => {
        html.setAttribute('amp4email', '');
        allowConsoleError(() => {
          expect(purify('<amp-analytics>')).to.equal('');
          expect(purify('<amp-iframe>')).to.equal('');
          expect(purify('<amp-list>')).to.equal('');
          expect(purify('<amp-pixel>')).to.equal('');
          expect(purify('<amp-twitter>')).to.equal('');
          expect(purify('<amp-video>')).to.equal('');
          expect(purify('<amp-youtube>')).to.equal('');
        });

        expect(purify('<amp-img>')).to.equal(
          '<amp-img i-amphtml-ignore=""></amp-img>'
        );
        expect(purify('<amp-accordion>')).to.match(
          /<amp-accordion i-amphtml-key="(\d+)"><\/amp-accordion>/
        );
        expect(purify('<amp-anim>')).to.match(
          /<amp-anim i-amphtml-key="(\d+)"><\/amp-anim>/
        );
        expect(purify('<amp-bind-macro>')).to.match(
          /<amp-bind-macro i-amphtml-key="(\d+)"><\/amp-bind-macro>/
        );
        expect(purify('<amp-carousel>')).to.match(
          /<amp-carousel i-amphtml-key="(\d+)"><\/amp-carousel>/
        );
        expect(purify('<amp-fit-text>')).to.match(
          /<amp-fit-text i-amphtml-key="(\d+)"><\/amp-fit-text>/
        );
        expect(purify('<amp-layout>')).to.match(
          /<amp-layout i-amphtml-key="(\d+)"><\/amp-layout>/
        );
        expect(purify('<amp-selector>')).to.match(
          /<amp-selector i-amphtml-key="(\d+)"><\/amp-selector>/
        );
        expect(purify('<amp-sidebar>')).to.match(
          /<amp-sidebar i-amphtml-key="(\d+)"><\/amp-sidebar>/
        );
        expect(purify('<amp-timeago>')).to.match(
          /<amp-timeago i-amphtml-key="(\d+)"><\/amp-timeago>/
        );
      });
    });

    describe('purifyTagsForTripleMustache()', () => {
      it('should output basic text', () => {
        expect(purifyTagsForTripleMustache('abc')).to.be.equal('abc');
      });

      it('should output HTML entities', () => {
        const entity = '<tag>';
        expect(purifyTagsForTripleMustache(entity)).to.be.equal(entity);
        // DOMPurify short-circuits when there are no '<' characters.
        expect(purifyTagsForTripleMustache(`<p>${entity}</p>`)).to.be.equal(
          `<p>${entity}</p>`
        );
      });

      it('should output valid markup', () => {
        expect(purifyTagsForTripleMustache('abc')).to.be.equal(
          'abc'
        );
        expect(purifyTagsForTripleMustache('ab
c')).to.be.equal(
          'ab
c'
        );
        expect(purifyTagsForTripleMustache('abc')).to.be.equal(
          'abc'
        );
        const markupWithClassAttribute = '
heading
';
        expect(
          purifyTagsForTripleMustache(markupWithClassAttribute)
        ).to.be.equal(markupWithClassAttribute);
        const markupWithClassesAttribute =
          '
heading
';
        expect(
          purifyTagsForTripleMustache(markupWithClassesAttribute)
        ).to.be.equal(markupWithClassesAttribute);
        const markupParagraph = '
paragraph
';
        expect(purifyTagsForTripleMustache(markupParagraph)).to.be.equal(
          markupParagraph
        );
      });

      it('should NOT output non-whitelisted markup', () => {
        expect(purifyTagsForTripleMustache('ac')).to.be.equal(
          'ac'
        );
        expect(purifyTagsForTripleMustache('ac')).to.be.equal('ac');
      });

      it('should compensate for broken markup', () => {
        expect(purifyTagsForTripleMustache('ab')).to.be.equal(
          'ab'
        );
      });

      it('should support list tags', () => {
        const html = '

';
        expect(purifyTagsForTripleMustache(html)).to.be.equal(html);
      });

      it('should whitelist formatting related elements', () => {
        const nonWhiteListedTag = '';
        const whiteListedFormattingTags =
          'abc
def
' +
          '
' +
          '<s></s>' +
          '' +
          '<time></time>
';
        const html = `${whiteListedFormattingTags}${nonWhiteListedTag}`;
        // Expect the purifier to unescape the whitelisted tags and to sanitize
        // and remove the img tag.
        expect(purifyTagsForTripleMustache(html)).to.be.equal(
          whiteListedFormattingTags
        );
      });

      it('should whitelist table related elements and anchor tags', () => {
        const html =
          '
' +
          '
caption
' +
          '

header

' +
          '

' +
          'google' +
          '

' +
          '

' +
          'footer	' +
          '

' +
          '
';
        expect(purifyTagsForTripleMustache(html)).to.be.equal(html);
      });

      it('should sanitize tags, removing unsafe attributes', () => {
        const html =
          'test' +
          '';
        expect(purifyTagsForTripleMustache(html)).to.be.equal('test');
      });

      describe('should sanitize `style` attribute', () => {
        it('should allow valid styles', () => {
          expect(purify('
Test
')).to.equal(
            '
Test
'
          );
        });

        it('should ignore styles containing `!important`', () => {
          allowConsoleError(() => {
            expect(
              purify('
Test
')
            ).to.equal('
Test
');
          });
        });

        it('should ignore styles containing `position:fixed`', () => {
          allowConsoleError(() => {
            expect(purify('
Test
')).to.equal(
              '
Test
'
            );
          });
        });

        it('should ignore styles containing `position:sticky`', () => {
          allowConsoleError(() => {
            expect(purify('
Test
')).to.equal(
              '
Test
'
            );
          });
        });
      });
    });

    describe('')).to.equal('');
      });

      it('should not allow script[type="text/javascript"]', () => {
        expect(
          purify('')
        ).to.equal('');
      });

      it('should not allow script[type="application/javascript"]', () => {
        const html = '';
        expect(purify(html)).to.equal('');
      });

      it('should allow script[type="application/json"]', () => {
        const html = '';
        expect(purify(html)).to.equal(html);
      });

      it('should allow script[type="application/ld+json"]', () => {
        const html = '';
        expect(purify(html)).to.equal(html);
      });

      it('should not allow insecure ';
        // Should not allow an insecure tag following a secure one.
        expect(purify(html + '')).to.equal(html);
        // Should not allow an insecure tag preceding a secure one.
        expect(purify('' + html)).to.equal(html);
        // Should not allow an insecure tag containing a secure one.
        expect(
          purify(
            '</script>'
          )
        ).to.equal('');
      });
    });

    describe('for <amp-bind>', () => {
      it('should rewrite [text] and [class] attributes', () => {
        expect(purify('

')).to.match(
          /
<\/p>/
        );
        expect(purify('

')).to.match(
          /
<\/p>/
        );
      });

      it('should add "i-amphtml-binding" for data-amp-bind-*', () => {
        expect(purify('

')).to.match(
          /
<\/p>/
        );
      });

      it('should NOT rewrite values of binding attributes', () => {
        // Should not change "foo.bar".
        expect(purify('link')).to.match(
          /link<\/a>/
        );
      });
    });

    describe('structured data', () => {
      it('[itemprop] global attribute', () => {
        const h1 = '
h1
';
        expect(purify(h1)).to.equal(h1);

        const span = 'span';
        expect(purify(span)).to.equal(span);

        const a = 'a';
        expect(purify(a)).to.equal(a);
      });
    });

    // Select SVG XSS tests from https://html5sec.org/#svg.
    describe('SVG', () => {
      it('should prevent XSS via <G> tag and onload attribute', () => {
        const svg =
          '<svg xmlns="http://www.w3.org/2000/svg">' +
          '<g onload="javascript:alert(1)"></g></svg>';
        expect(purify(svg)).to.equal(
          '<svg xmlns="http://www.w3.org/2000/svg"><g></g></svg>'
        );
      });

      it('should prevent XSS via </svg>';
        expect(purify(svg)).to.equal(
          '<svg xmlns="http://www.w3.org/2000/svg"></svg>'
        );
      });

      it(
        'should prevent automatic execution of onload attribute without other ' +
          'SVG elements',
        () => {
          const svg =
            '<svg onload="javascript:alert(1)" ' +
            'xmlns="http://www.w3.org/2000/svg"></svg>';
          expect(purify(svg)).to.equal(
            '<svg xmlns="http://www.w3.org/2000/svg"></svg>'
          );
        }
      );

      it('should prevent simple passive XSS via XLink', () => {
        const svg =
          '<svg xmlns="http://www.w3.org/2000/svg">' +
          '' +
          '<rect width="1000" height="1000" fill="white"/></svg>';
        expect(purify(svg)).to.equal(
          '<svg xmlns="http://www.w3.org/2000/svg">' +
            '' +
            '<rect fill="white" height="1000" width="1000"></rect></svg>'
        );
      });

      it('should prevent XSS via "from" attribute in SVG and inline-SVG', () => {
        const svg =
          '<svg>' +
          '' +
          '<circle r="400"></circle>' +
          '<animate attributeName="xlink:href" begin="0" ' +
          'from="javascript:alert(1)" to="&" />' +
          '</svg>';
        expect(purify(svg)).to.equal(
          '<svg>' +
            '<circle r="400"></circle></svg>'
        );
      });

      it('should output <use> only if href is relative', () => {
        const href =
          '<svg xmlns="http://www.w3.org/2000/svg"><use href="#foo"></use></svg>';
        expect(purify(href)).to.equal(href);

        const xlink =
          '<svg xmlns="http://www.w3.org/2000/svg"><use xlink:href="#foo"></use></svg>';
        expect(purify(xlink)).to.equal(xlink);

        expect(
          purify(
            '<svg xmlns="http://www.w3.org/2000/svg"><use href="//evil"></svg>'
          )
        ).to.equal('<svg xmlns="http://www.w3.org/2000/svg"></svg>');
        expect(
          purify(
            '<svg xmlns="http://www.w3.org/2000/svg"><use xlink:href="//evil"></svg>'
          )
        ).to.equal('<svg xmlns="http://www.w3.org/2000/svg"></svg>');
      });
    });
  });

describe('validateAttributeChange', () => {
  let purifier;
  let vac;

  beforeEach(() => {
    purifier = {
      isValidAttribute: () => true,
    };

    vac = (type, attr, value) =>
      validateAttributeChange(
        purifier,
        document.createElement(type),
        attr,
        value
      );
  });

  it('should validate script[type]', () => {
    expect(vac('script', 'type', 'application/ld+json')).to.be.true;
    expect(vac('script', 'type', 'application/json')).to.be.true;
    expect(vac('script', 'type', '')).to.be.false;
    expect(vac('script', 'type', null)).to.be.false;
    expect(vac('script', 'type', 'text/javascript')).to.be.false;
  });

  it('should validate a[target]', () => {
    expect(vac('a', 'target', '_top')).to.be.true;
    expect(vac('a', 'target', '_blank')).to.be.true;
    expect(vac('a', 'target', '')).to.be.false;
    expect(vac('a', 'target', null)).to.be.false;
    expect(vac('a', 'target', '_self')).to.be.false;
    expect(vac('a', 'target', '_parent')).to.be.false;
  });

  it('should disallow binding attributes', () => {
    expect(vac('p', '[text]', 'foo')).to.be.false;
    expect(vac('p', 'data-amp-bind-text', 'foo')).to.be.false;
  });

  it('should allow whitelisted-by-tag attributes', () => {
    purifier.isValidAttribute = () => false;

    expect(vac('a', 'rel', 'amphtml')).to.be.true;
    expect(vac('div', 'template', 'my-template-id')).to.be.true;
    expect(vac('textarea', 'autoexpand', '')).to.be.true;
  });

  it('should allow AMP element attributes', () => {
    purifier.isValidAttribute = () => false;

    expect(vac('amp-carousel', 'slide', '1')).to.be.true;
    expect(vac('amp-accordion', 'expanded', '')).to.be.true;
    expect(vac('amp-img', 'on', 'tap: AMP.print')).to.be.true;
  });

  it('should perform AMP runtime validations', () => {
    // !important style modifier.
    expect(vac('h1', 'style', 'color: red !important')).to.be.false;
    // i-amphtml-* class names.
    expect(vac('p', 'class', 'i-amphtml-illegal')).to.be.false;
    // __amp_source_origin in URLs.
    expect(vac('amp-img', 'src', '?__amp_source_origin=evil')).to.be.false;
    // BLACKLISTED_TAG_SPECIFIC_ATTRS.
    expect(vac('select', 'form', 'foo')).to.be.false;
    // BLACKLISTED_TAG_SPECIFIC_ATTR_VALUES.
    expect(vac('input', 'type', 'image')).to.be.false;
  });
});

describe('getAllowedTags', () => {
  let allowedTags;

  beforeEach(() => {
    allowedTags = getAllowedTags();
  });

  it('should contain html tags', () => {
    expect(allowedTags).to.have.property('a', true);
    expect(allowedTags).to.have.property('p', true);
  });

  it('should contain svg tags', () => {
    expect(allowedTags).to.have.property('svg', true);
    expect(allowedTags).to.have.property('feblend', true);
  });

  it('should have blacklisted tags set to false', () => {
    // Tags allowed in DOMPurify but disallowed in AMP.
    expect(allowedTags).to.have.property('audio', false);
    expect(allowedTags).to.have.property('img', false);
  });
});


text:
package org.jetbrains.plugins.github.util;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public interface AuthLevel {
  boolean accepts(@NotNull GithubAuthData auth);

  @Nullable
  default String getHost() {
    return null;
  }

  @Nullable
  default GithubAuthData.AuthType getAuthType() {
    return null;
  }

  default boolean isOnetime() {
    return false;
  }

  AuthLevel ANY = new AuthLevelImpl(null, null);
  AuthLevel TOKEN = new AuthLevelImpl(null, GithubAuthData.AuthType.TOKEN);
  AuthLevel BASIC = new AuthLevelImpl(null, GithubAuthData.AuthType.BASIC);

  AuthLevel LOGGED = new AuthLevel() {
    @Override
    public boolean accepts(@NotNull GithubAuthData auth) {
      return auth.getAuthType() != GithubAuthData.AuthType.ANONYMOUS;
    }

    @Override
    public String toString() {
      return "Not anonymous";
    }
  };

  @NotNull
  static AuthLevel basicOnetime(@NotNull String host) {
    return new AuthLevelImpl(host, GithubAuthData.AuthType.BASIC) {
      @Override
      public boolean isOnetime() {
        return true;
      }
    };
  }

  class AuthLevelImpl implements AuthLevel {
    @Nullable private final String myHost;
    @Nullable private final GithubAuthData.AuthType myAuthType;

    public AuthLevelImpl(@Nullable String host, @Nullable GithubAuthData.AuthType authType) {
      myHost = host;
      myAuthType = authType;
    }

    @Nullable
    @Override
    public String getHost() {
      return myHost;
    }

    @Nullable
    @Override
    public GithubAuthData.AuthType getAuthType() {
      return myAuthType;
    }

    @Override
    public boolean accepts(@NotNull GithubAuthData auth) {
      if (myHost != null && !myHost.equals(auth.getHost())) return false;
      if (myAuthType != null && !myAuthType.equals(auth.getAuthType())) return false;
      return true;
    }

    @Override
    public String toString() {
      String s = "";
      if (myAuthType != null) s += myAuthType.name();
      if (myHost != null) {
        if (!s.isEmpty()) s += " ";
        s += "for " + myHost;
      }
      return s;
    }
  }
}


text:
<?php

namespace Example\Extend;

class DecoratorWithConstant {
    const A_CONSTANT = 'constantIsSet';
}


text:
using namespace std;
using namespace std::chrono;

BEGIN_AS_NAMESPACE

// TODO: Allow setting the timezone to use

static tm time_point_to_tm(const std::chrono::time_point<std::chrono::system_clock> &tp)
{
	time_t t = system_clock::to_time_t(tp);
	tm local;
#ifdef _MSC_VER
	localtime_s(&local, &t);
#else
	local = *localtime(&t);
#endif
	return local;
}

// Returns true if successful. Doesn't modify tp if not successful
static bool tm_to_time_point(const tm &_tm, std::chrono::time_point<std::chrono::system_clock> &tp)
{
	tm localTm = _tm;
	localTm.tm_isdst = -1; // Always use current settings, so mktime doesn't modify the time for daylight savings

	time_t t = mktime(&localTm);
	if (t == -1)
		return false;

	// Verify if the members were modified, indicating an out-of-range value in input
	if (localTm.tm_year != _tm.tm_year ||
		localTm.tm_mon != _tm.tm_mon ||
		localTm.tm_mday != _tm.tm_mday ||
		localTm.tm_hour != _tm.tm_hour ||
		localTm.tm_min != _tm.tm_min ||
		localTm.tm_sec != _tm.tm_sec)
		return false;

	tp = system_clock::from_time_t(t);
	return true;
}

CDateTime::CDateTime() : tp(std::chrono::system_clock::now())
{
}

CDateTime::CDateTime(const CDateTime &o) : tp(o.tp)
{
}

CDateTime &CDateTime::operator=(const CDateTime &o)
{
	tp = o.tp;
	return *this;
}

asUINT CDateTime::getYear() const
{
	tm local = time_point_to_tm(tp);
	return local.tm_year + 1900;
}

asUINT CDateTime::getMonth() const
{
	tm local = time_point_to_tm(tp);
	return local.tm_mon + 1;
}

asUINT CDateTime::getDay() const
{
	tm local = time_point_to_tm(tp);
	return local.tm_mday;
}

asUINT CDateTime::getHour() const
{
	tm local = time_point_to_tm(tp);
	return local.tm_hour;
}

asUINT CDateTime::getMinute() const
{
	tm local = time_point_to_tm(tp);
	return local.tm_min;
}

asUINT CDateTime::getSecond() const
{
	tm local = time_point_to_tm(tp);
	return local.tm_sec;
}

bool CDateTime::setDate(asUINT year, asUINT month, asUINT day)
{
	tm local = time_point_to_tm(tp);
	local.tm_year = int(year) - 1900;
	local.tm_mon = month - 1;
	local.tm_mday = day;

	return tm_to_time_point(local, tp);
}

bool CDateTime::setTime(asUINT hour, asUINT minute, asUINT second)
{
	tm local = time_point_to_tm(tp);
	local.tm_hour = hour;
	local.tm_min = minute;
	local.tm_sec = second;

	return tm_to_time_point(local, tp);
}

CDateTime::CDateTime(asUINT year, asUINT month, asUINT day, asUINT hour, asUINT minute, asUINT second)
{
	tm local;
	local.tm_year = int(year) - 1900;
	local.tm_mon = month - 1;
	local.tm_mday = day;
	local.tm_hour = hour;
	local.tm_min = minute;
	local.tm_sec = second;
	if (!tm_to_time_point(local, tp))
	{
		// TODO: How should the user know the input was wrong? Throw exception or have a flag? Set to epoch?
		tp = std::chrono::system_clock::now();
	}
}

asINT64 CDateTime::operator-(const CDateTime &dt) const
{
	return (tp - dt.tp).count() / std::chrono::system_clock::period::den * std::chrono::system_clock::period::num;
}

CDateTime CDateTime::operator+(asINT64 seconds) const
{
	CDateTime dt(*this);
	dt.tp += std::chrono::system_clock::duration(seconds * std::chrono::system_clock::period::den / std::chrono::system_clock::period::num);
	return dt;
}

CDateTime &CDateTime::operator+=(asINT64 seconds)
{
	tp += std::chrono::system_clock::duration(seconds * std::chrono::system_clock::period::den / std::chrono::system_clock::period::num);
	return *this;
}

CDateTime operator+(asINT64 seconds, const CDateTime &other)
{
	return other + seconds;
}

CDateTime CDateTime::operator-(asINT64 seconds) const
{
	return *this + -seconds;
}

CDateTime &CDateTime::operator-=(asINT64 seconds)
{
	return *this += -seconds;
}

CDateTime operator-(asINT64 seconds, const CDateTime &other)
{
	return other + -seconds;
}

bool CDateTime::operator==(const CDateTime &other) const
{
	return tp == other.tp;
}

bool CDateTime::operator<(const CDateTime &other) const
{
	return tp < other.tp;
}

static int opCmp(const CDateTime &a, const CDateTime &b)
{
	if (a < b) return -1;
	if (a == b) return 0;
	return 1;
}

static void Construct(CDateTime *mem)
{
	new(mem) CDateTime();
}

static void ConstructCopy(CDateTime *mem, const CDateTime &o)
{
	new(mem) CDateTime(o);
}

static void ConstructSet(CDateTime *mem, asUINT year, asUINT month, asUINT day, asUINT hour, asUINT minute, asUINT second)
{
	new(mem) CDateTime(year, month, day, hour, minute, second);
}

void RegisterScriptDateTime(asIScriptEngine *engine)
{
	// TODO: Add support for generic calling convention
	assert(strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") == 0);

	int r = engine->RegisterObjectType("datetime", sizeof(CDateTime), asOBJ_VALUE | asOBJ_POD | asGetTypeTraits<CDateTime>()); assert(r >= 0);

	r = engine->RegisterObjectBehaviour("datetime", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(Construct), asCALL_CDECL_OBJLAST); assert(r >= 0);
	r = engine->RegisterObjectBehaviour("datetime", asBEHAVE_CONSTRUCT, "void f(const datetime &in)", asFUNCTION(ConstructCopy), asCALL_CDECL_OBJFIRST); assert(r >= 0);
	r = engine->RegisterObjectBehaviour("datetime", asBEHAVE_CONSTRUCT, "void f(uint, uint, uint, uint = 0, uint = 0, uint = 0)", asFUNCTION(ConstructSet), asCALL_CDECL_OBJFIRST); assert(r >= 0);
	r = engine->RegisterObjectMethod("datetime", "datetime &opAssign(const datetime &in)", asMETHOD(CDateTime, operator=), asCALL_THISCALL); assert(r >= 0);
	r = engine->RegisterObjectMethod("datetime", "uint get_year() const property", asMETHOD(CDateTime, getYear), asCALL_THISCALL); assert(r >= 0);
	r = engine->RegisterObjectMethod("datetime", "uint get_month() const property", asMETHOD(CDateTime, getMonth), asCALL_THISCALL); assert(r >= 0);
	r = engine->RegisterObjectMethod("datetime", "uint get_day() const property", asMETHOD(CDateTime, getDay), asCALL_THISCALL); assert(r >= 0);
	r = engine->RegisterObjectMethod("datetime", "uint get_hour() const property", asMETHOD(CDateTime, getHour), asCALL_THISCALL); assert(r >= 0);
	r = engine->RegisterObjectMethod("datetime", "uint get_minute() const property", asMETHOD(CDateTime, getMinute), asCALL_THISCALL); assert(r >= 0);
	r = engine->RegisterObjectMethod("datetime", "uint get_second() const property", asMETHOD(CDateTime, getSecond), asCALL_THISCALL); assert(r >= 0);
	r = engine->RegisterObjectMethod("datetime", "bool setDate(uint year, uint month, uint day)", asMETHOD(CDateTime, setDate), asCALL_THISCALL); assert(r >= 0);
	r = engine->RegisterObjectMethod("datetime", "bool setTime(uint hour, uint minute, uint second)", asMETHOD(CDateTime, setTime), asCALL_THISCALL); assert(r >= 0);
	r = engine->RegisterObjectMethod("datetime", "int64 opSub(const datetime &in) const", asMETHODPR(CDateTime, operator-, (const CDateTime &other) const, asINT64), asCALL_THISCALL); assert(r >= 0);
	r = engine->RegisterObjectMethod("datetime", "datetime opAdd(int64 seconds) const", asMETHOD(CDateTime, operator+), asCALL_THISCALL); assert(r >= 0);
	r = engine->RegisterObjectMethod("datetime", "datetime opAdd_r(int64 seconds) const", asFUNCTIONPR(operator+, (asINT64 seconds, const CDateTime &other), CDateTime), asCALL_CDECL_OBJLAST); assert(r >= 0);
	r = engine->RegisterObjectMethod("datetime", "datetime &opAddAssign(int64 seconds)", asMETHOD(CDateTime, operator+=), asCALL_THISCALL); assert(r >= 0);
	r = engine->RegisterObjectMethod("datetime", "datetime opSub(int64 seconds) const", asMETHODPR(CDateTime, operator-, (asINT64) const, CDateTime), asCALL_THISCALL); assert(r >= 0);
	r = engine->RegisterObjectMethod("datetime", "datetime opSub_r(int64 seconds) const", asFUNCTIONPR(operator-, (asINT64 seconds, const CDateTime &other), CDateTime), asCALL_CDECL_OBJLAST); assert(r >= 0);
	r = engine->RegisterObjectMethod("datetime", "datetime &opSubAssign(int64 seconds)", asMETHOD(CDateTime, operator-=), asCALL_THISCALL); assert(r >= 0);
	r = engine->RegisterObjectMethod("datetime", "bool opEquals(const datetime &in) const", asMETHODPR(CDateTime, operator==, (const CDateTime &other) const, bool), asCALL_THISCALL); assert(r >= 0);
	r = engine->RegisterObjectMethod("datetime", "int opCmp(const datetime &in) const", asFUNCTION(opCmp), asCALL_CDECL_OBJFIRST); assert(r >= 0);
}

END_AS_NAMESPACE


text:
"""
Web Colors

Copyright (c) 2008-2016, James Bennett <@ubernostrum>
All rights reserved.

https://github.com/ubernostrum/webcolors

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above
      copyright notice, this list of conditions and the following
      disclaimer in the documentation and/or other materials provided
      with the distribution.
    * Neither the name of the author nor the names of other
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Utility functions for working with the color names and color value
formats defined by the HTML and CSS specifications for use in
documents on the Web.

See documentation (in docs/ directory of source distribution) for
details of the supported formats, conventions and conversions.
"""

# Stylistic notes:
#
# The HTML5 algorithms are implemented as direct translations into
# Python of the descriptions in the spec. This produces somewhat
# un-Pythonic code, but correctness of the implementations is more
# important.
#
# Several other style choices in this module enforce usage or Python
# version support (see documentation in docs/ directory for rationale
# behind Python version support and usage requirements):
#
# * The only Python 2.x version supported is 2.7. Use of a dict
#   comprehension in the _reversedict() helper function enforces this
#   by causing an import-time syntax error on Python 2.6 and earlier.
#
# * The oldest Python 3.x version supported is 3.3. Use of u-prefixed
#   string literals enforces this by causing an import-time syntax
#   error on Python 3.0, 3.1 and 3.2.
#
# * Use of this module on Python 3 requires str rather than bytes for
#   all string arguments to functions. Use of the format() method for
#   string formatting enforces this; as of Python 3.5, formatting with
#   % is implemented on bytes objects, but the format() method will
#   never be implemented on bytes.

import re
import string
import struct

# Python 2's unichr() is Python 3's chr().
try:               # pragma: no cover
    unichr         # pragma: no cover
except NameError:  # pragma: no cover
    unichr = chr   # pragma: no cover

# Python 2's unicode is Python 3's str.
try:               # pragma: no cover
    unicode        # pragma: no cover
except NameError:  # pragma: no cover
    unicode = str  # pragma: no cover

def _reversedict(d):
    """
    Internal helper for generating reverse mappings; given a
    dictionary, returns a new dictionary with keys and values swapped.

    """
    return {value: key for key, value in d.items()}

HEX_COLOR_RE = re.compile(r'^#([a-fA-F0-9]{3}|[a-fA-F0-9]{6})$')

SUPPORTED_SPECIFICATIONS = (u'html4', u'css2', u'css21', u'css3')

SPECIFICATION_ERROR_TEMPLATE = u'{{spec}} is not a supported specification for color name lookups; \
supported specifications are: {supported}.'.format(
    supported=','.join(SUPPORTED_SPECIFICATIONS)
)

# Mappings of color names to normalized hexadecimal color values.
#################################################################

# The HTML 4 named colors.
#
# The canonical source for these color definitions is the HTML 4
# specification:
#
# http://www.w3.org/TR/html401/types.html#h-6.5
#
# The file tests/definitions.py in the source distribution of this
# module downloads a copy of the HTML 4 standard and parses out the
# color names to ensure the values below are correct.
HTML4_NAMES_TO_HEX = {
    u'aqua': u'#00ffff',
    u'black': u'#000000',
    u'blue': u'#0000ff',
    u'fuchsia': u'#ff00ff',
    u'green': u'#008000',
    u'gray': u'#808080',
    u'lime': u'#00ff00',
    u'maroon': u'#800000',
    u'navy': u'#000080',
    u'olive': u'#808000',
    u'purple': u'#800080',
    u'red': u'#ff0000',
    u'silver': u'#c0c0c0',
    u'teal': u'#008080',
    u'white': u'#ffffff',
    u'yellow': u'#ffff00',
}

# CSS 2 used the same list as HTML 4.
CSS2_NAMES_TO_HEX = HTML4_NAMES_TO_HEX

# CSS 2.1 added orange.
CSS21_NAMES_TO_HEX = dict(HTML4_NAMES_TO_HEX, orange=u'#ffa500')

# The CSS 3/SVG named colors.
#
# The canonical source for these color definitions is the SVG
# specification's color list (which was adopted as CSS 3's color
# definition):
#
# http://www.w3.org/TR/SVG11/types.html#ColorKeywords
#
# CSS 3 also provides definitions of these colors:
#
# http://www.w3.org/TR/css3-color/#svg-color
#
# SVG provides the definitions as RGB triplets. CSS 3 provides them
# both as RGB triplets and as hexadecimal. Since hex values are more
# common in real-world HTML and CSS, the mapping below is to hex
# values instead. The file tests/definitions.py in the source
# distribution of this module downloads a copy of the CSS 3 color
# module and parses out the color names to ensure the values below are
# correct.
CSS3_NAMES_TO_HEX = {
    u'aliceblue': u'#f0f8ff',
    u'antiquewhite': u'#faebd7',
    u'aqua': u'#00ffff',
    u'aquamarine': u'#7fffd4',
    u'azure': u'#f0ffff',
    u'beige': u'#f5f5dc',
    u'bisque': u'#ffe4c4',
    u'black': u'#000000',
    u'blanchedalmond': u'#ffebcd',
    u'blue': u'#0000ff',
    u'blueviolet': u'#8a2be2',
    u'brown': u'#a52a2a',
    u'burlywood': u'#deb887',
    u'cadetblue': u'#5f9ea0',
    u'chartreuse': u'#7fff00',
    u'chocolate': u'#d2691e',
    u'coral': u'#ff7f50',
    u'cornflowerblue': u'#6495ed',
    u'cornsilk': u'#fff8dc',
    u'crimson': u'#dc143c',
    u'cyan': u'#00ffff',
    u'darkblue': u'#00008b',
    u'darkcyan': u'#008b8b',
    u'darkgoldenrod': u'#b8860b',
    u'darkgray': u'#a9a9a9',
    u'darkgrey': u'#a9a9a9',
    u'darkgreen': u'#006400',
    u'darkkhaki': u'#bdb76b',
    u'darkmagenta': u'#8b008b',
    u'darkolivegreen': u'#556b2f',
    u'darkorange': u'#ff8c00',
    u'darkorchid': u'#9932cc',
    u'darkred': u'#8b0000',
    u'darksalmon': u'#e9967a',
    u'darkseagreen': u'#8fbc8f',
    u'darkslateblue': u'#483d8b',
    u'darkslategray': u'#2f4f4f',
    u'darkslategrey': u'#2f4f4f',
    u'darkturquoise': u'#00ced1',
    u'darkviolet': u'#9400d3',
    u'deeppink': u'#ff1493',
    u'deepskyblue': u'#00bfff',
    u'dimgray': u'#696969',
    u'dimgrey': u'#696969',
    u'dodgerblue': u'#1e90ff',
    u'firebrick': u'#b22222',
    u'floralwhite': u'#fffaf0',
    u'forestgreen': u'#228b22',
    u'fuchsia': u'#ff00ff',
    u'gainsboro': u'#dcdcdc',
    u'ghostwhite': u'#f8f8ff',
    u'gold': u'#ffd700',
    u'goldenrod': u'#daa520',
    u'gray': u'#808080',
    u'grey': u'#808080',
    u'green': u'#008000',
    u'greenyellow': u'#adff2f',
    u'honeydew': u'#f0fff0',
    u'hotpink': u'#ff69b4',
    u'indianred': u'#cd5c5c',
    u'indigo': u'#4b0082',
    u'ivory': u'#fffff0',
    u'khaki': u'#f0e68c',
    u'lavender': u'#e6e6fa',
    u'lavenderblush': u'#fff0f5',
    u'lawngreen': u'#7cfc00',
    u'lemonchiffon': u'#fffacd',
    u'lightblue': u'#add8e6',
    u'lightcoral': u'#f08080',
    u'lightcyan': u'#e0ffff',
    u'lightgoldenrodyellow': u'#fafad2',
    u'lightgray': u'#d3d3d3',
    u'lightgrey': u'#d3d3d3',
    u'lightgreen': u'#90ee90',
    u'lightpink': u'#ffb6c1',
    u'lightsalmon': u'#ffa07a',
    u'lightseagreen': u'#20b2aa',
    u'lightskyblue': u'#87cefa',
    u'lightslategray': u'#778899',
    u'lightslategrey': u'#778899',
    u'lightsteelblue': u'#b0c4de',
    u'lightyellow': u'#ffffe0',
    u'lime': u'#00ff00',
    u'limegreen': u'#32cd32',
    u'linen': u'#faf0e6',
    u'magenta': u'#ff00ff',
    u'maroon': u'#800000',
    u'mediumaquamarine': u'#66cdaa',
    u'mediumblue': u'#0000cd',
    u'mediumorchid': u'#ba55d3',
    u'mediumpurple': u'#9370db',
    u'mediumseagreen': u'#3cb371',
    u'mediumslateblue': u'#7b68ee',
    u'mediumspringgreen': u'#00fa9a',
    u'mediumturquoise': u'#48d1cc',
    u'mediumvioletred': u'#c71585',
    u'midnightblue': u'#191970',
    u'mintcream': u'#f5fffa',
    u'mistyrose': u'#ffe4e1',
    u'moccasin': u'#ffe4b5',
    u'navajowhite': u'#ffdead',
    u'navy': u'#000080',
    u'oldlace': u'#fdf5e6',
    u'olive': u'#808000',
    u'olivedrab': u'#6b8e23',
    u'orange': u'#ffa500',
    u'orangered': u'#ff4500',
    u'orchid': u'#da70d6',
    u'palegoldenrod': u'#eee8aa',
    u'palegreen': u'#98fb98',
    u'paleturquoise': u'#afeeee',
    u'palevioletred': u'#db7093',
    u'papayawhip': u'#ffefd5',
    u'peachpuff': u'#ffdab9',
    u'peru': u'#cd853f',
    u'pink': u'#ffc0cb',
    u'plum': u'#dda0dd',
    u'powderblue': u'#b0e0e6',
    u'purple': u'#800080',
    u'red': u'#ff0000',
    u'rosybrown': u'#bc8f8f',
    u'royalblue': u'#4169e1',
    u'saddlebrown': u'#8b4513',
    u'salmon': u'#fa8072',
    u'sandybrown': u'#f4a460',
    u'seagreen': u'#2e8b57',
    u'seashell': u'#fff5ee',
    u'sienna': u'#a0522d',
    u'silver': u'#c0c0c0',
    u'skyblue': u'#87ceeb',
    u'slateblue': u'#6a5acd',
    u'slategray': u'#708090',
    u'slategrey': u'#708090',
    u'snow': u'#fffafa',
    u'springgreen': u'#00ff7f',
    u'steelblue': u'#4682b4',
    u'tan': u'#d2b48c',
    u'teal': u'#008080',
    u'thistle': u'#d8bfd8',
    u'tomato': u'#ff6347',
    u'turquoise': u'#40e0d0',
    u'violet': u'#ee82ee',
    u'wheat': u'#f5deb3',
    u'white': u'#ffffff',
    u'whitesmoke': u'#f5f5f5',
    u'yellow': u'#ffff00',
    u'yellowgreen': u'#9acd32',
}

# Mappings of normalized hexadecimal color values to color names.
#################################################################

HTML4_HEX_TO_NAMES = _reversedict(HTML4_NAMES_TO_HEX)

CSS2_HEX_TO_NAMES = HTML4_HEX_TO_NAMES

CSS21_HEX_TO_NAMES = _reversedict(CSS21_NAMES_TO_HEX)

CSS3_HEX_TO_NAMES = _reversedict(CSS3_NAMES_TO_HEX)

# Aliases of the above mappings, for backwards compatibility.
#################################################################
(html4_names_to_hex,
 css2_names_to_hex,
 css21_names_to_hex,
 css3_names_to_hex) = (HTML4_NAMES_TO_HEX,
                       CSS2_NAMES_TO_HEX,
                       CSS21_NAMES_TO_HEX,
                       CSS3_NAMES_TO_HEX)

(html4_hex_to_names,
 css2_hex_to_names,
 css21_hex_to_names,
 css3_hex_to_names) = (HTML4_HEX_TO_NAMES,
                       CSS2_HEX_TO_NAMES,
                       CSS21_HEX_TO_NAMES,
                       CSS3_HEX_TO_NAMES)

# Normalization functions.
#################################################################

def normalize_hex(hex_value):
    """
    Normalize a hexadecimal color value to 6 digits, lowercase.

    """
    match = HEX_COLOR_RE.match(hex_value)
    if match is None:
        raise ValueError(
            u"'{}' is not a valid hexadecimal color value.".format(hex_value)
        )
    hex_digits = match.group(1)
    if len(hex_digits) == 3:
        hex_digits = u''.join(2 * s for s in hex_digits)
    return u'#{}'.format(hex_digits.lower())

def _normalize_integer_rgb(value):
    """
    Internal normalization function for clipping integer values into
    the permitted range (0-255, inclusive).

    """
    return 0 if value < 0 \
        else 255 if value > 255 \
        else value

def normalize_integer_triplet(rgb_triplet):
    """
    Normalize an integer ``rgb()`` triplet so that all values are
    within the range 0-255 inclusive.

    """
    return tuple(_normalize_integer_rgb(value) for value in rgb_triplet)

def _normalize_percent_rgb(value):
    """
    Internal normalization function for clipping percent values into
    the permitted range (0%-100%, inclusive).

    """
    percent = value.split(u'%')[0]
    percent = float(percent) if u'.' in percent else int(percent)

    return u'0%' if percent < 0 \
        else u'100%' if percent > 100 \
        else u'{}%'.format(percent)

def normalize_percent_triplet(rgb_triplet):
    """
    Normalize a percentage ``rgb()`` triplet so that all values are
    within the range 0%-100% inclusive.

    """
    return tuple(_normalize_percent_rgb(value) for value in rgb_triplet)

# Conversions from color names to various formats.
#################################################################

def name_to_hex(name, spec=u'css3'):
    """
    Convert a color name to a normalized hexadecimal color value.

    The optional keyword argument ``spec`` determines which
    specification's list of color names will be used; valid values are
    ``html4``, ``css2``, ``css21`` and ``css3``, and the default is
    ``css3``.

    When no color of that name exists in the given specification,
    ``ValueError`` is raised.

    """
    if spec not in SUPPORTED_SPECIFICATIONS:
        raise ValueError(SPECIFICATION_ERROR_TEMPLATE.format(spec=spec))
    normalized = name.lower()
    hex_value = {u'css2': CSS2_NAMES_TO_HEX,
                 u'css21': CSS21_NAMES_TO_HEX,
                 u'css3': CSS3_NAMES_TO_HEX,
                 u'html4': HTML4_NAMES_TO_HEX}[spec].get(normalized)
    if hex_value is None:
        raise ValueError(
            u"'{name}' is not defined as a named color in {spec}".format(
                name=name, spec=spec
            )
        )
    return hex_value

def name_to_rgb(name, spec=u'css3'):
    """
    Convert a color name to a 3-tuple of integers suitable for use in
    an ``rgb()`` triplet specifying that color.

    """
    return hex_to_rgb(name_to_hex(name, spec=spec))

def name_to_rgb_percent(name, spec=u'css3'):
    """
    Convert a color name to a 3-tuple of percentages suitable for use
    in an ``rgb()`` triplet specifying that color.

    """
    return rgb_to_rgb_percent(name_to_rgb(name, spec=spec))

# Conversions from hexadecimal color values to various formats.
#################################################################

def hex_to_name(hex_value, spec=u'css3'):
    """
    Convert a hexadecimal color value to its corresponding normalized
    color name, if any such name exists.

    The optional keyword argument ``spec`` determines which
    specification's list of color names will be used; valid values are
    ``html4``, ``css2``, ``css21`` and ``css3``, and the default is
    ``css3``.

    When no color name for the value is found in the given
    specification, ``ValueError`` is raised.

    """
    if spec not in SUPPORTED_SPECIFICATIONS:
        raise ValueError(SPECIFICATION_ERROR_TEMPLATE.format(spec=spec))
    normalized = normalize_hex(hex_value)
    name = {u'css2': CSS2_HEX_TO_NAMES,
            u'css21': CSS21_HEX_TO_NAMES,
            u'css3': CSS3_HEX_TO_NAMES,
            u'html4': HTML4_HEX_TO_NAMES}[spec].get(normalized)
    if name is None:
        raise ValueError(
            u"'{}' has no defined color name in {}".format(hex_value, spec)
        )
    return name

def hex_to_rgb(hex_value):
    """
    Convert a hexadecimal color value to a 3-tuple of integers
    suitable for use in an ``rgb()`` triplet specifying that color.

    """
    hex_value = normalize_hex(hex_value)
    hex_value = int(hex_value[1:], 16)
    return (hex_value >> 16,
            hex_value >> 8 & 0xff,
            hex_value & 0xff)

def hex_to_rgb_percent(hex_value):
    """
    Convert a hexadecimal color value to a 3-tuple of percentages
    suitable for use in an ``rgb()`` triplet representing that color.

    """
    return rgb_to_rgb_percent(hex_to_rgb(hex_value))

# Conversions from  integer rgb() triplets to various formats.
#################################################################

def rgb_to_name(rgb_triplet, spec=u'css3'):
    """
    Convert a 3-tuple of integers, suitable for use in an ``rgb()``
    color triplet, to its corresponding normalized color name, if any
    such name exists.

    The optional keyword argument ``spec`` determines which
    specification's list of color names will be used; valid values are
    ``html4``, ``css2``, ``css21`` and ``css3``, and the default is
    ``css3``.

    If there is no matching name, ``ValueError`` is raised.

    """
    return hex_to_name(
        rgb_to_hex(
            normalize_integer_triplet(
                rgb_triplet
            )
        ),
        spec=spec
    )

def rgb_to_hex(rgb_triplet):
    """
    Convert a 3-tuple of integers, suitable for use in an ``rgb()``
    color triplet, to a normalized hexadecimal value for that color.

    """
    return u'#{:02x}{:02x}{:02x}'.format(
        *normalize_integer_triplet(
            rgb_triplet
        )
    )

def rgb_to_rgb_percent(rgb_triplet):
    """
    Convert a 3-tuple of integers, suitable for use in an ``rgb()``
    color triplet, to a 3-tuple of percentages suitable for use in
    representing that color.

    This function makes some trade-offs in terms of the accuracy of
    the final representation; for some common integer values,
    special-case logic is used to ensure a precise result (e.g.,
    integer 128 will always convert to '50%', integer 32 will always
    convert to '12.5%'), but for all other values a standard Python
    ``float`` is used and rounded to two decimal places, which may
    result in a loss of precision for some values.

    """
    # In order to maintain precision for common values,
    # special-case them.
    specials = {255: u'100%', 128: u'50%', 64: u'25%',
                32: u'12.5%', 16: u'6.25%', 0: u'0%'}
    return tuple(specials.get(d, u'{:.02f}%'.format(d / 255.0 * 100))
                 for d in normalize_integer_triplet(rgb_triplet))

# Conversions from percentage rgb() triplets to various formats.
#################################################################

def rgb_percent_to_name(rgb_percent_triplet, spec=u'css3'):
    """
    Convert a 3-tuple of percentages, suitable for use in an ``rgb()``
    color triplet, to its corresponding normalized color name, if any
    such name exists.

    The optional keyword argument ``spec`` determines which
    specification's list of color names will be used; valid values are
    ``html4``, ``css2``, ``css21`` and ``css3``, and the default is
    ``css3``.

    If there is no matching name, ``ValueError`` is raised.

    """
    return rgb_to_name(
        rgb_percent_to_rgb(
            normalize_percent_triplet(
                rgb_percent_triplet
            )
        ),
        spec=spec
    )

def rgb_percent_to_hex(rgb_percent_triplet):
    """
    Convert a 3-tuple of percentages, suitable for use in an ``rgb()``
    color triplet, to a normalized hexadecimal color value for that
    color.

    """
    return rgb_to_hex(
        rgb_percent_to_rgb(
            normalize_percent_triplet(
                rgb_percent_triplet
            )
        )
    )

def _percent_to_integer(percent):
    """
    Internal helper for converting a percentage value to an integer
    between 0 and 255 inclusive.

    """
    return int(
        round(
            float(percent.split(u'%')[0]) / 100 * 255
        )
    )

def rgb_percent_to_rgb(rgb_percent_triplet):
    """
    Convert a 3-tuple of percentages, suitable for use in an ``rgb()``
    color triplet, to a 3-tuple of integers suitable for use in
    representing that color.

    Some precision may be lost in this conversion. See the note
    regarding precision for ``rgb_to_rgb_percent()`` for details.

    """
    return tuple(
        map(
            _percent_to_integer,
            normalize_percent_triplet(
                rgb_percent_triplet
            )
        )
    )

# HTML5 color algorithms.
#################################################################

# These functions are written in a way that may seem strange to
# developers familiar with Python, because they do not use the most
# efficient or idiomatic way of accomplishing their tasks. This is
# because, for compliance, these functions are written as literal
# translations into Python of the algorithms in HTML5.
#
# For ease of understanding, the relevant steps of the algorithm from
# the standard are included as comments interspersed in the
# implementation.

def html5_parse_simple_color(input):
    """
    Apply the simple color parsing algorithm from section 2.4.6 of
    HTML5.

    """
    # 1. Let input be the string being parsed.
    #
    # 2. If input is not exactly seven characters long, then return an
    #    error.
    if not isinstance(input, unicode) or len(input) != 7:
        raise ValueError(
            u"An HTML5 simple color must be a Unicode string "
            u"exactly seven characters long."
        )

    # 3. If the first character in input is not a U+0023 NUMBER SIGN
    #    character (#), then return an error.
    if not input.startswith('#'):
        raise ValueError(
            u"An HTML5 simple color must begin with the "
            u"character '#' (U+0023)."
        )

    # 4. If the last six characters of input are not all ASCII hex
    #    digits, then return an error.
    if not all(c in string.hexdigits for c in input[1:]):
        raise ValueError(
            u"An HTML5 simple color must contain exactly six ASCII hex digits."
        )

    # 5. Let result be a simple color.
    #
    # 6. Interpret the second and third characters as a hexadecimal
    #    number and let the result be the red component of result.
    #
    # 7. Interpret the fourth and fifth characters as a hexadecimal
    #    number and let the result be the green component of result.
    #
    # 8. Interpret the sixth and seventh characters as a hexadecimal
    #    number and let the result be the blue component of result.
    #
    # 9. Return result.
    result = (int(input[1:3], 16),
              int(input[3:5], 16),
              int(input[5:7], 16))
    return result

def html5_serialize_simple_color(simple_color):
    """
    Apply the serialization algorithm for a simple color from section
    2.4.6 of HTML5.

    """
    red, green, blue = simple_color

    # 1. Let result be a string consisting of a single "#" (U+0023)
    #    character.
    result = u'#'

    # 2. Convert the red, green, and blue components in turn to
    #    two-digit hexadecimal numbers using lowercase ASCII hex
    #    digits, zero-padding if necessary, and append these numbers
    #    to result, in the order red, green, blue.
    format_string = '{:02x}'
    result += format_string.format(red)
    result += format_string.format(green)
    result += format_string.format(blue)

    # 3. Return result, which will be a valid lowercase simple color.
    return result

def html5_parse_legacy_color(input):
    """
    Apply the legacy color parsing algorithm from section 2.4.6 of
    HTML5.

    """
    # 1. Let input be the string being parsed.
    if not isinstance(input, unicode):
        raise ValueError(
            u"HTML5 legacy color parsing requires a Unicode string as input."
        )

    # 2. If input is the empty string, then return an error.
    if input == "":
        raise ValueError(
            u"HTML5 legacy color parsing forbids empty string as a value."
        )

    # 3. Strip leading and trailing whitespace from input.
    input = input.strip()

    # 4. If input is an ASCII case-insensitive match for the string
    #    "transparent", then return an error.
    if input.lower() == u"transparent":
        raise ValueError(
            u'HTML5 legacy color parsing forbids "transparent" as a value.'
        )

    # 5. If input is an ASCII case-insensitive match for one of the
    #    keywords listed in the SVG color keywords section of the CSS3
    #    Color specification, then return the simple color
    #    corresponding to that keyword.
    keyword_hex = CSS3_NAMES_TO_HEX.get(input.lower())
    if keyword_hex is not None:
        return html5_parse_simple_color(keyword_hex)

    # 6. If input is four characters long, and the first character in
    #    input is a "#" (U+0023) character, and the last three
    #    characters of input are all ASCII hex digits, then run these
    #    substeps:
    if len(input) == 4 and \
       input.startswith(u'#') and \
       all(c in string.hexdigits for c in input[1:]):
        # 1. Let result be a simple color.
        #
        # 2. Interpret the second character of input as a hexadecimal
        #    digit; let the red component of result be the resulting
        #    number multiplied by 17.
        #
        # 3. Interpret the third character of input as a hexadecimal
        #    digit; let the green component of result be the resulting
        #    number multiplied by 17.
        #
        # 4. Interpret the fourth character of input as a hexadecimal
        #    digit; let the blue component of result be the resulting
        #    number multiplied by 17.
        result = (int(input[1], 16) * 17,
                  int(input[2], 16) * 17,
                  int(input[3], 16) * 17)

        # 5. Return result.
        return result

    # 7. Replace any characters in input that have a Unicode code
    #    point greater than U+FFFF (i.e. any characters that are not
    #    in the basic multilingual plane) with the two-character
    #    string "00".

    # This one's a bit weird due to the existence of multiple internal
    # Unicode string representations in different versions and builds
    # of Python.
    #
    # From Python 2.2 through 3.2, Python could be compiled with
    # "narrow" or "wide" Unicode strings (see PEP 261). Narrow builds
    # handled Unicode strings with two-byte characters and surrogate
    # pairs for non-BMP code points. Wide builds handled Unicode
    # strings with four-byte characters and no surrogates. This means
    # ord() is only sufficient to identify a non-BMP character on a
    # wide build.
    #
    # Starting with Python 3.3, the internal string representation
    # (see PEP 393) is now dynamic, and Python chooses an encoding --
    # either latin-1, UCS-2 or UCS-4 -- wide enough to handle the
    # highest code point in the string.
    #
    # The code below bypasses all of that for a consistently effective
    # method: encode the string to little-endian UTF-32, then perform
    # a binary unpack of it as four-byte integers. Those integers will
    # be the Unicode code points, and from there filtering out non-BMP
    # code points is easy.
    encoded_input = input.encode('utf_32_le')

    # Format string is '<' (for little-endian byte order), then a
    # sequence of 'L' characters (for 4-byte unsigned long integer)
    # equal to the length of the original string, which is also
    # one-fourth the encoded length.  For example, for a six-character
    # input the generated format string will be '<LLLLLL'.
    format_string = '<' + ('L' * (int(len(encoded_input) / 4)))
    codepoints = struct.unpack(format_string, encoded_input)
    input = ''.join(u'00' if c > 0xffff
                    else unichr(c)
                    for c in codepoints)

    # 8. If input is longer than 128 characters, truncate input,
    #    leaving only the first 128 characters.
    if len(input) > 128:
        input = input[:128]

    # 9. If the first character in input is a "#" (U+0023) character,
    #    remove it.
    if input.startswith(u'#'):
        input = input[1:]

    # 10. Replace any character in input that is not an ASCII hex
    #     digit with the character "0" (U+0030).
    if any(c for c in input if c not in string.hexdigits):
        input = ''.join(c if c in string.hexdigits else u'0' for c in input)

    # 11. While input's length is zero or not a multiple of three,
    #     append a "0" (U+0030) character to input.
    while (len(input) == 0) or (len(input) % 3 != 0):
        input += u'0'

    # 12. Split input into three strings of equal length, to obtain
    #     three components. Let length be the length of those
    #     components (one third the length of input).
    length = int(len(input) / 3)
    red = input[:length]
    green = input[length:length*2]
    blue = input[length*2:]

    # 13. If length is greater than 8, then remove the leading
    #     length-8 characters in each component, and let length be 8.
    if length > 8:
        red, green, blue = (red[length-8:],
                            green[length-8:],
                            blue[length-8:])
        length = 8

    # 14. While length is greater than two and the first character in
    #     each component is a "0" (U+0030) character, remove that
    #     character and reduce length by one.
    while (length > 2) and (red[0] == u'0' and
                            green[0] == u'0' and
                            blue[0] == u'0'):
        red, green, blue = (red[1:],
                            green[1:],
                            blue[1:])
        length -= 1

    # 15. If length is still greater than two, truncate each
    #     component, leaving only the first two characters in each.
    if length > 2:
        red, green, blue = (red[:2],
                            green[:2],
                            blue[:2])

    # 16. Let result be a simple color.
    #
    # 17. Interpret the first component as a hexadecimal number; let
    #     the red component of result be the resulting number.
    #
    # 18. Interpret the second component as a hexadecimal number; let
    #     the green component of result be the resulting number.
    #
    # 19. Interpret the third component as a hexadecimal number; let
    #     the blue component of result be the resulting number.
    result = (int(red, 16),
              int(green, 16),
              int(blue, 16))

    # 20. Return result.
    return result