Attempt to host (large) JavaScript

This commit is contained in:
Chris Hodapp 2020-07-11 20:00:00 -04:00
parent 9bfa83594f
commit 535ac1eb15
89 changed files with 186815 additions and 0 deletions

View File

@ -18,6 +18,12 @@ Uncomment the `(run-sketch)` and the below should work:
- Emacs - run cider, open `core.clj` and press `C-c C-k` to evaluate the file.
- REPL - run `(require 'quil_perlin.core)`.
## Demo
See
[here](https://cdn.jsdelivr.net/gh/hodapp87/curlnoise@master/resources/public/index.html)
to run the ClojureScript version in the browser.
## TODO
- Better docs.

361
resources/public/js/main.js Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,386 @@
goog.provide('cljs.core.constants');
goog.require('cljs.core');
cljs.core.cst$kw$y = new cljs.core.Keyword(null,"y","y",-1757859776);
cljs.core.cst$sym$form = new cljs.core.Symbol(null,"form","form",16469056,null);
cljs.core.cst$kw$key_DASH_code = new cljs.core.Keyword(null,"key-code","key-code",-1732114304);
cljs.core.cst$sym$tag = new cljs.core.Symbol(null,"tag","tag",350170304,null);
cljs.core.cst$sym$_AMPERSAND_ = new cljs.core.Symbol(null,"&","&",-2144855648,null);
cljs.core.cst$sym$uuid = new cljs.core.Symbol(null,"uuid","uuid",-504564192,null);
cljs.core.cst$kw$shift = new cljs.core.Keyword(null,"shift","shift",997140064);
cljs.core.cst$kw$features = new cljs.core.Keyword(null,"features","features",-1146962336);
cljs.core.cst$sym$case_STAR_ = new cljs.core.Symbol(null,"case*","case*",-1938255072,null);
cljs.core.cst$kw$open = new cljs.core.Keyword(null,"open","open",-1763596448);
cljs.core.cst$kw$baseline = new cljs.core.Keyword(null,"baseline","baseline",1151033280);
cljs.core.cst$kw$bold = new cljs.core.Keyword(null,"bold","bold",-116809535);
cljs.core.cst$sym$end = new cljs.core.Symbol(null,"end","end",1372345569,null);
cljs.core.cst$kw$disable_DASH_stroke_DASH_perspective = new cljs.core.Keyword(null,"disable-stroke-perspective","disable-stroke-perspective",479198433);
cljs.core.cst$sym$quil$core_SLASH_PGraphics = new cljs.core.Symbol("quil.core","PGraphics","quil.core/PGraphics",-1265754815,null);
cljs.core.cst$sym$defrecord_STAR_ = new cljs.core.Symbol(null,"defrecord*","defrecord*",-1936366207,null);
cljs.core.cst$sym$base = new cljs.core.Symbol(null,"base","base",1825810849,null);
cljs.core.cst$sym$obj = new cljs.core.Symbol(null,"obj","obj",-1672671807,null);
cljs.core.cst$kw$hsb = new cljs.core.Keyword(null,"hsb","hsb",-753472031);
cljs.core.cst$kw$quads = new cljs.core.Keyword(null,"quads","quads",1347497505);
cljs.core.cst$sym$fqn = new cljs.core.Symbol(null,"fqn","fqn",-1749334463,null);
cljs.core.cst$kw$arrow = new cljs.core.Keyword(null,"arrow","arrow",1071351425);
cljs.core.cst$kw$dilate = new cljs.core.Keyword(null,"dilate","dilate",1504745153);
cljs.core.cst$sym$first = new cljs.core.Symbol(null,"first","first",996428481,null);
cljs.core.cst$sym$quil$core_SLASH_current_DASH_fill = new cljs.core.Symbol("quil.core","current-fill","quil.core/current-fill",269663137,null);
cljs.core.cst$sym$try = new cljs.core.Symbol(null,"try","try",-1273693247,null);
cljs.core.cst$sym$has_DASH_nil_QMARK_ = new cljs.core.Symbol(null,"has-nil?","has-nil?",825886722,null);
cljs.core.cst$sym$quil$core_SLASH__STAR_graphics_STAR_ = new cljs.core.Symbol("quil.core","*graphics*","quil.core/*graphics*",-1088142302,null);
cljs.core.cst$kw$quad_DASH_strip = new cljs.core.Keyword(null,"quad-strip","quad-strip",-1297270686);
cljs.core.cst$kw$down = new cljs.core.Keyword(null,"down","down",1565245570);
cljs.core.cst$sym$cljs$core_SLASH_sequential_QMARK_ = new cljs.core.Symbol("cljs.core","sequential?","cljs.core/sequential?",1777854658,null);
cljs.core.cst$kw$disable_DASH_depth_DASH_mask = new cljs.core.Keyword(null,"disable-depth-mask","disable-depth-mask",3298562);
cljs.core.cst$kw$pixels_DASH_in_DASH_360 = new cljs.core.Keyword(null,"pixels-in-360","pixels-in-360",1789567298);
cljs.core.cst$kw$frame_DASH_rate = new cljs.core.Keyword(null,"frame-rate","frame-rate",-994918942);
cljs.core.cst$kw$p_DASH_y = new cljs.core.Keyword(null,"p-y","p-y",-530704830);
cljs.core.cst$sym$tr__5974__auto__ = new cljs.core.Symbol(null,"tr__5974__auto__","tr__5974__auto__",-592553406,null);
cljs.core.cst$kw$step_DASH_size = new cljs.core.Keyword(null,"step-size","step-size",1545609922);
cljs.core.cst$kw$zoom = new cljs.core.Keyword(null,"zoom","zoom",-1827487038);
cljs.core.cst$sym$quil$core_SLASH_no_DASH_fill = new cljs.core.Symbol("quil.core","no-fill","quil.core/no-fill",-389901598,null);
cljs.core.cst$sym$rear = new cljs.core.Symbol(null,"rear","rear",-900164830,null);
cljs.core.cst$sym$hierarchy = new cljs.core.Symbol(null,"hierarchy","hierarchy",587061186,null);
cljs.core.cst$sym$iter = new cljs.core.Symbol(null,"iter","iter",-1346195486,null);
cljs.core.cst$sym$cljs$core_SLASH_binding = new cljs.core.Symbol("cljs.core","binding","cljs.core/binding",2050379843,null);
cljs.core.cst$kw$space = new cljs.core.Keyword(null,"space","space",348133475);
cljs.core.cst$kw$burn = new cljs.core.Keyword(null,"burn","burn",-458179293);
cljs.core.cst$sym$step = new cljs.core.Symbol(null,"step","step",-1365547645,null);
cljs.core.cst$sym$boolean = new cljs.core.Symbol(null,"boolean","boolean",-278886877,null);
cljs.core.cst$kw$key_DASH_typed = new cljs.core.Keyword(null,"key-typed","key-typed",-876037597);
cljs.core.cst$sym$update_DASH_count = new cljs.core.Symbol(null,"update-count","update-count",-411982269,null);
cljs.core.cst$kw$mouse_DASH_clicked = new cljs.core.Keyword(null,"mouse-clicked","mouse-clicked",-199339421);
cljs.core.cst$sym$method_DASH_table = new cljs.core.Symbol(null,"method-table","method-table",-1878263165,null);
cljs.core.cst$kw$mouse_DASH_released = new cljs.core.Keyword(null,"mouse-released","mouse-released",-664480061);
cljs.core.cst$kw$ready = new cljs.core.Keyword(null,"ready","ready",1086465795);
cljs.core.cst$kw$posterize = new cljs.core.Keyword(null,"posterize","posterize",-148251901);
cljs.core.cst$sym$chunk = new cljs.core.Symbol(null,"chunk","chunk",449371907,null);
cljs.core.cst$kw$fn = new cljs.core.Keyword(null,"fn","fn",-1175266204);
cljs.core.cst$sym$quil$sketch_SLASH_with_DASH_sketch = new cljs.core.Symbol("quil.sketch","with-sketch","quil.sketch/with-sketch",-634067708,null);
cljs.core.cst$sym$i = new cljs.core.Symbol(null,"i","i",253690212,null);
cljs.core.cst$kw$f8 = new cljs.core.Keyword(null,"f8","f8",-2141475484);
cljs.core.cst$kw$bolditalic = new cljs.core.Keyword(null,"bolditalic","bolditalic",-1629061788);
cljs.core.cst$sym$rest = new cljs.core.Symbol(null,"rest","rest",398835108,null);
cljs.core.cst$kw$meta = new cljs.core.Keyword(null,"meta","meta",1499536964);
cljs.core.cst$sym$quil$core_SLASH_translate = new cljs.core.Symbol("quil.core","translate","quil.core/translate",150889028,null);
cljs.core.cst$kw$screen = new cljs.core.Keyword(null,"screen","screen",1990059748);
cljs.core.cst$kw$enable_DASH_depth_DASH_test = new cljs.core.Keyword(null,"enable-depth-test","enable-depth-test",1519326084);
cljs.core.cst$kw$mouse_DASH_exited = new cljs.core.Keyword(null,"mouse-exited","mouse-exited",-483205244);
cljs.core.cst$kw$gray = new cljs.core.Keyword(null,"gray","gray",1013268388);
cljs.core.cst$kw$frame = new cljs.core.Keyword(null,"frame","frame",-1711082588);
cljs.core.cst$kw$enable_DASH_depth_DASH_sort = new cljs.core.Keyword(null,"enable-depth-sort","enable-depth-sort",-383089627);
cljs.core.cst$kw$f1 = new cljs.core.Keyword(null,"f1","f1",1714532389);
cljs.core.cst$sym$stroke__5954__auto__ = new cljs.core.Symbol(null,"stroke__5954__auto__","stroke__5954__auto__",-1076007899,null);
cljs.core.cst$sym$quil$core_SLASH_no_DASH_stroke = new cljs.core.Symbol("quil.core","no-stroke","quil.core/no-stroke",779188421,null);
cljs.core.cst$kw$dup = new cljs.core.Keyword(null,"dup","dup",556298533);
cljs.core.cst$kw$java2d = new cljs.core.Keyword(null,"java2d","java2d",166099237);
cljs.core.cst$kw$corner = new cljs.core.Keyword(null,"corner","corner",1296717125);
cljs.core.cst$kw$disable_DASH_texture_DASH_mipmaps = new cljs.core.Keyword(null,"disable-texture-mipmaps","disable-texture-mipmaps",1697917541);
cljs.core.cst$kw$key = new cljs.core.Keyword(null,"key","key",-1516042587);
cljs.core.cst$sym$comp = new cljs.core.Symbol(null,"comp","comp",-1462482139,null);
cljs.core.cst$sym$dispatch_DASH_fn = new cljs.core.Symbol(null,"dispatch-fn","dispatch-fn",-1401088155,null);
cljs.core.cst$kw$darkest = new cljs.core.Keyword(null,"darkest","darkest",68197253);
cljs.core.cst$sym$buffer = new cljs.core.Symbol(null,"buffer","buffer",-2037140571,null);
cljs.core.cst$kw$f10 = new cljs.core.Keyword(null,"f10","f10",627525541);
cljs.core.cst$kw$dodge = new cljs.core.Keyword(null,"dodge","dodge",-1556666427);
cljs.core.cst$kw$bottom = new cljs.core.Keyword(null,"bottom","bottom",-1550509018);
cljs.core.cst$kw$bevel = new cljs.core.Keyword(null,"bevel","bevel",2090515654);
cljs.core.cst$sym$quil$core_SLASH_end_DASH_draw = new cljs.core.Symbol("quil.core","end-draw","quil.core/end-draw",1718268198,null);
cljs.core.cst$kw$else = new cljs.core.Keyword(null,"else","else",-1508377146);
cljs.core.cst$kw$on_DASH_close = new cljs.core.Keyword(null,"on-close","on-close",-761178394);
cljs.core.cst$kw$disable_DASH_stroke_DASH_pure = new cljs.core.Keyword(null,"disable-stroke-pure","disable-stroke-pure",735493926);
cljs.core.cst$kw$replace = new cljs.core.Keyword(null,"replace","replace",-786587770);
cljs.core.cst$kw$alt = new cljs.core.Keyword(null,"alt","alt",-3214426);
cljs.core.cst$sym$left = new cljs.core.Symbol(null,"left","left",1241415590,null);
cljs.core.cst$sym$ns_STAR_ = new cljs.core.Symbol(null,"ns*","ns*",1840949383,null);
cljs.core.cst$kw$cljs$core_SLASH_none = new cljs.core.Keyword("cljs.core","none","cljs.core/none",926646439);
cljs.core.cst$sym$shift = new cljs.core.Symbol(null,"shift","shift",-1657295705,null);
cljs.core.cst$sym$iters = new cljs.core.Symbol(null,"iters","iters",719353031,null);
cljs.core.cst$kw$button = new cljs.core.Keyword(null,"button","button",1456579943);
cljs.core.cst$kw$top = new cljs.core.Keyword(null,"top","top",-1856271961);
cljs.core.cst$kw$mouse_DASH_wheel = new cljs.core.Keyword(null,"mouse-wheel","mouse-wheel",1811662439);
cljs.core.cst$sym$cljs$core_SLASH_apply = new cljs.core.Symbol("cljs.core","apply","cljs.core/apply",1757277831,null);
cljs.core.cst$sym$xform = new cljs.core.Symbol(null,"xform","xform",-85179481,null);
cljs.core.cst$kw$validator = new cljs.core.Keyword(null,"validator","validator",-1966190681);
cljs.core.cst$kw$disable_DASH_depth_DASH_test = new cljs.core.Keyword(null,"disable-depth-test","disable-depth-test",284606407);
cljs.core.cst$sym$finally = new cljs.core.Symbol(null,"finally","finally",-1065347064,null);
cljs.core.cst$kw$keyPressed = new cljs.core.Keyword(null,"keyPressed","keyPressed",1791025256);
cljs.core.cst$kw$default = new cljs.core.Keyword(null,"default","default",-1987822328);
cljs.core.cst$sym$prefer_DASH_table = new cljs.core.Symbol(null,"prefer-table","prefer-table",462168584,null);
cljs.core.cst$sym$loop_STAR_ = new cljs.core.Symbol(null,"loop*","loop*",615029416,null);
cljs.core.cst$sym$watches = new cljs.core.Symbol(null,"watches","watches",1367433992,null);
cljs.core.cst$kw$ns = new cljs.core.Keyword(null,"ns","ns",441598760);
cljs.core.cst$kw$grid = new cljs.core.Keyword(null,"grid","grid",402978600);
cljs.core.cst$kw$pie = new cljs.core.Keyword(null,"pie","pie",1530441672);
cljs.core.cst$sym$quil$core_SLASH_pop_DASH_matrix = new cljs.core.Symbol("quil.core","pop-matrix","quil.core/pop-matrix",310892617,null);
cljs.core.cst$kw$decor = new cljs.core.Keyword(null,"decor","decor",-1730969431);
cljs.core.cst$kw$w = new cljs.core.Keyword(null,"w","w",354169001);
cljs.core.cst$kw$pending = new cljs.core.Keyword(null,"pending","pending",-220036727);
cljs.core.cst$sym$bitmap = new cljs.core.Symbol(null,"bitmap","bitmap",501334601,null);
cljs.core.cst$kw$enable_DASH_stroke_DASH_perspective = new cljs.core.Keyword(null,"enable-stroke-perspective","enable-stroke-perspective",-259923319);
cljs.core.cst$sym$_seq = new cljs.core.Symbol(null,"_seq","_seq",-449557847,null);
cljs.core.cst$sym$nil_DASH_val = new cljs.core.Symbol(null,"nil-val","nil-val",-513933559,null);
cljs.core.cst$kw$opengl = new cljs.core.Keyword(null,"opengl","opengl",-614998103);
cljs.core.cst$kw$mouse_DASH_moved = new cljs.core.Keyword(null,"mouse-moved","mouse-moved",-1918152310);
cljs.core.cst$kw$file = new cljs.core.Keyword(null,"file","file",-1269645878);
cljs.core.cst$sym$v = new cljs.core.Symbol(null,"v","v",1661996586,null);
cljs.core.cst$kw$end_DASH_column = new cljs.core.Keyword(null,"end-column","end-column",1425389514);
cljs.core.cst$kw$safe_DASH_draw_DASH_fn = new cljs.core.Keyword(null,"safe-draw-fn","safe-draw-fn",1454900202);
cljs.core.cst$kw$chord = new cljs.core.Keyword(null,"chord","chord",-696248342);
cljs.core.cst$sym$riter = new cljs.core.Symbol(null,"riter","riter",-237834262,null);
cljs.core.cst$sym$__hash = new cljs.core.Symbol(null,"__hash","__hash",-1328796629,null);
cljs.core.cst$kw$rgb = new cljs.core.Keyword(null,"rgb","rgb",1432123467);
cljs.core.cst$sym$meta = new cljs.core.Symbol(null,"meta","meta",-1154898805,null);
cljs.core.cst$sym$_meta = new cljs.core.Symbol(null,"_meta","_meta",-1716892533,null);
cljs.core.cst$sym$meta1566 = new cljs.core.Symbol(null,"meta1566","meta1566",-1745668885,null);
cljs.core.cst$sym$$dispose = new cljs.core.Symbol(null,".dispose",".dispose",-1697594101,null);
cljs.core.cst$sym$gr__5983__auto__ = new cljs.core.Symbol(null,"gr__5983__auto__","gr__5983__auto__",1565352203,null);
cljs.core.cst$kw$lines = new cljs.core.Keyword(null,"lines","lines",-700165781);
cljs.core.cst$sym$afn = new cljs.core.Symbol(null,"afn","afn",216963467,null);
cljs.core.cst$kw$argb = new cljs.core.Keyword(null,"argb","argb",633844107);
cljs.core.cst$kw$mouseOut = new cljs.core.Keyword(null,"mouseOut","mouseOut",-386669045);
cljs.core.cst$kw$move = new cljs.core.Keyword(null,"move","move",-2110884309);
cljs.core.cst$sym$tree = new cljs.core.Symbol(null,"tree","tree",1444219499,null);
cljs.core.cst$sym$fn = new cljs.core.Symbol(null,"fn","fn",465265323,null);
cljs.core.cst$kw$f5 = new cljs.core.Keyword(null,"f5","f5",1587057387);
cljs.core.cst$kw$settings = new cljs.core.Keyword(null,"settings","settings",1556144875);
cljs.core.cst$sym$front = new cljs.core.Symbol(null,"front","front",117022539,null);
cljs.core.cst$sym$buf = new cljs.core.Symbol(null,"buf","buf",1426618187,null);
cljs.core.cst$sym$tr__5965__auto__ = new cljs.core.Symbol(null,"tr__5965__auto__","tr__5965__auto__",-816620693,null);
cljs.core.cst$kw$command = new cljs.core.Keyword(null,"command","command",-894540724);
cljs.core.cst$sym$next_DASH_entry = new cljs.core.Symbol(null,"next-entry","next-entry",1091342476,null);
cljs.core.cst$kw$val = new cljs.core.Keyword(null,"val","val",128701612);
cljs.core.cst$sym$key = new cljs.core.Symbol(null,"key","key",124488940,null);
cljs.core.cst$sym$_next = new cljs.core.Symbol(null,"_next","_next",101877036,null);
cljs.core.cst$sym$quil$core_SLASH_stroke = new cljs.core.Symbol("quil.core","stroke","quil.core/stroke",577473004,null);
cljs.core.cst$kw$hsl = new cljs.core.Keyword(null,"hsl","hsl",-1282948596);
cljs.core.cst$kw$update = new cljs.core.Keyword(null,"update","update",1045576396);
cljs.core.cst$sym$root_DASH_iter = new cljs.core.Symbol(null,"root-iter","root-iter",1974672108,null);
cljs.core.cst$sym$do = new cljs.core.Symbol(null,"do","do",1686842252,null);
cljs.core.cst$sym$vec = new cljs.core.Symbol(null,"vec","vec",982683596,null);
cljs.core.cst$kw$miter = new cljs.core.Keyword(null,"miter","miter",327727052);
cljs.core.cst$kw$points = new cljs.core.Keyword(null,"points","points",-1486596883);
cljs.core.cst$kw$fallback_DASH_impl = new cljs.core.Keyword(null,"fallback-impl","fallback-impl",-1501286995);
cljs.core.cst$kw$keyword_DASH_fn = new cljs.core.Keyword(null,"keyword-fn","keyword-fn",-64566675);
cljs.core.cst$kw$hand = new cljs.core.Keyword(null,"hand","hand",791601933);
cljs.core.cst$kw$flush_DASH_on_DASH_newline = new cljs.core.Keyword(null,"flush-on-newline","flush-on-newline",-151457939);
cljs.core.cst$kw$mouseDragged = new cljs.core.Keyword(null,"mouseDragged","mouseDragged",129975181);
cljs.core.cst$sym$default_DASH_dispatch_DASH_val = new cljs.core.Symbol(null,"default-dispatch-val","default-dispatch-val",-1231201266,null);
cljs.core.cst$kw$no_DASH_test = new cljs.core.Keyword(null,"no-test","no-test",-1679482642);
cljs.core.cst$kw$close = new cljs.core.Keyword(null,"close","close",1835149582);
cljs.core.cst$sym$fill__5943__auto__ = new cljs.core.Symbol(null,"fill__5943__auto__","fill__5943__auto__",-2049998450,null);
cljs.core.cst$sym$validator = new cljs.core.Symbol(null,"validator","validator",-325659154,null);
cljs.core.cst$kw$radius = new cljs.core.Keyword(null,"radius","radius",-2073122258);
cljs.core.cst$kw$normal = new cljs.core.Keyword(null,"normal","normal",-1519123858);
cljs.core.cst$sym$letfn_STAR_ = new cljs.core.Symbol(null,"letfn*","letfn*",-110097810,null);
cljs.core.cst$sym$if = new cljs.core.Symbol(null,"if","if",1181717262,null);
cljs.core.cst$kw$s = new cljs.core.Keyword(null,"s","s",1705939918);
cljs.core.cst$sym$previous_DASH_fill__5944__auto__ = new cljs.core.Symbol(null,"previous-fill__5944__auto__","previous-fill__5944__auto__",1519336559,null);
cljs.core.cst$sym$arr = new cljs.core.Symbol(null,"arr","arr",2115492975,null);
cljs.core.cst$kw$threshold = new cljs.core.Keyword(null,"threshold","threshold",204221583);
cljs.core.cst$sym$new = new cljs.core.Symbol(null,"new","new",-444906321,null);
cljs.core.cst$kw$rotate_DASH_on = new cljs.core.Keyword(null,"rotate-on","rotate-on",-1282225937);
cljs.core.cst$kw$up = new cljs.core.Keyword(null,"up","up",-269712113);
cljs.core.cst$kw$descendants = new cljs.core.Keyword(null,"descendants","descendants",1824886031);
cljs.core.cst$kw$renderer = new cljs.core.Keyword(null,"renderer","renderer",336841071);
cljs.core.cst$sym$ns = new cljs.core.Symbol(null,"ns","ns",2082130287,null);
cljs.core.cst$kw$size = new cljs.core.Keyword(null,"size","size",1098693007);
cljs.core.cst$kw$title = new cljs.core.Keyword(null,"title","title",636505583);
cljs.core.cst$kw$column = new cljs.core.Keyword(null,"column","column",2078222095);
cljs.core.cst$kw$center = new cljs.core.Keyword(null,"center","center",-748944368);
cljs.core.cst$sym$completed = new cljs.core.Symbol(null,"completed","completed",1154475024,null);
cljs.core.cst$kw$setup = new cljs.core.Keyword(null,"setup","setup",1987730512);
cljs.core.cst$kw$mouse_DASH_pressed = new cljs.core.Keyword(null,"mouse-pressed","mouse-pressed",736955536);
cljs.core.cst$kw$ancestors = new cljs.core.Keyword(null,"ancestors","ancestors",-776045424);
cljs.core.cst$kw$middleware = new cljs.core.Keyword(null,"middleware","middleware",1462115504);
cljs.core.cst$kw$round = new cljs.core.Keyword(null,"round","round",2009433328);
cljs.core.cst$kw$disable_DASH_optimized_DASH_stroke = new cljs.core.Keyword(null,"disable-optimized-stroke","disable-optimized-stroke",74038544);
cljs.core.cst$kw$mouseWheel = new cljs.core.Keyword(null,"mouseWheel","mouseWheel",-1057803856);
cljs.core.cst$sym$value = new cljs.core.Symbol(null,"value","value",1946509744,null);
cljs.core.cst$kw$focus_DASH_gained = new cljs.core.Keyword(null,"focus-gained","focus-gained",-857086384);
cljs.core.cst$sym$name = new cljs.core.Symbol(null,"name","name",-810760592,null);
cljs.core.cst$kw$readably = new cljs.core.Keyword(null,"readably","readably",1129599760);
cljs.core.cst$kw$more_DASH_marker = new cljs.core.Keyword(null,"more-marker","more-marker",-14717935);
cljs.core.cst$sym$cljs$core_SLASH_let = new cljs.core.Symbol("cljs.core","let","cljs.core/let",-308701135,null);
cljs.core.cst$sym$fields = new cljs.core.Symbol(null,"fields","fields",-291534703,null);
cljs.core.cst$kw$corners = new cljs.core.Keyword(null,"corners","corners",-137817903);
cljs.core.cst$sym$method_DASH_cache = new cljs.core.Symbol(null,"method-cache","method-cache",1230193905,null);
cljs.core.cst$kw$z = new cljs.core.Keyword(null,"z","z",-789527183);
cljs.core.cst$kw$f11 = new cljs.core.Keyword(null,"f11","f11",-1417398799);
cljs.core.cst$kw$host = new cljs.core.Keyword(null,"host","host",-1558485167);
cljs.core.cst$sym$edit = new cljs.core.Symbol(null,"edit","edit",-1302639,null);
cljs.core.cst$sym$cljs$core_SLASH_cond = new cljs.core.Symbol("cljs.core","cond","cljs.core/cond",2005388338,null);
cljs.core.cst$sym$editable_QMARK_ = new cljs.core.Symbol(null,"editable?","editable?",-164945806,null);
cljs.core.cst$kw$opaque = new cljs.core.Keyword(null,"opaque","opaque",-1243552654);
cljs.core.cst$kw$straight = new cljs.core.Keyword(null,"straight","straight",-1252567854);
cljs.core.cst$sym$base_DASH_count = new cljs.core.Symbol(null,"base-count","base-count",-1180647182,null);
cljs.core.cst$kw$overlay = new cljs.core.Keyword(null,"overlay","overlay",-139131598);
cljs.core.cst$kw$mouse_DASH_entered = new cljs.core.Keyword(null,"mouse-entered","mouse-entered",811350322);
cljs.core.cst$sym$collision_DASH_hash = new cljs.core.Symbol(null,"collision-hash","collision-hash",-35831342,null);
cljs.core.cst$sym$deftype_STAR_ = new cljs.core.Symbol(null,"deftype*","deftype*",962659890,null);
cljs.core.cst$sym$let_STAR_ = new cljs.core.Symbol(null,"let*","let*",1920721458,null);
cljs.core.cst$kw$enable_DASH_opengl_DASH_errors = new cljs.core.Keyword(null,"enable-opengl-errors","enable-opengl-errors",89998962);
cljs.core.cst$sym$start = new cljs.core.Symbol(null,"start","start",1285322546,null);
cljs.core.cst$sym$sourceIter = new cljs.core.Symbol(null,"sourceIter","sourceIter",1068220306,null);
cljs.core.cst$sym$coll = new cljs.core.Symbol(null,"coll","coll",-1006698606,null);
cljs.core.cst$sym$not_DASH_native = new cljs.core.Symbol(null,"not-native","not-native",-236392494,null);
cljs.core.cst$sym$js_STAR_ = new cljs.core.Symbol(null,"js*","js*",-1134233646,null);
cljs.core.cst$kw$enable_DASH_stroke_DASH_pure = new cljs.core.Keyword(null,"enable-stroke-pure","enable-stroke-pure",881345587);
cljs.core.cst$sym$strobj = new cljs.core.Symbol(null,"strobj","strobj",1088091283,null);
cljs.core.cst$kw$no_DASH_safe_DASH_draw = new cljs.core.Keyword(null,"no-safe-draw","no-safe-draw",-1157778157);
cljs.core.cst$kw$line = new cljs.core.Keyword(null,"line","line",212345235);
cljs.core.cst$kw$blur = new cljs.core.Keyword(null,"blur","blur",-453500461);
cljs.core.cst$sym$_rest = new cljs.core.Symbol(null,"_rest","_rest",-2100466189,null);
cljs.core.cst$kw$enable_DASH_async_DASH_saveframe = new cljs.core.Keyword(null,"enable-async-saveframe","enable-async-saveframe",-1817644525);
cljs.core.cst$kw$project = new cljs.core.Keyword(null,"project","project",1124394579);
cljs.core.cst$kw$triangle_DASH_fan = new cljs.core.Keyword(null,"triangle-fan","triangle-fan",1743150739);
cljs.core.cst$kw$focus_DASH_lost = new cljs.core.Keyword(null,"focus-lost","focus-lost",-554849613);
cljs.core.cst$sym$fn_STAR_ = new cljs.core.Symbol(null,"fn*","fn*",-752876845,null);
cljs.core.cst$kw$f3 = new cljs.core.Keyword(null,"f3","f3",1954829043);
cljs.core.cst$sym$val = new cljs.core.Symbol(null,"val","val",1769233139,null);
cljs.core.cst$sym$ascending_QMARK_ = new cljs.core.Symbol(null,"ascending?","ascending?",-1938452653,null);
cljs.core.cst$sym$recur = new cljs.core.Symbol(null,"recur","recur",1202958259,null);
cljs.core.cst$sym$xf = new cljs.core.Symbol(null,"xf","xf",2042434515,null);
cljs.core.cst$sym$ci = new cljs.core.Symbol(null,"ci","ci",2049808339,null);
cljs.core.cst$kw$enable_DASH_depth_DASH_mask = new cljs.core.Keyword(null,"enable-depth-mask","enable-depth-mask",872785875);
cljs.core.cst$kw$status = new cljs.core.Keyword(null,"status","status",-1997798413);
cljs.core.cst$kw$key_DASH_pressed = new cljs.core.Keyword(null,"key-pressed","key-pressed",-757100364);
cljs.core.cst$kw$key_DASH_released = new cljs.core.Keyword(null,"key-released","key-released",215919828);
cljs.core.cst$kw$print_DASH_length = new cljs.core.Keyword(null,"print-length","print-length",1931866356);
cljs.core.cst$sym$gr__5797__auto__ = new cljs.core.Symbol(null,"gr__5797__auto__","gr__5797__auto__",804484404,null);
cljs.core.cst$kw$f2 = new cljs.core.Keyword(null,"f2","f2",396168596);
cljs.core.cst$kw$keyReleased = new cljs.core.Keyword(null,"keyReleased","keyReleased",541714964);
cljs.core.cst$sym$state = new cljs.core.Symbol(null,"state","state",-348086572,null);
cljs.core.cst$kw$control = new cljs.core.Keyword(null,"control","control",1892578036);
cljs.core.cst$kw$ok = new cljs.core.Keyword(null,"ok","ok",967785236);
cljs.core.cst$kw$current_DASH_fill = new cljs.core.Keyword(null,"current-fill","current-fill",1421462292);
cljs.core.cst$kw$difference = new cljs.core.Keyword(null,"difference","difference",1916101396);
cljs.core.cst$sym$quil$core_SLASH_push_DASH_matrix = new cljs.core.Symbol("quil.core","push-matrix","quil.core/push-matrix",1356326676,null);
cljs.core.cst$sym$vals = new cljs.core.Symbol(null,"vals","vals",-1886377036,null);
cljs.core.cst$sym$all = new cljs.core.Symbol(null,"all","all",-1762306027,null);
cljs.core.cst$kw$looping_QMARK_ = new cljs.core.Keyword(null,"looping?","looping?",78344245);
cljs.core.cst$kw$cljs$core_SLASH_halt = new cljs.core.Keyword("cljs.core","halt","cljs.core/halt",-1049036715);
cljs.core.cst$kw$mouseClicked = new cljs.core.Keyword(null,"mouseClicked","mouseClicked",1764302965);
cljs.core.cst$kw$square = new cljs.core.Keyword(null,"square","square",812434677);
cljs.core.cst$sym$cached_DASH_hierarchy = new cljs.core.Symbol(null,"cached-hierarchy","cached-hierarchy",-1085460203,null);
cljs.core.cst$kw$enable_DASH_optimized_DASH_stroke = new cljs.core.Keyword(null,"enable-optimized-stroke","enable-optimized-stroke",1537575253);
cljs.core.cst$sym$s = new cljs.core.Symbol(null,"s","s",-948495851,null);
cljs.core.cst$kw$parents = new cljs.core.Keyword(null,"parents","parents",-2027538891);
cljs.core.cst$sym$cnt = new cljs.core.Symbol(null,"cnt","cnt",1924510325,null);
cljs.core.cst$sym$js_SLASH_p5$prototype = new cljs.core.Symbol("js","p5.prototype","js/p5.prototype",-1153964427,null);
cljs.core.cst$kw$p_DASH_x = new cljs.core.Keyword(null,"p-x","p-x",-1721211211);
cljs.core.cst$sym$cljs$core_SLASH_nil_QMARK_ = new cljs.core.Symbol("cljs.core","nil?","cljs.core/nil?",945071861,null);
cljs.core.cst$kw$p2d = new cljs.core.Keyword(null,"p2d","p2d",-2106175755);
cljs.core.cst$kw$keep_DASH_on_DASH_top = new cljs.core.Keyword(null,"keep-on-top","keep-on-top",-970284267);
cljs.core.cst$kw$navigation_DASH_3d = new cljs.core.Keyword(null,"navigation-3d","navigation-3d",682305301);
cljs.core.cst$sym$node = new cljs.core.Symbol(null,"node","node",-2073234571,null);
cljs.core.cst$kw$mouseReleased = new cljs.core.Keyword(null,"mouseReleased","mouseReleased",1116234838);
cljs.core.cst$kw$mousePressed = new cljs.core.Keyword(null,"mousePressed","mousePressed",1776186454);
cljs.core.cst$sym$sym = new cljs.core.Symbol(null,"sym","sym",195671222,null);
cljs.core.cst$kw$mouseMoved = new cljs.core.Keyword(null,"mouseMoved","mouseMoved",-1936954058);
cljs.core.cst$kw$f12 = new cljs.core.Keyword(null,"f12","f12",853352790);
cljs.core.cst$sym$sb = new cljs.core.Symbol(null,"sb","sb",-1249746442,null);
cljs.core.cst$kw$mouseOver = new cljs.core.Keyword(null,"mouseOver","mouseOver",-1334461930);
cljs.core.cst$kw$exclusion = new cljs.core.Keyword(null,"exclusion","exclusion",531897910);
cljs.core.cst$sym$quil$core_SLASH_fill = new cljs.core.Symbol("quil.core","fill","quil.core/fill",814613078,null);
cljs.core.cst$sym$seed = new cljs.core.Symbol(null,"seed","seed",1709144854,null);
cljs.core.cst$kw$current_DASH_stroke = new cljs.core.Keyword(null,"current-stroke","current-stroke",-1338415274);
cljs.core.cst$kw$end_DASH_line = new cljs.core.Keyword(null,"end-line","end-line",1837326455);
cljs.core.cst$kw$disable_DASH_opengl_DASH_errors = new cljs.core.Keyword(null,"disable-opengl-errors","disable-opengl-errors",506822839);
cljs.core.cst$kw$unknown_DASH_key = new cljs.core.Keyword(null,"unknown-key","unknown-key",255305911);
cljs.core.cst$kw$alpha = new cljs.core.Keyword(null,"alpha","alpha",-1574982441);
cljs.core.cst$sym$prev_DASH_seed = new cljs.core.Symbol(null,"prev-seed","prev-seed",2126381367,null);
cljs.core.cst$kw$wait = new cljs.core.Keyword(null,"wait","wait",-260664777);
cljs.core.cst$kw$right = new cljs.core.Keyword(null,"right","right",-452581833);
cljs.core.cst$sym$quil$core_SLASH_current_DASH_stroke = new cljs.core.Symbol("quil.core","current-stroke","quil.core/current-stroke",-1148124489,null);
cljs.core.cst$kw$host_DASH_id = new cljs.core.Keyword(null,"host-id","host-id",742376279);
cljs.core.cst$kw$repeat = new cljs.core.Keyword(null,"repeat","repeat",832692087);
cljs.core.cst$kw$hard_DASH_light = new cljs.core.Keyword(null,"hard-light","hard-light",-37591145);
cljs.core.cst$sym$quil$sketch$macros_SLASH_defsketch = new cljs.core.Symbol("quil.sketch$macros","defsketch","quil.sketch$macros/defsketch",2065609719,null);
cljs.core.cst$kw$keyTyped = new cljs.core.Keyword(null,"keyTyped","keyTyped",1437329399);
cljs.core.cst$kw$position = new cljs.core.Keyword(null,"position","position",-2011731912);
cljs.core.cst$kw$no_DASH_start = new cljs.core.Keyword(null,"no-start","no-start",1381488856);
cljs.core.cst$sym$_hash = new cljs.core.Symbol(null,"_hash","_hash",-2130838312,null);
cljs.core.cst$kw$image = new cljs.core.Keyword(null,"image","image",-58725096);
cljs.core.cst$kw$d = new cljs.core.Keyword(null,"d","d",1972142424);
cljs.core.cst$kw$disable_DASH_async_DASH_saveframe = new cljs.core.Keyword(null,"disable-async-saveframe","disable-async-saveframe",-1346138728);
cljs.core.cst$kw$multiply = new cljs.core.Keyword(null,"multiply","multiply",-1036907048);
cljs.core.cst$kw$lightest = new cljs.core.Keyword(null,"lightest","lightest",-2043115912);
cljs.core.cst$sym$quil$core_SLASH_rotate = new cljs.core.Symbol("quil.core","rotate","quil.core/rotate",-1944995048,null);
cljs.core.cst$kw$f7 = new cljs.core.Keyword(null,"f7","f7",356150168);
cljs.core.cst$sym$keys = new cljs.core.Symbol(null,"keys","keys",-1586012071,null);
cljs.core.cst$kw$x = new cljs.core.Keyword(null,"x","x",2099068185);
cljs.core.cst$kw$blend = new cljs.core.Keyword(null,"blend","blend",249565561);
cljs.core.cst$sym$set_BANG_ = new cljs.core.Symbol(null,"set!","set!",250714521,null);
cljs.core.cst$kw$disable_DASH_depth_DASH_sort = new cljs.core.Keyword(null,"disable-depth-sort","disable-depth-sort",-1568352839);
cljs.core.cst$sym$quil$core_SLASH_begin_DASH_draw = new cljs.core.Symbol("quil.core","begin-draw","quil.core/begin-draw",454448665,null);
cljs.core.cst$kw$tag = new cljs.core.Keyword(null,"tag","tag",-1290361223);
cljs.core.cst$sym$tree_DASH_map = new cljs.core.Symbol(null,"tree-map","tree-map",1373073049,null);
cljs.core.cst$kw$raw_DASH_key = new cljs.core.Keyword(null,"raw-key","raw-key",-162482279);
cljs.core.cst$kw$target = new cljs.core.Keyword(null,"target","target",253001721);
cljs.core.cst$sym$_DOT_ = new cljs.core.Symbol(null,".",".",1975675962,null);
cljs.core.cst$sym$var = new cljs.core.Symbol(null,"var","var",870848730,null);
cljs.core.cst$kw$mutable = new cljs.core.Keyword(null,"mutable","mutable",875778266);
cljs.core.cst$sym$quote = new cljs.core.Symbol(null,"quote","quote",1377916282,null);
cljs.core.cst$kw$f9 = new cljs.core.Keyword(null,"f9","f9",704633338);
cljs.core.cst$sym$root = new cljs.core.Symbol(null,"root","root",1191874074,null);
cljs.core.cst$sym$multi = new cljs.core.Symbol(null,"multi","multi",1450238522,null);
cljs.core.cst$sym$str = new cljs.core.Symbol(null,"str","str",-1564826950,null);
cljs.core.cst$sym$next = new cljs.core.Symbol(null,"next","next",1522830042,null);
cljs.core.cst$kw$draw = new cljs.core.Keyword(null,"draw","draw",1358331674);
cljs.core.cst$sym$nodes = new cljs.core.Symbol(null,"nodes","nodes",-459054278,null);
cljs.core.cst$sym$seen = new cljs.core.Symbol(null,"seen","seen",1121531738,null);
cljs.core.cst$sym$hash_DASH_map = new cljs.core.Symbol(null,"hash-map","hash-map",-439030950,null);
cljs.core.cst$kw$erode = new cljs.core.Keyword(null,"erode","erode",1539530618);
cljs.core.cst$kw$add = new cljs.core.Keyword(null,"add","add",235287739);
cljs.core.cst$sym$catch = new cljs.core.Symbol(null,"catch","catch",-1616370245,null);
cljs.core.cst$sym$return_DASH_val__5956__auto__ = new cljs.core.Symbol(null,"return-val__5956__auto__","return-val__5956__auto__",-813190725,null);
cljs.core.cst$kw$soft_DASH_light = new cljs.core.Keyword(null,"soft-light","soft-light",513207899);
cljs.core.cst$kw$alt_DASH_impl = new cljs.core.Keyword(null,"alt-impl","alt-impl",670969595);
cljs.core.cst$sym$ext_DASH_map_DASH_iter = new cljs.core.Symbol(null,"ext-map-iter","ext-map-iter",-1215982757,null);
cljs.core.cst$sym$tail = new cljs.core.Symbol(null,"tail","tail",494507963,null);
cljs.core.cst$sym$quil$core_SLASH_with_DASH_graphics = new cljs.core.Symbol("quil.core","with-graphics","quil.core/with-graphics",481277883,null);
cljs.core.cst$kw$subtract = new cljs.core.Keyword(null,"subtract","subtract",2136988635);
cljs.core.cst$kw$clamp = new cljs.core.Keyword(null,"clamp","clamp",1803814940);
cljs.core.cst$sym$record = new cljs.core.Symbol(null,"record","record",861424668,null);
cljs.core.cst$sym$meta3617 = new cljs.core.Symbol(null,"meta3617","meta3617",-1967821732,null);
cljs.core.cst$kw$italic = new cljs.core.Keyword(null,"italic","italic",32599196);
cljs.core.cst$sym$mseq = new cljs.core.Symbol(null,"mseq","mseq",1602647196,null);
cljs.core.cst$sym$count = new cljs.core.Symbol(null,"count","count",-514511684,null);
cljs.core.cst$kw$last_DASH_time = new cljs.core.Keyword(null,"last-time","last-time",-1707132740);
cljs.core.cst$kw$radians = new cljs.core.Keyword(null,"radians","radians",1835725084);
cljs.core.cst$kw$degrees = new cljs.core.Keyword(null,"degrees","degrees",2015169884);
cljs.core.cst$sym$previous_DASH_stroke__5955__auto__ = new cljs.core.Symbol(null,"previous-stroke__5955__auto__","previous-stroke__5955__auto__",1882972700,null);
cljs.core.cst$kw$f6 = new cljs.core.Keyword(null,"f6","f6",2103080604);
cljs.core.cst$kw$keywordize_DASH_keys = new cljs.core.Keyword(null,"keywordize-keys","keywordize-keys",1310784252);
cljs.core.cst$sym$current = new cljs.core.Symbol(null,"current","current",552492924,null);
cljs.core.cst$sym$off = new cljs.core.Symbol(null,"off","off",-2047994980,null);
cljs.core.cst$kw$clj = new cljs.core.Keyword(null,"clj","clj",-660495428);
cljs.core.cst$kw$f4 = new cljs.core.Keyword(null,"f4","f4",990968764);
cljs.core.cst$kw$triangle_DASH_strip = new cljs.core.Keyword(null,"triangle-strip","triangle-strip",221845500);
cljs.core.cst$kw$navigation_DASH_2d = new cljs.core.Keyword(null,"navigation-2d","navigation-2d",-1924168611);
cljs.core.cst$sym$stack = new cljs.core.Symbol(null,"stack","stack",847125597,null);
cljs.core.cst$sym$transient_DASH_map = new cljs.core.Symbol(null,"transient-map","transient-map",351764893,null);
cljs.core.cst$sym$prev = new cljs.core.Symbol(null,"prev","prev",43462301,null);
cljs.core.cst$sym$len = new cljs.core.Symbol(null,"len","len",-1230778691,null);
cljs.core.cst$sym$return_DASH_val__5945__auto__ = new cljs.core.Symbol(null,"return-val__5945__auto__","return-val__5945__auto__",-1144998083,null);
cljs.core.cst$kw$cross = new cljs.core.Keyword(null,"cross","cross",194557789);
cljs.core.cst$sym$right = new cljs.core.Symbol(null,"right","right",1187949694,null);
cljs.core.cst$sym$throw = new cljs.core.Symbol(null,"throw","throw",595905694,null);
cljs.core.cst$kw$p3d = new cljs.core.Keyword(null,"p3d","p3d",-850380194);
cljs.core.cst$sym$fseq = new cljs.core.Symbol(null,"fseq","fseq",-1466412450,null);
cljs.core.cst$kw$a = new cljs.core.Keyword(null,"a","a",-2123407586);
cljs.core.cst$sym$chunk_DASH_next = new cljs.core.Symbol(null,"chunk-next","chunk-next",-547810434,null);
cljs.core.cst$kw$triangles = new cljs.core.Keyword(null,"triangles","triangles",-1525417058);
cljs.core.cst$kw$mouse_DASH_dragged = new cljs.core.Keyword(null,"mouse-dragged","mouse-dragged",-1220073441);
cljs.core.cst$sym$cljs$core_SLASH_aget = new cljs.core.Symbol("cljs.core","aget","cljs.core/aget",6345791,null);
cljs.core.cst$kw$left = new cljs.core.Keyword(null,"left","left",-399115937);
cljs.core.cst$kw$cljs$core_SLASH_not_DASH_found = new cljs.core.Keyword("cljs.core","not-found","cljs.core/not-found",-1572889185);
cljs.core.cst$sym$more = new cljs.core.Symbol(null,"more","more",-418290273,null);
cljs.core.cst$sym$def = new cljs.core.Symbol(null,"def","def",597100991,null);
cljs.core.cst$kw$invert = new cljs.core.Keyword(null,"invert","invert",1553577503);
cljs.core.cst$kw$text = new cljs.core.Keyword(null,"text","text",-1790561697);
cljs.core.cst$kw$enable_DASH_texture_DASH_mipmaps = new cljs.core.Keyword(null,"enable-texture-mipmaps","enable-texture-mipmaps",1241892671);
cljs.core.cst$kw$model = new cljs.core.Keyword(null,"model","model",331153215);
cljs.core.cst$sym$f = new cljs.core.Symbol(null,"f","f",43394975,null);
cljs.core.cst$sym$next_DASH_iter = new cljs.core.Symbol(null,"next-iter","next-iter",1526626239,null);

View File

@ -0,0 +1 @@
{:output-wrapper true, :externs ["closure-js/externs"], :libs ["closure-js/libs"], :main "curlnoise.core", :output-to "resources/public/js/main.js", :output-dir "resources/public/js/optimized", :asset-path "js/optimized", :optimizations :advanced}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,289 @@
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.string
(:refer-clojure :exclude [replace reverse])
(:require [goog.string :as gstring])
(:import [goog.string StringBuffer]))
(defn- seq-reverse
[coll]
(reduce conj () coll))
(def ^:private re-surrogate-pair
(js/RegExp. "([\\uD800-\\uDBFF])([\\uDC00-\\uDFFF])" "g"))
(defn reverse
"Returns s with its characters reversed."
[s]
(-> (.replace s re-surrogate-pair "$2$1")
(.. (split "") (reverse) (join ""))))
(defn- replace-all
[s re replacement]
(let [r (js/RegExp. (.-source re)
(cond-> "g"
(.-ignoreCase re) (str "i")
(.-multiline re) (str "m")
(.-unicode re) (str "u")))]
(.replace s r replacement)))
(defn- replace-with
[f]
(fn [& args]
(let [matches (drop-last 2 args)]
(if (= (count matches) 1)
(f (first matches))
(f (vec matches))))))
(defn replace
"Replaces all instance of match with replacement in s.
match/replacement can be:
string / string
pattern / (string or function of match).
See also replace-first.
The replacement is literal (i.e. none of its characters are treated
specially) for all cases above except pattern / string.
For pattern / string, $1, $2, etc. in the replacement string are
substituted with the string that matched the corresponding
parenthesized group in the pattern.
Example:
(clojure.string/replace \"Almost Pig Latin\" #\"\\b(\\w)(\\w+)\\b\" \"$2$1ay\")
-> \"lmostAay igPay atinLay\""
[s match replacement]
(cond
(string? match)
(.replace s (js/RegExp. (gstring/regExpEscape match) "g") replacement)
(instance? js/RegExp match)
(if (string? replacement)
(replace-all s match replacement)
(replace-all s match (replace-with replacement)))
:else (throw (str "Invalid match arg: " match))))
(defn replace-first
"Replaces the first instance of match with replacement in s.
match/replacement can be:
string / string
pattern / (string or function of match).
See also replace.
The replacement is literal (i.e. none of its characters are treated
specially) for all cases above except pattern / string.
For pattern / string, $1, $2, etc. in the replacement string are
substituted with the string that matched the corresponding
parenthesized group in the pattern.
Example:
(clojure.string/replace-first \"swap first two words\"
#\"(\\w+)(\\s+)(\\w+)\" \"$3$2$1\")
-> \"first swap two words\""
[s match replacement]
(.replace s match replacement))
(defn join
"Returns a string of all elements in coll, as returned by (seq coll),
separated by an optional separator."
([coll]
(loop [sb (StringBuffer.) coll (seq coll)]
(if-not (nil? coll)
(recur (. sb (append (str (first coll)))) (next coll))
(.toString sb))))
([separator coll]
(loop [sb (StringBuffer.) coll (seq coll)]
(if-not (nil? coll)
(do
(. sb (append (str (first coll))))
(let [coll (next coll)]
(when-not (nil? coll)
(. sb (append separator)))
(recur sb coll)))
(.toString sb)))))
(defn upper-case
"Converts string to all upper-case."
[s]
(.toUpperCase s))
(defn lower-case
"Converts string to all lower-case."
[s]
(.toLowerCase s))
(defn capitalize
"Converts first character of the string to upper-case, all other
characters to lower-case."
[s]
(gstring/capitalize s))
;; The JavaScript split function takes a limit argument but the return
;; value is not the same as the Java split function.
;;
;; Java: (.split "a-b-c" #"-" 2) => ["a" "b-c"]
;; JavaScript: (.split "a-b-c" #"-" 2) => ["a" "b"]
;;
;; For consistency, the three arg version has been implemented to
;; mimic Java's behavior.
(defn- pop-last-while-empty
[v]
(loop [v v]
(if (identical? "" (peek v))
(recur (pop v))
v)))
(defn- discard-trailing-if-needed
[limit v]
(if (and (== 0 limit) (< 1 (count v)))
(pop-last-while-empty v)
v))
(defn- split-with-empty-regex
[s limit]
(if (or (<= limit 0) (>= limit (+ 2 (count s))))
(conj (vec (cons "" (map str (seq s)))) "")
(condp == limit
1 (vector s)
2 (vector "" s)
(let [c (- limit 2)]
(conj (vec (cons "" (subvec (vec (map str (seq s))) 0 c))) (subs s c))))))
(defn split
"Splits string on a regular expression. Optional argument limit is
the maximum number of splits. Not lazy. Returns vector of the splits."
([s re]
(split s re 0))
([s re limit]
(discard-trailing-if-needed limit
(if (identical? "/(?:)/" (str re))
(split-with-empty-regex s limit)
(if (< limit 1)
(vec (.split (str s) re))
(loop [s s
limit limit
parts []]
(if (== 1 limit)
(conj parts s)
(let [m (re-find re s)]
(if-not (nil? m)
(let [index (.indexOf s m)]
(recur (.substring s (+ index (count m)))
(dec limit)
(conj parts (.substring s 0 index))))
(conj parts s))))))))))
(defn split-lines
"Splits s on \\n or \\r\\n."
[s]
(split s #"\n|\r\n"))
(defn trim
"Removes whitespace from both ends of string."
[s]
(gstring/trim s))
(defn triml
"Removes whitespace from the left side of string."
[s]
(gstring/trimLeft s))
(defn trimr
"Removes whitespace from the right side of string."
[s]
(gstring/trimRight s))
(defn trim-newline
"Removes all trailing newline \\n or return \\r characters from
string. Similar to Perl's chomp."
[s]
(loop [index (.-length s)]
(if (zero? index)
""
(let [ch (get s (dec index))]
(if (or (identical? \newline ch)
(identical? \return ch))
(recur (dec index))
(.substring s 0 index))))))
(defn ^boolean blank?
"True is s is nil, empty, or contains only whitespace."
[s]
(gstring/isEmptySafe s))
(defn escape
"Return a new string, using cmap to escape each character ch
from s as follows:
If (cmap ch) is nil, append ch to the new string.
If (cmap ch) is non-nil, append (str (cmap ch)) instead."
[s cmap]
(let [buffer (StringBuffer.)
length (.-length s)]
(loop [index 0]
(if (== length index)
(. buffer (toString))
(let [ch (.charAt s index)
replacement (get cmap ch)]
(if-not (nil? replacement)
(.append buffer (str replacement))
(.append buffer ch))
(recur (inc index)))))))
(defn index-of
"Return index of value (string or char) in s, optionally searching
forward from from-index or nil if not found."
([s value]
(let [result (.indexOf s value)]
(if (neg? result)
nil
result)))
([s value from-index]
(let [result (.indexOf s value from-index)]
(if (neg? result)
nil
result))))
(defn last-index-of
"Return last index of value (string or char) in s, optionally
searching backward from from-index or nil if not found."
([s value]
(let [result (.lastIndexOf s value)]
(if (neg? result)
nil
result)))
([s value from-index]
(let [result (.lastIndexOf s value from-index)]
(if (neg? result)
nil
result))))
(defn ^boolean starts-with?
"True if s starts with substr."
[s substr]
(gstring/startsWith s substr))
(defn ^boolean ends-with?
"True if s ends with substr."
[s substr]
(gstring/endsWith s substr))
(defn ^boolean includes?
"True if s includes substr."
[s substr]
(gstring/contains s substr))

View File

@ -0,0 +1,478 @@
// Compiled by ClojureScript 1.10.520 {:static-fns true, :optimize-constants true}
goog.provide('clojure.string');
goog.require('cljs.core');
goog.require('cljs.core.constants');
goog.require('goog.string');
goog.require('goog.string.StringBuffer');
clojure.string.seq_reverse = (function clojure$string$seq_reverse(coll){
return cljs.core.reduce.cljs$core$IFn$_invoke$arity$3(cljs.core.conj,cljs.core.List.EMPTY,coll);
});
clojure.string.re_surrogate_pair = (new RegExp("([\\uD800-\\uDBFF])([\\uDC00-\\uDFFF])","g"));
/**
* Returns s with its characters reversed.
*/
clojure.string.reverse = (function clojure$string$reverse(s){
return s.replace(clojure.string.re_surrogate_pair,"$2$1").split("").reverse().join("");
});
clojure.string.replace_all = (function clojure$string$replace_all(s,re,replacement){
var r = (new RegExp(re.source,(function (){var G__5537 = "g";
var G__5537__$1 = (cljs.core.truth_(re.ignoreCase)?[G__5537,"i"].join(''):G__5537);
var G__5537__$2 = (cljs.core.truth_(re.multiline)?[G__5537__$1,"m"].join(''):G__5537__$1);
if(cljs.core.truth_(re.unicode)){
return [G__5537__$2,"u"].join('');
} else {
return G__5537__$2;
}
})()));
return s.replace(r,replacement);
});
clojure.string.replace_with = (function clojure$string$replace_with(f){
return (function() {
var G__5540__delegate = function (args){
var matches = cljs.core.drop_last.cljs$core$IFn$_invoke$arity$2((2),args);
if(cljs.core._EQ_.cljs$core$IFn$_invoke$arity$2(cljs.core.count(matches),(1))){
var G__5538 = cljs.core.first(matches);
return (f.cljs$core$IFn$_invoke$arity$1 ? f.cljs$core$IFn$_invoke$arity$1(G__5538) : f.call(null,G__5538));
} else {
var G__5539 = cljs.core.vec(matches);
return (f.cljs$core$IFn$_invoke$arity$1 ? f.cljs$core$IFn$_invoke$arity$1(G__5539) : f.call(null,G__5539));
}
};
var G__5540 = function (var_args){
var args = null;
if (arguments.length > 0) {
var G__5541__i = 0, G__5541__a = new Array(arguments.length - 0);
while (G__5541__i < G__5541__a.length) {G__5541__a[G__5541__i] = arguments[G__5541__i + 0]; ++G__5541__i;}
args = new cljs.core.IndexedSeq(G__5541__a,0,null);
}
return G__5540__delegate.call(this,args);};
G__5540.cljs$lang$maxFixedArity = 0;
G__5540.cljs$lang$applyTo = (function (arglist__5542){
var args = cljs.core.seq(arglist__5542);
return G__5540__delegate(args);
});
G__5540.cljs$core$IFn$_invoke$arity$variadic = G__5540__delegate;
return G__5540;
})()
;
});
/**
* Replaces all instance of match with replacement in s.
*
* match/replacement can be:
*
* string / string
* pattern / (string or function of match).
*
* See also replace-first.
*
* The replacement is literal (i.e. none of its characters are treated
* specially) for all cases above except pattern / string.
*
* For pattern / string, $1, $2, etc. in the replacement string are
* substituted with the string that matched the corresponding
* parenthesized group in the pattern.
*
* Example:
* (clojure.string/replace "Almost Pig Latin" #"\b(\w)(\w+)\b" "$2$1ay")
* -> "lmostAay igPay atinLay"
*/
clojure.string.replace = (function clojure$string$replace(s,match,replacement){
if(typeof match === 'string'){
return s.replace((new RegExp(goog.string.regExpEscape(match),"g")),replacement);
} else {
if((match instanceof RegExp)){
if(typeof replacement === 'string'){
return clojure.string.replace_all(s,match,replacement);
} else {
return clojure.string.replace_all(s,match,clojure.string.replace_with(replacement));
}
} else {
throw ["Invalid match arg: ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(match)].join('');
}
}
});
/**
* Replaces the first instance of match with replacement in s.
*
* match/replacement can be:
*
* string / string
* pattern / (string or function of match).
*
* See also replace.
*
* The replacement is literal (i.e. none of its characters are treated
* specially) for all cases above except pattern / string.
*
* For pattern / string, $1, $2, etc. in the replacement string are
* substituted with the string that matched the corresponding
* parenthesized group in the pattern.
*
* Example:
* (clojure.string/replace-first "swap first two words"
* #"(\w+)(\s+)(\w+)" "$3$2$1")
* -> "first swap two words"
*/
clojure.string.replace_first = (function clojure$string$replace_first(s,match,replacement){
return s.replace(match,replacement);
});
/**
* Returns a string of all elements in coll, as returned by (seq coll),
* separated by an optional separator.
*/
clojure.string.join = (function clojure$string$join(var_args){
var G__5544 = arguments.length;
switch (G__5544) {
case 1:
return clojure.string.join.cljs$core$IFn$_invoke$arity$1((arguments[(0)]));
break;
case 2:
return clojure.string.join.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('')));
}
});
clojure.string.join.cljs$core$IFn$_invoke$arity$1 = (function (coll){
var sb = (new goog.string.StringBuffer());
var coll__$1 = cljs.core.seq(coll);
while(true){
if((!((coll__$1 == null)))){
var G__5546 = sb.append(cljs.core.str.cljs$core$IFn$_invoke$arity$1(cljs.core.first(coll__$1)));
var G__5547 = cljs.core.next(coll__$1);
sb = G__5546;
coll__$1 = G__5547;
continue;
} else {
return sb.toString();
}
break;
}
});
clojure.string.join.cljs$core$IFn$_invoke$arity$2 = (function (separator,coll){
var sb = (new goog.string.StringBuffer());
var coll__$1 = cljs.core.seq(coll);
while(true){
if((!((coll__$1 == null)))){
sb.append(cljs.core.str.cljs$core$IFn$_invoke$arity$1(cljs.core.first(coll__$1)));
var coll__$2 = cljs.core.next(coll__$1);
if((coll__$2 == null)){
} else {
sb.append(separator);
}
var G__5548 = sb;
var G__5549 = coll__$2;
sb = G__5548;
coll__$1 = G__5549;
continue;
} else {
return sb.toString();
}
break;
}
});
clojure.string.join.cljs$lang$maxFixedArity = 2;
/**
* Converts string to all upper-case.
*/
clojure.string.upper_case = (function clojure$string$upper_case(s){
return s.toUpperCase();
});
/**
* Converts string to all lower-case.
*/
clojure.string.lower_case = (function clojure$string$lower_case(s){
return s.toLowerCase();
});
/**
* Converts first character of the string to upper-case, all other
* characters to lower-case.
*/
clojure.string.capitalize = (function clojure$string$capitalize(s){
return goog.string.capitalize(s);
});
clojure.string.pop_last_while_empty = (function clojure$string$pop_last_while_empty(v){
var v__$1 = v;
while(true){
if(("" === cljs.core.peek(v__$1))){
var G__5550 = cljs.core.pop(v__$1);
v__$1 = G__5550;
continue;
} else {
return v__$1;
}
break;
}
});
clojure.string.discard_trailing_if_needed = (function clojure$string$discard_trailing_if_needed(limit,v){
if(((((0) === limit)) && (((1) < cljs.core.count(v))))){
return clojure.string.pop_last_while_empty(v);
} else {
return v;
}
});
clojure.string.split_with_empty_regex = (function clojure$string$split_with_empty_regex(s,limit){
if((((limit <= (0))) || ((limit >= ((2) + cljs.core.count(s)))))){
return cljs.core.conj.cljs$core$IFn$_invoke$arity$2(cljs.core.vec(cljs.core.cons("",cljs.core.map.cljs$core$IFn$_invoke$arity$2(cljs.core.str,cljs.core.seq(s)))),"");
} else {
var pred__5551 = cljs.core._EQ__EQ_;
var expr__5552 = limit;
if(cljs.core.truth_((pred__5551.cljs$core$IFn$_invoke$arity$2 ? pred__5551.cljs$core$IFn$_invoke$arity$2((1),expr__5552) : pred__5551.call(null,(1),expr__5552)))){
return (new cljs.core.PersistentVector(null,1,(5),cljs.core.PersistentVector.EMPTY_NODE,[s],null));
} else {
if(cljs.core.truth_((pred__5551.cljs$core$IFn$_invoke$arity$2 ? pred__5551.cljs$core$IFn$_invoke$arity$2((2),expr__5552) : pred__5551.call(null,(2),expr__5552)))){
return (new cljs.core.PersistentVector(null,2,(5),cljs.core.PersistentVector.EMPTY_NODE,["",s],null));
} else {
var c = (limit - (2));
return cljs.core.conj.cljs$core$IFn$_invoke$arity$2(cljs.core.vec(cljs.core.cons("",cljs.core.subvec.cljs$core$IFn$_invoke$arity$3(cljs.core.vec(cljs.core.map.cljs$core$IFn$_invoke$arity$2(cljs.core.str,cljs.core.seq(s))),(0),c))),cljs.core.subs.cljs$core$IFn$_invoke$arity$2(s,c));
}
}
}
});
/**
* Splits string on a regular expression. Optional argument limit is
* the maximum number of splits. Not lazy. Returns vector of the splits.
*/
clojure.string.split = (function clojure$string$split(var_args){
var G__5555 = arguments.length;
switch (G__5555) {
case 2:
return clojure.string.split.cljs$core$IFn$_invoke$arity$2((arguments[(0)]),(arguments[(1)]));
break;
case 3:
return clojure.string.split.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('')));
}
});
clojure.string.split.cljs$core$IFn$_invoke$arity$2 = (function (s,re){
return clojure.string.split.cljs$core$IFn$_invoke$arity$3(s,re,(0));
});
clojure.string.split.cljs$core$IFn$_invoke$arity$3 = (function (s,re,limit){
return clojure.string.discard_trailing_if_needed(limit,((("/(?:)/" === cljs.core.str.cljs$core$IFn$_invoke$arity$1(re)))?clojure.string.split_with_empty_regex(s,limit):(((limit < (1)))?cljs.core.vec(cljs.core.str.cljs$core$IFn$_invoke$arity$1(s).split(re)):(function (){var s__$1 = s;
var limit__$1 = limit;
var parts = cljs.core.PersistentVector.EMPTY;
while(true){
if(((1) === limit__$1)){
return cljs.core.conj.cljs$core$IFn$_invoke$arity$2(parts,s__$1);
} else {
var m = cljs.core.re_find(re,s__$1);
if((!((m == null)))){
var index = s__$1.indexOf(m);
var G__5557 = s__$1.substring((index + cljs.core.count(m)));
var G__5558 = (limit__$1 - (1));
var G__5559 = cljs.core.conj.cljs$core$IFn$_invoke$arity$2(parts,s__$1.substring((0),index));
s__$1 = G__5557;
limit__$1 = G__5558;
parts = G__5559;
continue;
} else {
return cljs.core.conj.cljs$core$IFn$_invoke$arity$2(parts,s__$1);
}
}
break;
}
})())));
});
clojure.string.split.cljs$lang$maxFixedArity = 3;
/**
* Splits s on \n or \r\n.
*/
clojure.string.split_lines = (function clojure$string$split_lines(s){
return clojure.string.split.cljs$core$IFn$_invoke$arity$2(s,/\n|\r\n/);
});
/**
* Removes whitespace from both ends of string.
*/
clojure.string.trim = (function clojure$string$trim(s){
return goog.string.trim(s);
});
/**
* Removes whitespace from the left side of string.
*/
clojure.string.triml = (function clojure$string$triml(s){
return goog.string.trimLeft(s);
});
/**
* Removes whitespace from the right side of string.
*/
clojure.string.trimr = (function clojure$string$trimr(s){
return goog.string.trimRight(s);
});
/**
* Removes all trailing newline \n or return \r characters from
* string. Similar to Perl's chomp.
*/
clojure.string.trim_newline = (function clojure$string$trim_newline(s){
var index = s.length;
while(true){
if((index === (0))){
return "";
} else {
var ch = cljs.core.get.cljs$core$IFn$_invoke$arity$2(s,(index - (1)));
if(((("\n" === ch)) || (("\r" === ch)))){
var G__5560 = (index - (1));
index = G__5560;
continue;
} else {
return s.substring((0),index);
}
}
break;
}
});
/**
* True is s is nil, empty, or contains only whitespace.
*/
clojure.string.blank_QMARK_ = (function clojure$string$blank_QMARK_(s){
return goog.string.isEmptySafe(s);
});
/**
* Return a new string, using cmap to escape each character ch
* from s as follows:
*
* If (cmap ch) is nil, append ch to the new string.
* If (cmap ch) is non-nil, append (str (cmap ch)) instead.
*/
clojure.string.escape = (function clojure$string$escape(s,cmap){
var buffer = (new goog.string.StringBuffer());
var length = s.length;
var index = (0);
while(true){
if((length === index)){
return buffer.toString();
} else {
var ch = s.charAt(index);
var replacement = cljs.core.get.cljs$core$IFn$_invoke$arity$2(cmap,ch);
if((!((replacement == null)))){
buffer.append(cljs.core.str.cljs$core$IFn$_invoke$arity$1(replacement));
} else {
buffer.append(ch);
}
var G__5561 = (index + (1));
index = G__5561;
continue;
}
break;
}
});
/**
* Return index of value (string or char) in s, optionally searching
* forward from from-index or nil if not found.
*/
clojure.string.index_of = (function clojure$string$index_of(var_args){
var G__5563 = arguments.length;
switch (G__5563) {
case 2:
return clojure.string.index_of.cljs$core$IFn$_invoke$arity$2((arguments[(0)]),(arguments[(1)]));
break;
case 3:
return clojure.string.index_of.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('')));
}
});
clojure.string.index_of.cljs$core$IFn$_invoke$arity$2 = (function (s,value){
var result = s.indexOf(value);
if((result < (0))){
return null;
} else {
return result;
}
});
clojure.string.index_of.cljs$core$IFn$_invoke$arity$3 = (function (s,value,from_index){
var result = s.indexOf(value,from_index);
if((result < (0))){
return null;
} else {
return result;
}
});
clojure.string.index_of.cljs$lang$maxFixedArity = 3;
/**
* Return last index of value (string or char) in s, optionally
* searching backward from from-index or nil if not found.
*/
clojure.string.last_index_of = (function clojure$string$last_index_of(var_args){
var G__5566 = arguments.length;
switch (G__5566) {
case 2:
return clojure.string.last_index_of.cljs$core$IFn$_invoke$arity$2((arguments[(0)]),(arguments[(1)]));
break;
case 3:
return clojure.string.last_index_of.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('')));
}
});
clojure.string.last_index_of.cljs$core$IFn$_invoke$arity$2 = (function (s,value){
var result = s.lastIndexOf(value);
if((result < (0))){
return null;
} else {
return result;
}
});
clojure.string.last_index_of.cljs$core$IFn$_invoke$arity$3 = (function (s,value,from_index){
var result = s.lastIndexOf(value,from_index);
if((result < (0))){
return null;
} else {
return result;
}
});
clojure.string.last_index_of.cljs$lang$maxFixedArity = 3;
/**
* True if s starts with substr.
*/
clojure.string.starts_with_QMARK_ = (function clojure$string$starts_with_QMARK_(s,substr){
return goog.string.startsWith(s,substr);
});
/**
* True if s ends with substr.
*/
clojure.string.ends_with_QMARK_ = (function clojure$string$ends_with_QMARK_(s,substr){
return goog.string.endsWith(s,substr);
});
/**
* True if s includes substr.
*/
clojure.string.includes_QMARK_ = (function clojure$string$includes_QMARK_(s,substr){
return goog.string.contains(s,substr);
});

View File

@ -0,0 +1,560 @@
// Compiled by ClojureScript 1.10.520 {:static-fns true, :optimize-constants true}
goog.provide('curlnoise.core');
goog.require('cljs.core');
goog.require('cljs.core.constants');
goog.require('quil.core');
goog.require('quil.middleware');
curlnoise.core.framerate = (60);
curlnoise.core.res_x = (500);
curlnoise.core.res_y = curlnoise.core.res_x;
curlnoise.core.grid_size = (10);
curlnoise.core.alpha = (40);
curlnoise.core.renderer = cljs.core.cst$kw$p2d;
curlnoise.core.grid = (function curlnoise$core$grid(nx,ny){
return cljs.core.mapcat.cljs$core$IFn$_invoke$arity$variadic((function (x){
return cljs.core.map.cljs$core$IFn$_invoke$arity$2((function (y){
return new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, [x,y], null);
}),cljs.core.range.cljs$core$IFn$_invoke$arity$1(ny));
}),cljs.core.prim_seq.cljs$core$IFn$_invoke$arity$2([cljs.core.range.cljs$core$IFn$_invoke$arity$1(nx)], 0));
});
curlnoise.core.pix_grid = (function curlnoise$core$pix_grid(grid_size,res_x,res_y){
var nx = ((res_x / grid_size) | (0));
var ny = ((res_y / grid_size) | (0));
var offset = ((grid_size / (2)) | (0));
var x2pix = ((function (nx,ny,offset){
return (function (p1__6098_SHARP_){
return (offset + quil.core.map_range(p1__6098_SHARP_,(0),nx,(0),res_x));
});})(nx,ny,offset))
;
var y2pix = ((function (nx,ny,offset,x2pix){
return (function (p1__6099_SHARP_){
return (offset + quil.core.map_range(p1__6099_SHARP_,(0),ny,(0),res_y));
});})(nx,ny,offset,x2pix))
;
return cljs.core.map.cljs$core$IFn$_invoke$arity$2(((function (nx,ny,offset,x2pix,y2pix){
return (function (p__6100){
var vec__6101 = p__6100;
var i = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6101,(0),null);
var j = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6101,(1),null);
return new cljs.core.PersistentVector(null, 4, 5, cljs.core.PersistentVector.EMPTY_NODE, [i,j,x2pix(i),y2pix(j)], null);
});})(nx,ny,offset,x2pix,y2pix))
,curlnoise.core.grid(nx,ny));
});
curlnoise.core.ramp = (function curlnoise$core$ramp(r){
if((r >= 1.0)){
return 1.0;
} else {
if((r <= -1.0)){
return -1.0;
} else {
var c1 = (15.0 / 8.0);
var c3 = (-10.0 / 8.0);
var c5 = (3.0 / 8.0);
return (((r * c1) + (Math.pow(r,(3)) * c3)) + (Math.pow(r,(5)) * c5));
}
}
});
curlnoise.core.dist2 = (function curlnoise$core$dist2(x,y){
return ((x * x) + (y * y));
});
curlnoise.core.dist = (function curlnoise$core$dist(x,y){
var G__6104 = curlnoise.core.dist2(x,y);
return Math.sqrt(G__6104);
});
curlnoise.core.clamp = (function curlnoise$core$clamp(v,v0,v1){
if((v < v0)){
return v0;
} else {
if((v > v1)){
return v1;
} else {
return v;
}
}
});
curlnoise.core.setup = (function curlnoise$core$setup(){
quil.core.background.cljs$core$IFn$_invoke$arity$1((255));
quil.core.frame_rate(curlnoise.core.framerate);
var gr = quil.core.create_graphics.cljs$core$IFn$_invoke$arity$2(curlnoise.core.res_x,curlnoise.core.res_y);
var gr__575__auto___6107 = gr;
var _STAR_graphics_STAR__orig_val__6105_6108 = quil.core._STAR_graphics_STAR_;
var _STAR_graphics_STAR__temp_val__6106_6109 = gr__575__auto___6107;
quil.core._STAR_graphics_STAR_ = _STAR_graphics_STAR__temp_val__6106_6109;
try{quil.core.begin_draw(gr__575__auto___6107);
quil.core.background.cljs$core$IFn$_invoke$arity$2((255),curlnoise.core.alpha);
quil.core.end_draw(gr__575__auto___6107);
}finally {quil.core._STAR_graphics_STAR_ = _STAR_graphics_STAR__orig_val__6105_6108;
}
return new cljs.core.PersistentArrayMap(null, 3, [cljs.core.cst$kw$frame,(0),cljs.core.cst$kw$grid,curlnoise.core.pix_grid(curlnoise.core.grid_size,curlnoise.core.res_x,curlnoise.core.res_y),cljs.core.cst$kw$blend,gr], null);
});
curlnoise.core.sdf_box = (function curlnoise$core$sdf_box(px,py,bx,by){
var bx2 = (0.5 * bx);
var by2 = (0.5 * by);
var dx = ((function (){var G__6110 = (px - bx2);
return Math.abs(G__6110);
})() - bx2);
var dy = ((function (){var G__6111 = (py - by2);
return Math.abs(G__6111);
})() - by2);
var l = (curlnoise.core.dist((function (){var x__4219__auto__ = 0.0;
var y__4220__auto__ = dx;
return ((x__4219__auto__ > y__4220__auto__) ? x__4219__auto__ : y__4220__auto__);
})(),(function (){var x__4219__auto__ = 0.0;
var y__4220__auto__ = dy;
return ((x__4219__auto__ > y__4220__auto__) ? x__4219__auto__ : y__4220__auto__);
})()) + (function (){var x__4222__auto__ = (function (){var x__4219__auto__ = dx;
var y__4220__auto__ = dy;
return ((x__4219__auto__ > y__4220__auto__) ? x__4219__auto__ : y__4220__auto__);
})();
var y__4223__auto__ = 0.0;
return ((x__4222__auto__ < y__4223__auto__) ? x__4222__auto__ : y__4223__auto__);
})());
return l;
});
curlnoise.core.update_state = (function curlnoise$core$update_state(state){
var w = quil.core.width();
var h = quil.core.height();
var vf = 0.1;
var scale = 400.0;
var noise_scale = (scale * 10.0);
var rad = 20.0;
var rect_rad = 100.0;
var margin = (0);
var eps = (w * 0.001);
var mx = quil.core.mouse_x();
var my = quil.core.mouse_y();
var f_inv = ((1) / scale);
var d0 = 150.0;
var d_mouse = ((function (w,h,vf,scale,noise_scale,rad,rect_rad,margin,eps,mx,my,f_inv,d0){
return (function (p1__6112_SHARP_,p2__6113_SHARP_){
if(cljs.core.truth_(quil.core.mouse_pressed_QMARK_())){
return (curlnoise.core.dist((mx - p1__6112_SHARP_),(my - p2__6113_SHARP_)) - rad);
} else {
return 1000000.0;
}
});})(w,h,vf,scale,noise_scale,rad,rect_rad,margin,eps,mx,my,f_inv,d0))
;
var d_border = ((function (w,h,vf,scale,noise_scale,rad,rect_rad,margin,eps,mx,my,f_inv,d0,d_mouse){
return (function (p1__6114_SHARP_,p2__6115_SHARP_){
return (rect_rad - curlnoise.core.sdf_box((p1__6114_SHARP_ - rect_rad),(p2__6115_SHARP_ - rect_rad),(w - (rect_rad * (2))),(h - (rect_rad * (2)))));
});})(w,h,vf,scale,noise_scale,rad,rect_rad,margin,eps,mx,my,f_inv,d0,d_mouse))
;
var amp_fn = ((function (w,h,vf,scale,noise_scale,rad,rect_rad,margin,eps,mx,my,f_inv,d0,d_mouse,d_border){
return (function (_,___$1){
return 1.0;
});})(w,h,vf,scale,noise_scale,rad,rect_rad,margin,eps,mx,my,f_inv,d0,d_mouse,d_border))
;
var mouse_drift = ((function (w,h,vf,scale,noise_scale,rad,rect_rad,margin,eps,mx,my,f_inv,d0,d_mouse,d_border,amp_fn){
return (function (p1__6117_SHARP_,p2__6116_SHARP_){
if(cljs.core.truth_(quil.core.mouse_pressed_QMARK_())){
return (((((mx / w) - 0.5) * p2__6116_SHARP_) * 0.01) + ((((my / h) - 0.5) * p1__6117_SHARP_) * -0.01));
} else {
return 0.0;
}
});})(w,h,vf,scale,noise_scale,rad,rect_rad,margin,eps,mx,my,f_inv,d0,d_mouse,d_border,amp_fn))
;
var n_fn = ((function (w,h,vf,scale,noise_scale,rad,rect_rad,margin,eps,mx,my,f_inv,d0,d_mouse,d_border,amp_fn,mouse_drift){
return (function (p1__6118_SHARP_,p2__6119_SHARP_,p3__6120_SHARP_){
return (noise_scale * (mouse_drift(p1__6118_SHARP_,p2__6119_SHARP_) + quil.core.noise.cljs$core$IFn$_invoke$arity$3((f_inv * p1__6118_SHARP_),(f_inv * p2__6119_SHARP_),(f_inv * p3__6120_SHARP_))));
});})(w,h,vf,scale,noise_scale,rad,rect_rad,margin,eps,mx,my,f_inv,d0,d_mouse,d_border,amp_fn,mouse_drift))
;
var p_fn = ((function (w,h,vf,scale,noise_scale,rad,rect_rad,margin,eps,mx,my,f_inv,d0,d_mouse,d_border,amp_fn,mouse_drift,n_fn){
return (function (p1__6121_SHARP_,p2__6122_SHARP_,p3__6123_SHARP_){
return ((vf * amp_fn(p1__6121_SHARP_,p2__6122_SHARP_)) * n_fn(p1__6121_SHARP_,p2__6122_SHARP_,p3__6123_SHARP_));
});})(w,h,vf,scale,noise_scale,rad,rect_rad,margin,eps,mx,my,f_inv,d0,d_mouse,d_border,amp_fn,mouse_drift,n_fn))
;
var points = cljs.core.map.cljs$core$IFn$_invoke$arity$2(((function (w,h,vf,scale,noise_scale,rad,rect_rad,margin,eps,mx,my,f_inv,d0,d_mouse,d_border,amp_fn,mouse_drift,n_fn,p_fn){
return (function (pt){
var vec__6124 = pt;
var i = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6124,(0),null);
var j = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6124,(1),null);
var x = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6124,(2),null);
var y = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6124,(3),null);
var z = (cljs.core.cst$kw$frame.cljs$core$IFn$_invoke$arity$1(state) / 20.0);
var border = (((((((x > margin)) && ((x < (w - margin))))) && ((((y > margin)) && ((x < (h - margin)))))))?1.0:0.0);
var n = p_fn(x,y,z);
var n_dx = p_fn((x + eps),y,z);
var n_dy = p_fn(x,(y + eps),z);
var vx = ((n_dy - n) / eps);
var vy = ((n - n_dx) / eps);
var x2 = (x + vx);
var y2 = (y + vy);
var vec__6127 = (((((x2 < (0))) || ((x2 > w)) || ((y2 < (0))) || ((y2 > h))))?new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, [quil.core.random.cljs$core$IFn$_invoke$arity$1(w),quil.core.random.cljs$core$IFn$_invoke$arity$1(h)], null):new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, [x2,y2], null));
var x3 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6127,(0),null);
var y3 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6127,(1),null);
return new cljs.core.PersistentVector(null, 4, 5, cljs.core.PersistentVector.EMPTY_NODE, [i,j,x3,y3], null);
});})(w,h,vf,scale,noise_scale,rad,rect_rad,margin,eps,mx,my,f_inv,d0,d_mouse,d_border,amp_fn,mouse_drift,n_fn,p_fn))
,cljs.core.cst$kw$grid.cljs$core$IFn$_invoke$arity$1(state));
return cljs.core.assoc.cljs$core$IFn$_invoke$arity$3(cljs.core.update.cljs$core$IFn$_invoke$arity$3(state,cljs.core.cst$kw$frame,cljs.core.inc),cljs.core.cst$kw$grid,points);
});
curlnoise.core.show_fn = false;
curlnoise.core.draw_field = (function curlnoise$core$draw_field(offset,sdf,domain_xform){
var pix = quil.core.pixels.cljs$core$IFn$_invoke$arity$0();
var w = quil.core.width();
var h = quil.core.height();
var seq__6130_6170 = cljs.core.seq(curlnoise.core.grid(quil.core.width(),quil.core.height()));
var chunk__6131_6171 = null;
var count__6132_6172 = (0);
var i__6133_6173 = (0);
while(true){
if((i__6133_6173 < count__6132_6172)){
var point_6174 = chunk__6131_6171.cljs$core$IIndexed$_nth$arity$2(null,i__6133_6173);
var vec__6152_6175 = point_6174;
var px_6176 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6152_6175,(0),null);
var py_6177 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6152_6175,(1),null);
var vec__6155_6178 = (domain_xform.cljs$core$IFn$_invoke$arity$2 ? domain_xform.cljs$core$IFn$_invoke$arity$2(px_6176,py_6177) : domain_xform.call(null,px_6176,py_6177));
var px2_6179 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6155_6178,(0),null);
var py2_6180 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6155_6178,(1),null);
var w2_6181 = w;
var h2_6182 = h;
var d_6183 = (sdf.cljs$core$IFn$_invoke$arity$2 ? sdf.cljs$core$IFn$_invoke$arity$2(px2_6179,py2_6180) : sdf.call(null,px2_6179,py2_6180));
var step_6184 = (cljs.core.mod(d_6183,offset) / offset);
var val_6185 = ((step_6184 * (255)) | (0));
var vec__6158_6186 = (((Math.abs(d_6183) >= (offset * 0.5)))?new cljs.core.PersistentVector(null, 3, 5, cljs.core.PersistentVector.EMPTY_NODE, [val_6185,val_6185,val_6185], null):new cljs.core.PersistentVector(null, 3, 5, cljs.core.PersistentVector.EMPTY_NODE, [val_6185,0.0,0.0], null));
var r_6187 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6158_6186,(0),null);
var g_6188 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6158_6186,(1),null);
var b_6189 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6158_6186,(2),null);
var offset_6190__$1 = ((4) * (px_6176 + (py_6177 * w)));
(pix[offset_6190__$1] = r_6187);
(pix[(offset_6190__$1 + (1))] = g_6188);
(pix[(offset_6190__$1 + (2))] = b_6189);
(pix[(offset_6190__$1 + (3))] = (255));
var G__6191 = seq__6130_6170;
var G__6192 = chunk__6131_6171;
var G__6193 = count__6132_6172;
var G__6194 = (i__6133_6173 + (1));
seq__6130_6170 = G__6191;
chunk__6131_6171 = G__6192;
count__6132_6172 = G__6193;
i__6133_6173 = G__6194;
continue;
} else {
var temp__5735__auto___6195 = cljs.core.seq(seq__6130_6170);
if(temp__5735__auto___6195){
var seq__6130_6196__$1 = temp__5735__auto___6195;
if(cljs.core.chunked_seq_QMARK_(seq__6130_6196__$1)){
var c__4550__auto___6197 = cljs.core.chunk_first(seq__6130_6196__$1);
var G__6198 = cljs.core.chunk_rest(seq__6130_6196__$1);
var G__6199 = c__4550__auto___6197;
var G__6200 = cljs.core.count(c__4550__auto___6197);
var G__6201 = (0);
seq__6130_6170 = G__6198;
chunk__6131_6171 = G__6199;
count__6132_6172 = G__6200;
i__6133_6173 = G__6201;
continue;
} else {
var point_6202 = cljs.core.first(seq__6130_6196__$1);
var vec__6161_6203 = point_6202;
var px_6204 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6161_6203,(0),null);
var py_6205 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6161_6203,(1),null);
var vec__6164_6206 = (domain_xform.cljs$core$IFn$_invoke$arity$2 ? domain_xform.cljs$core$IFn$_invoke$arity$2(px_6204,py_6205) : domain_xform.call(null,px_6204,py_6205));
var px2_6207 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6164_6206,(0),null);
var py2_6208 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6164_6206,(1),null);
var w2_6209 = w;
var h2_6210 = h;
var d_6211 = (sdf.cljs$core$IFn$_invoke$arity$2 ? sdf.cljs$core$IFn$_invoke$arity$2(px2_6207,py2_6208) : sdf.call(null,px2_6207,py2_6208));
var step_6212 = (cljs.core.mod(d_6211,offset) / offset);
var val_6213 = ((step_6212 * (255)) | (0));
var vec__6167_6214 = (((Math.abs(d_6211) >= (offset * 0.5)))?new cljs.core.PersistentVector(null, 3, 5, cljs.core.PersistentVector.EMPTY_NODE, [val_6213,val_6213,val_6213], null):new cljs.core.PersistentVector(null, 3, 5, cljs.core.PersistentVector.EMPTY_NODE, [val_6213,0.0,0.0], null));
var r_6215 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6167_6214,(0),null);
var g_6216 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6167_6214,(1),null);
var b_6217 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6167_6214,(2),null);
var offset_6218__$1 = ((4) * (px_6204 + (py_6205 * w)));
(pix[offset_6218__$1] = r_6215);
(pix[(offset_6218__$1 + (1))] = g_6216);
(pix[(offset_6218__$1 + (2))] = b_6217);
(pix[(offset_6218__$1 + (3))] = (255));
var G__6219 = cljs.core.next(seq__6130_6196__$1);
var G__6220 = null;
var G__6221 = (0);
var G__6222 = (0);
seq__6130_6170 = G__6219;
chunk__6131_6171 = G__6220;
count__6132_6172 = G__6221;
i__6133_6173 = G__6222;
continue;
}
} else {
}
}
break;
}
return quil.core.update_pixels.cljs$core$IFn$_invoke$arity$0();
});
curlnoise.core.draw_state = (function curlnoise$core$draw_state(state){
quil.core.image.cljs$core$IFn$_invoke$arity$3(cljs.core.cst$kw$blend.cljs$core$IFn$_invoke$arity$1(state),(0),(0));
var pix = quil.core.pixels.cljs$core$IFn$_invoke$arity$0();
var w = quil.core.width();
var h = quil.core.height();
var color = quil.core.color.cljs$core$IFn$_invoke$arity$1((0));
var rad = (50);
if(curlnoise.core.show_fn){
curlnoise.core.draw_field(10.0,((function (pix,w,h,color,rad){
return (function (p1__6223_SHARP_,p2__6224_SHARP_){
return (rad - curlnoise.core.sdf_box(p1__6223_SHARP_,p2__6224_SHARP_,(quil.core.width() - (rad * (2))),(quil.core.height() - (rad * (2)))));
});})(pix,w,h,color,rad))
,((function (pix,w,h,color,rad){
return (function (p1__6225_SHARP_,p2__6226_SHARP_){
return cljs.core.vec(new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, [((p1__6225_SHARP_ - rad) - 0.0),((p2__6226_SHARP_ - rad) - 0.0)], null));
});})(pix,w,h,color,rad))
);
} else {
var seq__6227_6243 = cljs.core.seq(cljs.core.cst$kw$grid.cljs$core$IFn$_invoke$arity$1(state));
var chunk__6228_6244 = null;
var count__6229_6245 = (0);
var i__6230_6246 = (0);
while(true){
if((i__6230_6246 < count__6229_6245)){
var point_6247 = chunk__6228_6244.cljs$core$IIndexed$_nth$arity$2(null,i__6230_6246);
var vec__6237_6248 = point_6247;
var __6249 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6237_6248,(0),null);
var __6250__$1 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6237_6248,(1),null);
var px_6251 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6237_6248,(2),null);
var py_6252 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6237_6248,(3),null);
var ix_6253 = curlnoise.core.clamp((px_6251 | (0)),(0),(w - (1)));
var iy_6254 = curlnoise.core.clamp((py_6252 | (0)),(0),(h - (1)));
var offset_6255 = ((4) * (ix_6253 + (iy_6254 * w)));
(pix[offset_6255] = (0));
(pix[(offset_6255 + (1))] = (0));
(pix[(offset_6255 + (2))] = (0));
(pix[(offset_6255 + (3))] = (255));
var G__6256 = seq__6227_6243;
var G__6257 = chunk__6228_6244;
var G__6258 = count__6229_6245;
var G__6259 = (i__6230_6246 + (1));
seq__6227_6243 = G__6256;
chunk__6228_6244 = G__6257;
count__6229_6245 = G__6258;
i__6230_6246 = G__6259;
continue;
} else {
var temp__5735__auto___6260 = cljs.core.seq(seq__6227_6243);
if(temp__5735__auto___6260){
var seq__6227_6261__$1 = temp__5735__auto___6260;
if(cljs.core.chunked_seq_QMARK_(seq__6227_6261__$1)){
var c__4550__auto___6262 = cljs.core.chunk_first(seq__6227_6261__$1);
var G__6263 = cljs.core.chunk_rest(seq__6227_6261__$1);
var G__6264 = c__4550__auto___6262;
var G__6265 = cljs.core.count(c__4550__auto___6262);
var G__6266 = (0);
seq__6227_6243 = G__6263;
chunk__6228_6244 = G__6264;
count__6229_6245 = G__6265;
i__6230_6246 = G__6266;
continue;
} else {
var point_6267 = cljs.core.first(seq__6227_6261__$1);
var vec__6240_6268 = point_6267;
var __6269 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6240_6268,(0),null);
var __6270__$1 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6240_6268,(1),null);
var px_6271 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6240_6268,(2),null);
var py_6272 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6240_6268,(3),null);
var ix_6273 = curlnoise.core.clamp((px_6271 | (0)),(0),(w - (1)));
var iy_6274 = curlnoise.core.clamp((py_6272 | (0)),(0),(h - (1)));
var offset_6275 = ((4) * (ix_6273 + (iy_6274 * w)));
(pix[offset_6275] = (0));
(pix[(offset_6275 + (1))] = (0));
(pix[(offset_6275 + (2))] = (0));
(pix[(offset_6275 + (3))] = (255));
var G__6276 = cljs.core.next(seq__6227_6261__$1);
var G__6277 = null;
var G__6278 = (0);
var G__6279 = (0);
seq__6227_6243 = G__6276;
chunk__6228_6244 = G__6277;
count__6229_6245 = G__6278;
i__6230_6246 = G__6279;
continue;
}
} else {
}
}
break;
}
}
return quil.core.update_pixels.cljs$core$IFn$_invoke$arity$0();
});
curlnoise.core.settings = (function curlnoise$core$settings(){
return quil.core.pixel_density((1));
});
curlnoise.core.run_sketch = (function curlnoise$core$run_sketch(){
curlnoise.core.curlnoise = (function curlnoise$core$run_sketch_$_curlnoise(){
return quil.sketch.sketch.cljs$core$IFn$_invoke$arity$variadic(cljs.core.prim_seq.cljs$core$IFn$_invoke$arity$2([cljs.core.cst$kw$features,new cljs.core.PersistentVector(null, 1, 5, cljs.core.PersistentVector.EMPTY_NODE, [cljs.core.cst$kw$keep_DASH_on_DASH_top], null),cljs.core.cst$kw$settings,((cljs.core.fn_QMARK_(curlnoise.core.settings))?(function() {
var G__6280__delegate = function (args){
return cljs.core.apply.cljs$core$IFn$_invoke$arity$2(curlnoise.core.settings,args);
};
var G__6280 = function (var_args){
var args = null;
if (arguments.length > 0) {
var G__6281__i = 0, G__6281__a = new Array(arguments.length - 0);
while (G__6281__i < G__6281__a.length) {G__6281__a[G__6281__i] = arguments[G__6281__i + 0]; ++G__6281__i;}
args = new cljs.core.IndexedSeq(G__6281__a,0,null);
}
return G__6280__delegate.call(this,args);};
G__6280.cljs$lang$maxFixedArity = 0;
G__6280.cljs$lang$applyTo = (function (arglist__6282){
var args = cljs.core.seq(arglist__6282);
return G__6280__delegate(args);
});
G__6280.cljs$core$IFn$_invoke$arity$variadic = G__6280__delegate;
return G__6280;
})()
:curlnoise.core.settings),cljs.core.cst$kw$update,((cljs.core.fn_QMARK_(curlnoise.core.update_state))?(function() {
var G__6283__delegate = function (args){
return cljs.core.apply.cljs$core$IFn$_invoke$arity$2(curlnoise.core.update_state,args);
};
var G__6283 = function (var_args){
var args = null;
if (arguments.length > 0) {
var G__6284__i = 0, G__6284__a = new Array(arguments.length - 0);
while (G__6284__i < G__6284__a.length) {G__6284__a[G__6284__i] = arguments[G__6284__i + 0]; ++G__6284__i;}
args = new cljs.core.IndexedSeq(G__6284__a,0,null);
}
return G__6283__delegate.call(this,args);};
G__6283.cljs$lang$maxFixedArity = 0;
G__6283.cljs$lang$applyTo = (function (arglist__6285){
var args = cljs.core.seq(arglist__6285);
return G__6283__delegate(args);
});
G__6283.cljs$core$IFn$_invoke$arity$variadic = G__6283__delegate;
return G__6283;
})()
:curlnoise.core.update_state),cljs.core.cst$kw$renderer,((cljs.core.fn_QMARK_(curlnoise.core.renderer))?(function() {
var G__6286__delegate = function (args){
return cljs.core.apply.cljs$core$IFn$_invoke$arity$2(curlnoise.core.renderer,args);
};
var G__6286 = function (var_args){
var args = null;
if (arguments.length > 0) {
var G__6287__i = 0, G__6287__a = new Array(arguments.length - 0);
while (G__6287__i < G__6287__a.length) {G__6287__a[G__6287__i] = arguments[G__6287__i + 0]; ++G__6287__i;}
args = new cljs.core.IndexedSeq(G__6287__a,0,null);
}
return G__6286__delegate.call(this,args);};
G__6286.cljs$lang$maxFixedArity = 0;
G__6286.cljs$lang$applyTo = (function (arglist__6288){
var args = cljs.core.seq(arglist__6288);
return G__6286__delegate(args);
});
G__6286.cljs$core$IFn$_invoke$arity$variadic = G__6286__delegate;
return G__6286;
})()
:curlnoise.core.renderer),cljs.core.cst$kw$size,new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, [curlnoise.core.res_x,curlnoise.core.res_y], null),cljs.core.cst$kw$title,"Curl Noise",cljs.core.cst$kw$setup,((cljs.core.fn_QMARK_(curlnoise.core.setup))?(function() {
var G__6289__delegate = function (args){
return cljs.core.apply.cljs$core$IFn$_invoke$arity$2(curlnoise.core.setup,args);
};
var G__6289 = function (var_args){
var args = null;
if (arguments.length > 0) {
var G__6290__i = 0, G__6290__a = new Array(arguments.length - 0);
while (G__6290__i < G__6290__a.length) {G__6290__a[G__6290__i] = arguments[G__6290__i + 0]; ++G__6290__i;}
args = new cljs.core.IndexedSeq(G__6290__a,0,null);
}
return G__6289__delegate.call(this,args);};
G__6289.cljs$lang$maxFixedArity = 0;
G__6289.cljs$lang$applyTo = (function (arglist__6291){
var args = cljs.core.seq(arglist__6291);
return G__6289__delegate(args);
});
G__6289.cljs$core$IFn$_invoke$arity$variadic = G__6289__delegate;
return G__6289;
})()
:curlnoise.core.setup),cljs.core.cst$kw$middleware,new cljs.core.PersistentVector(null, 1, 5, cljs.core.PersistentVector.EMPTY_NODE, [quil.middleware.fun_mode], null),cljs.core.cst$kw$host,"curlnoise",cljs.core.cst$kw$draw,((cljs.core.fn_QMARK_(curlnoise.core.draw_state))?(function() {
var G__6292__delegate = function (args){
return cljs.core.apply.cljs$core$IFn$_invoke$arity$2(curlnoise.core.draw_state,args);
};
var G__6292 = function (var_args){
var args = null;
if (arguments.length > 0) {
var G__6293__i = 0, G__6293__a = new Array(arguments.length - 0);
while (G__6293__i < G__6293__a.length) {G__6293__a[G__6293__i] = arguments[G__6293__i + 0]; ++G__6293__i;}
args = new cljs.core.IndexedSeq(G__6293__a,0,null);
}
return G__6292__delegate.call(this,args);};
G__6292.cljs$lang$maxFixedArity = 0;
G__6292.cljs$lang$applyTo = (function (arglist__6294){
var args = cljs.core.seq(arglist__6294);
return G__6292__delegate(args);
});
G__6292.cljs$core$IFn$_invoke$arity$variadic = G__6292__delegate;
return G__6292;
})()
:curlnoise.core.draw_state)], 0));
});
goog.exportSymbol('curlnoise.core.curlnoise', curlnoise.core.curlnoise);
if(cljs.core.truth_(cljs.core.some((function (p1__201__202__auto__){
return cljs.core._EQ_.cljs$core$IFn$_invoke$arity$2(cljs.core.cst$kw$no_DASH_start,p1__201__202__auto__);
}),new cljs.core.PersistentVector(null, 1, 5, cljs.core.PersistentVector.EMPTY_NODE, [cljs.core.cst$kw$keep_DASH_on_DASH_top], null)))){
return null;
} else {
return quil.sketch.add_sketch_to_init_list(new cljs.core.PersistentArrayMap(null, 2, [cljs.core.cst$kw$fn,curlnoise.core.curlnoise,cljs.core.cst$kw$host_DASH_id,"curlnoise"], null));
}
});
goog.exportSymbol('curlnoise.core.run_sketch', curlnoise.core.run_sketch);
curlnoise.core._main = (function curlnoise$core$_main(var_args){
var args__4736__auto__ = [];
var len__4730__auto___6296 = arguments.length;
var i__4731__auto___6297 = (0);
while(true){
if((i__4731__auto___6297 < len__4730__auto___6296)){
args__4736__auto__.push((arguments[i__4731__auto___6297]));
var G__6298 = (i__4731__auto___6297 + (1));
i__4731__auto___6297 = G__6298;
continue;
} else {
}
break;
}
var argseq__4737__auto__ = ((((0) < args__4736__auto__.length))?(new cljs.core.IndexedSeq(args__4736__auto__.slice((0)),(0),null)):null);
return curlnoise.core._main.cljs$core$IFn$_invoke$arity$variadic(argseq__4737__auto__);
});
curlnoise.core._main.cljs$core$IFn$_invoke$arity$variadic = (function (args){
return curlnoise.core.run_sketch();
});
curlnoise.core._main.cljs$lang$maxFixedArity = (0);
/** @this {Function} */
curlnoise.core._main.cljs$lang$applyTo = (function (seq6295){
var self__4718__auto__ = this;
return self__4718__auto__.cljs$core$IFn$_invoke$arity$variadic(cljs.core.seq(seq6295));
});

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,369 @@
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Utilities to check the preconditions, postconditions and
* invariants runtime.
*
* Methods in this package should be given special treatment by the compiler
* for type-inference. For example, <code>goog.asserts.assert(foo)</code>
* will restrict <code>foo</code> to a truthy value.
*
* The compiler has an option to disable asserts. So code like:
* <code>
* var x = goog.asserts.assert(foo()); goog.asserts.assert(bar());
* </code>
* will be transformed into:
* <code>
* var x = foo();
* </code>
* The compiler will leave in foo() (because its return value is used),
* but it will remove bar() because it assumes it does not have side-effects.
*
* @author agrieve@google.com (Andrew Grieve)
*/
goog.provide('goog.asserts');
goog.provide('goog.asserts.AssertionError');
goog.require('goog.debug.Error');
goog.require('goog.dom.NodeType');
goog.require('goog.string');
/**
* @define {boolean} Whether to strip out asserts or to leave them in.
*/
goog.define('goog.asserts.ENABLE_ASSERTS', goog.DEBUG);
/**
* Error object for failed assertions.
* @param {string} messagePattern The pattern that was used to form message.
* @param {!Array<*>} messageArgs The items to substitute into the pattern.
* @constructor
* @extends {goog.debug.Error}
* @final
*/
goog.asserts.AssertionError = function(messagePattern, messageArgs) {
messageArgs.unshift(messagePattern);
goog.debug.Error.call(this, goog.string.subs.apply(null, messageArgs));
// Remove the messagePattern afterwards to avoid permanently modifying the
// passed in array.
messageArgs.shift();
/**
* The message pattern used to format the error message. Error handlers can
* use this to uniquely identify the assertion.
* @type {string}
*/
this.messagePattern = messagePattern;
};
goog.inherits(goog.asserts.AssertionError, goog.debug.Error);
/** @override */
goog.asserts.AssertionError.prototype.name = 'AssertionError';
/**
* The default error handler.
* @param {!goog.asserts.AssertionError} e The exception to be handled.
*/
goog.asserts.DEFAULT_ERROR_HANDLER = function(e) {
throw e;
};
/**
* The handler responsible for throwing or logging assertion errors.
* @private {function(!goog.asserts.AssertionError)}
*/
goog.asserts.errorHandler_ = goog.asserts.DEFAULT_ERROR_HANDLER;
/**
* Throws an exception with the given message and "Assertion failed" prefixed
* onto it.
* @param {string} defaultMessage The message to use if givenMessage is empty.
* @param {Array<*>} defaultArgs The substitution arguments for defaultMessage.
* @param {string|undefined} givenMessage Message supplied by the caller.
* @param {Array<*>} givenArgs The substitution arguments for givenMessage.
* @throws {goog.asserts.AssertionError} When the value is not a number.
* @private
*/
goog.asserts.doAssertFailure_ = function(
defaultMessage, defaultArgs, givenMessage, givenArgs) {
var message = 'Assertion failed';
if (givenMessage) {
message += ': ' + givenMessage;
var args = givenArgs;
} else if (defaultMessage) {
message += ': ' + defaultMessage;
args = defaultArgs;
}
// The '' + works around an Opera 10 bug in the unit tests. Without it,
// a stack trace is added to var message above. With this, a stack trace is
// not added until this line (it causes the extra garbage to be added after
// the assertion message instead of in the middle of it).
var e = new goog.asserts.AssertionError('' + message, args || []);
goog.asserts.errorHandler_(e);
};
/**
* Sets a custom error handler that can be used to customize the behavior of
* assertion failures, for example by turning all assertion failures into log
* messages.
* @param {function(!goog.asserts.AssertionError)} errorHandler
*/
goog.asserts.setErrorHandler = function(errorHandler) {
if (goog.asserts.ENABLE_ASSERTS) {
goog.asserts.errorHandler_ = errorHandler;
}
};
/**
* Checks if the condition evaluates to true if goog.asserts.ENABLE_ASSERTS is
* true.
* @template T
* @param {T} condition The condition to check.
* @param {string=} opt_message Error message in case of failure.
* @param {...*} var_args The items to substitute into the failure message.
* @return {T} The value of the condition.
* @throws {goog.asserts.AssertionError} When the condition evaluates to false.
*/
goog.asserts.assert = function(condition, opt_message, var_args) {
if (goog.asserts.ENABLE_ASSERTS && !condition) {
goog.asserts.doAssertFailure_(
'', null, opt_message, Array.prototype.slice.call(arguments, 2));
}
return condition;
};
/**
* Fails if goog.asserts.ENABLE_ASSERTS is true. This function is useful in case
* when we want to add a check in the unreachable area like switch-case
* statement:
*
* <pre>
* switch(type) {
* case FOO: doSomething(); break;
* case BAR: doSomethingElse(); break;
* default: goog.asserts.fail('Unrecognized type: ' + type);
* // We have only 2 types - "default:" section is unreachable code.
* }
* </pre>
*
* @param {string=} opt_message Error message in case of failure.
* @param {...*} var_args The items to substitute into the failure message.
* @throws {goog.asserts.AssertionError} Failure.
*/
goog.asserts.fail = function(opt_message, var_args) {
if (goog.asserts.ENABLE_ASSERTS) {
goog.asserts.errorHandler_(
new goog.asserts.AssertionError(
'Failure' + (opt_message ? ': ' + opt_message : ''),
Array.prototype.slice.call(arguments, 1)));
}
};
/**
* Checks if the value is a number if goog.asserts.ENABLE_ASSERTS is true.
* @param {*} value The value to check.
* @param {string=} opt_message Error message in case of failure.
* @param {...*} var_args The items to substitute into the failure message.
* @return {number} The value, guaranteed to be a number when asserts enabled.
* @throws {goog.asserts.AssertionError} When the value is not a number.
*/
goog.asserts.assertNumber = function(value, opt_message, var_args) {
if (goog.asserts.ENABLE_ASSERTS && !goog.isNumber(value)) {
goog.asserts.doAssertFailure_(
'Expected number but got %s: %s.', [goog.typeOf(value), value],
opt_message, Array.prototype.slice.call(arguments, 2));
}
return /** @type {number} */ (value);
};
/**
* Checks if the value is a string if goog.asserts.ENABLE_ASSERTS is true.
* @param {*} value The value to check.
* @param {string=} opt_message Error message in case of failure.
* @param {...*} var_args The items to substitute into the failure message.
* @return {string} The value, guaranteed to be a string when asserts enabled.
* @throws {goog.asserts.AssertionError} When the value is not a string.
*/
goog.asserts.assertString = function(value, opt_message, var_args) {
if (goog.asserts.ENABLE_ASSERTS && !goog.isString(value)) {
goog.asserts.doAssertFailure_(
'Expected string but got %s: %s.', [goog.typeOf(value), value],
opt_message, Array.prototype.slice.call(arguments, 2));
}
return /** @type {string} */ (value);
};
/**
* Checks if the value is a function if goog.asserts.ENABLE_ASSERTS is true.
* @param {*} value The value to check.
* @param {string=} opt_message Error message in case of failure.
* @param {...*} var_args The items to substitute into the failure message.
* @return {!Function} The value, guaranteed to be a function when asserts
* enabled.
* @throws {goog.asserts.AssertionError} When the value is not a function.
*/
goog.asserts.assertFunction = function(value, opt_message, var_args) {
if (goog.asserts.ENABLE_ASSERTS && !goog.isFunction(value)) {
goog.asserts.doAssertFailure_(
'Expected function but got %s: %s.', [goog.typeOf(value), value],
opt_message, Array.prototype.slice.call(arguments, 2));
}
return /** @type {!Function} */ (value);
};
/**
* Checks if the value is an Object if goog.asserts.ENABLE_ASSERTS is true.
* @param {*} value The value to check.
* @param {string=} opt_message Error message in case of failure.
* @param {...*} var_args The items to substitute into the failure message.
* @return {!Object} The value, guaranteed to be a non-null object.
* @throws {goog.asserts.AssertionError} When the value is not an object.
*/
goog.asserts.assertObject = function(value, opt_message, var_args) {
if (goog.asserts.ENABLE_ASSERTS && !goog.isObject(value)) {
goog.asserts.doAssertFailure_(
'Expected object but got %s: %s.', [goog.typeOf(value), value],
opt_message, Array.prototype.slice.call(arguments, 2));
}
return /** @type {!Object} */ (value);
};
/**
* Checks if the value is an Array if goog.asserts.ENABLE_ASSERTS is true.
* @param {*} value The value to check.
* @param {string=} opt_message Error message in case of failure.
* @param {...*} var_args The items to substitute into the failure message.
* @return {!Array<?>} The value, guaranteed to be a non-null array.
* @throws {goog.asserts.AssertionError} When the value is not an array.
*/
goog.asserts.assertArray = function(value, opt_message, var_args) {
if (goog.asserts.ENABLE_ASSERTS && !goog.isArray(value)) {
goog.asserts.doAssertFailure_(
'Expected array but got %s: %s.', [goog.typeOf(value), value],
opt_message, Array.prototype.slice.call(arguments, 2));
}
return /** @type {!Array<?>} */ (value);
};
/**
* Checks if the value is a boolean if goog.asserts.ENABLE_ASSERTS is true.
* @param {*} value The value to check.
* @param {string=} opt_message Error message in case of failure.
* @param {...*} var_args The items to substitute into the failure message.
* @return {boolean} The value, guaranteed to be a boolean when asserts are
* enabled.
* @throws {goog.asserts.AssertionError} When the value is not a boolean.
*/
goog.asserts.assertBoolean = function(value, opt_message, var_args) {
if (goog.asserts.ENABLE_ASSERTS && !goog.isBoolean(value)) {
goog.asserts.doAssertFailure_(
'Expected boolean but got %s: %s.', [goog.typeOf(value), value],
opt_message, Array.prototype.slice.call(arguments, 2));
}
return /** @type {boolean} */ (value);
};
/**
* Checks if the value is a DOM Element if goog.asserts.ENABLE_ASSERTS is true.
* @param {*} value The value to check.
* @param {string=} opt_message Error message in case of failure.
* @param {...*} var_args The items to substitute into the failure message.
* @return {!Element} The value, likely to be a DOM Element when asserts are
* enabled.
* @throws {goog.asserts.AssertionError} When the value is not an Element.
*/
goog.asserts.assertElement = function(value, opt_message, var_args) {
if (goog.asserts.ENABLE_ASSERTS &&
(!goog.isObject(value) || value.nodeType != goog.dom.NodeType.ELEMENT)) {
goog.asserts.doAssertFailure_(
'Expected Element but got %s: %s.', [goog.typeOf(value), value],
opt_message, Array.prototype.slice.call(arguments, 2));
}
return /** @type {!Element} */ (value);
};
/**
* Checks if the value is an instance of the user-defined type if
* goog.asserts.ENABLE_ASSERTS is true.
*
* The compiler may tighten the type returned by this function.
*
* @param {?} value The value to check.
* @param {function(new: T, ...)} type A user-defined constructor.
* @param {string=} opt_message Error message in case of failure.
* @param {...*} var_args The items to substitute into the failure message.
* @throws {goog.asserts.AssertionError} When the value is not an instance of
* type.
* @return {T}
* @template T
*/
goog.asserts.assertInstanceof = function(value, type, opt_message, var_args) {
if (goog.asserts.ENABLE_ASSERTS && !(value instanceof type)) {
goog.asserts.doAssertFailure_(
'Expected instanceof %s but got %s.',
[goog.asserts.getType_(type), goog.asserts.getType_(value)],
opt_message, Array.prototype.slice.call(arguments, 3));
}
return value;
};
/**
* Checks that no enumerable keys are present in Object.prototype. Such keys
* would break most code that use {@code for (var ... in ...)} loops.
*/
goog.asserts.assertObjectPrototypeIsIntact = function() {
for (var key in Object.prototype) {
goog.asserts.fail(key + ' should not be enumerable in Object.prototype.');
}
};
/**
* Returns the type of a value. If a constructor is passed, and a suitable
* string cannot be found, 'unknown type name' will be returned.
* @param {*} value A constructor, object, or primitive.
* @return {string} The best display name for the value, or 'unknown type name'.
* @private
*/
goog.asserts.getType_ = function(value) {
if (value instanceof Function) {
return value.displayName || value.name || 'unknown type name';
} else if (value instanceof Object) {
return value.constructor.displayName || value.constructor.name ||
Object.prototype.toString.call(value);
} else {
return value === null ? 'null' : typeof value;
}
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,159 @@
// Copyright 2010 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A global registry for entry points into a program,
* so that they can be instrumented. Each module should register their
* entry points with this registry. Designed to be compiled out
* if no instrumentation is requested.
*
* Entry points may be registered before or after a call to
* goog.debug.entryPointRegistry.monitorAll. If an entry point is registered
* later, the existing monitor will instrument the new entry point.
*
* @author nicksantos@google.com (Nick Santos)
*/
goog.provide('goog.debug.EntryPointMonitor');
goog.provide('goog.debug.entryPointRegistry');
goog.require('goog.asserts');
/**
* @interface
*/
goog.debug.EntryPointMonitor = function() {};
/**
* Instruments a function.
*
* @param {!Function} fn A function to instrument.
* @return {!Function} The instrumented function.
*/
goog.debug.EntryPointMonitor.prototype.wrap;
/**
* Try to remove an instrumentation wrapper created by this monitor.
* If the function passed to unwrap is not a wrapper created by this
* monitor, then we will do nothing.
*
* Notice that some wrappers may not be unwrappable. For example, if other
* monitors have applied their own wrappers, then it will be impossible to
* unwrap them because their wrappers will have captured our wrapper.
*
* So it is important that entry points are unwrapped in the reverse
* order that they were wrapped.
*
* @param {!Function} fn A function to unwrap.
* @return {!Function} The unwrapped function, or {@code fn} if it was not
* a wrapped function created by this monitor.
*/
goog.debug.EntryPointMonitor.prototype.unwrap;
/**
* An array of entry point callbacks.
* @type {!Array<function(!Function)>}
* @private
*/
goog.debug.entryPointRegistry.refList_ = [];
/**
* Monitors that should wrap all the entry points.
* @type {!Array<!goog.debug.EntryPointMonitor>}
* @private
*/
goog.debug.entryPointRegistry.monitors_ = [];
/**
* Whether goog.debug.entryPointRegistry.monitorAll has ever been called.
* Checking this allows the compiler to optimize out the registrations.
* @type {boolean}
* @private
*/
goog.debug.entryPointRegistry.monitorsMayExist_ = false;
/**
* Register an entry point with this module.
*
* The entry point will be instrumented when a monitor is passed to
* goog.debug.entryPointRegistry.monitorAll. If this has already occurred, the
* entry point is instrumented immediately.
*
* @param {function(!Function)} callback A callback function which is called
* with a transforming function to instrument the entry point. The callback
* is responsible for wrapping the relevant entry point with the
* transforming function.
*/
goog.debug.entryPointRegistry.register = function(callback) {
// Don't use push(), so that this can be compiled out.
goog.debug.entryPointRegistry
.refList_[goog.debug.entryPointRegistry.refList_.length] = callback;
// If no one calls monitorAll, this can be compiled out.
if (goog.debug.entryPointRegistry.monitorsMayExist_) {
var monitors = goog.debug.entryPointRegistry.monitors_;
for (var i = 0; i < monitors.length; i++) {
callback(goog.bind(monitors[i].wrap, monitors[i]));
}
}
};
/**
* Configures a monitor to wrap all entry points.
*
* Entry points that have already been registered are immediately wrapped by
* the monitor. When an entry point is registered in the future, it will also
* be wrapped by the monitor when it is registered.
*
* @param {!goog.debug.EntryPointMonitor} monitor An entry point monitor.
*/
goog.debug.entryPointRegistry.monitorAll = function(monitor) {
goog.debug.entryPointRegistry.monitorsMayExist_ = true;
var transformer = goog.bind(monitor.wrap, monitor);
for (var i = 0; i < goog.debug.entryPointRegistry.refList_.length; i++) {
goog.debug.entryPointRegistry.refList_[i](transformer);
}
goog.debug.entryPointRegistry.monitors_.push(monitor);
};
/**
* Try to unmonitor all the entry points that have already been registered. If
* an entry point is registered in the future, it will not be wrapped by the
* monitor when it is registered. Note that this may fail if the entry points
* have additional wrapping.
*
* @param {!goog.debug.EntryPointMonitor} monitor The last monitor to wrap
* the entry points.
* @throws {Error} If the monitor is not the most recently configured monitor.
*/
goog.debug.entryPointRegistry.unmonitorAllIfPossible = function(monitor) {
var monitors = goog.debug.entryPointRegistry.monitors_;
goog.asserts.assert(
monitor == monitors[monitors.length - 1],
'Only the most recent monitor can be unwrapped.');
var transformer = goog.bind(monitor.unwrap, monitor);
for (var i = 0; i < goog.debug.entryPointRegistry.refList_.length; i++) {
goog.debug.entryPointRegistry.refList_[i](transformer);
}
monitors.length--;
};

View File

@ -0,0 +1,63 @@
// Copyright 2009 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Provides a base class for custom Error objects such that the
* stack is correctly maintained.
*
* You should never need to throw goog.debug.Error(msg) directly, Error(msg) is
* sufficient.
*
*/
goog.provide('goog.debug.Error');
/**
* Base class for custom error objects.
* @param {*=} opt_msg The message associated with the error.
* @constructor
* @extends {Error}
*/
goog.debug.Error = function(opt_msg) {
// Attempt to ensure there is a stack trace.
if (Error.captureStackTrace) {
Error.captureStackTrace(this, goog.debug.Error);
} else {
var stack = new Error().stack;
if (stack) {
this.stack = stack;
}
}
if (opt_msg) {
this.message = String(opt_msg);
}
/**
* Whether to report this error to the server. Setting this to false will
* cause the error reporter to not report the error back to the server,
* which can be useful if the client knows that the error has already been
* logged on the server.
* @type {boolean}
*/
this.reportErrorToServer = true;
};
goog.inherits(goog.debug.Error, Error);
/** @override */
goog.debug.Error.prototype.name = 'CustomError';

View File

@ -0,0 +1,305 @@
// Copyright 2005 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Implements the disposable interface. The dispose method is used
* to clean up references and resources.
* @author arv@google.com (Erik Arvidsson)
*/
goog.provide('goog.Disposable');
goog.provide('goog.dispose');
goog.provide('goog.disposeAll');
goog.require('goog.disposable.IDisposable');
/**
* Class that provides the basic implementation for disposable objects. If your
* class holds one or more references to COM objects, DOM nodes, or other
* disposable objects, it should extend this class or implement the disposable
* interface (defined in goog.disposable.IDisposable).
* @constructor
* @implements {goog.disposable.IDisposable}
*/
goog.Disposable = function() {
/**
* If monitoring the goog.Disposable instances is enabled, stores the creation
* stack trace of the Disposable instance.
* @type {string|undefined}
*/
this.creationStack;
if (goog.Disposable.MONITORING_MODE != goog.Disposable.MonitoringMode.OFF) {
if (goog.Disposable.INCLUDE_STACK_ON_CREATION) {
this.creationStack = new Error().stack;
}
goog.Disposable.instances_[goog.getUid(this)] = this;
}
// Support sealing
this.disposed_ = this.disposed_;
this.onDisposeCallbacks_ = this.onDisposeCallbacks_;
};
/**
* @enum {number} Different monitoring modes for Disposable.
*/
goog.Disposable.MonitoringMode = {
/**
* No monitoring.
*/
OFF: 0,
/**
* Creating and disposing the goog.Disposable instances is monitored. All
* disposable objects need to call the {@code goog.Disposable} base
* constructor. The PERMANENT mode must be switched on before creating any
* goog.Disposable instances.
*/
PERMANENT: 1,
/**
* INTERACTIVE mode can be switched on and off on the fly without producing
* errors. It also doesn't warn if the disposable objects don't call the
* {@code goog.Disposable} base constructor.
*/
INTERACTIVE: 2
};
/**
* @define {number} The monitoring mode of the goog.Disposable
* instances. Default is OFF. Switching on the monitoring is only
* recommended for debugging because it has a significant impact on
* performance and memory usage. If switched off, the monitoring code
* compiles down to 0 bytes.
*/
goog.define('goog.Disposable.MONITORING_MODE', 0);
/**
* @define {boolean} Whether to attach creation stack to each created disposable
* instance; This is only relevant for when MonitoringMode != OFF.
*/
goog.define('goog.Disposable.INCLUDE_STACK_ON_CREATION', true);
/**
* Maps the unique ID of every undisposed {@code goog.Disposable} object to
* the object itself.
* @type {!Object<number, !goog.Disposable>}
* @private
*/
goog.Disposable.instances_ = {};
/**
* @return {!Array<!goog.Disposable>} All {@code goog.Disposable} objects that
* haven't been disposed of.
*/
goog.Disposable.getUndisposedObjects = function() {
var ret = [];
for (var id in goog.Disposable.instances_) {
if (goog.Disposable.instances_.hasOwnProperty(id)) {
ret.push(goog.Disposable.instances_[Number(id)]);
}
}
return ret;
};
/**
* Clears the registry of undisposed objects but doesn't dispose of them.
*/
goog.Disposable.clearUndisposedObjects = function() {
goog.Disposable.instances_ = {};
};
/**
* Whether the object has been disposed of.
* @type {boolean}
* @private
*/
goog.Disposable.prototype.disposed_ = false;
/**
* Callbacks to invoke when this object is disposed.
* @type {Array<!Function>}
* @private
*/
goog.Disposable.prototype.onDisposeCallbacks_;
/**
* @return {boolean} Whether the object has been disposed of.
* @override
*/
goog.Disposable.prototype.isDisposed = function() {
return this.disposed_;
};
/**
* @return {boolean} Whether the object has been disposed of.
* @deprecated Use {@link #isDisposed} instead.
*/
goog.Disposable.prototype.getDisposed = goog.Disposable.prototype.isDisposed;
/**
* Disposes of the object. If the object hasn't already been disposed of, calls
* {@link #disposeInternal}. Classes that extend {@code goog.Disposable} should
* override {@link #disposeInternal} in order to delete references to COM
* objects, DOM nodes, and other disposable objects. Reentrant.
*
* @return {void} Nothing.
* @override
*/
goog.Disposable.prototype.dispose = function() {
if (!this.disposed_) {
// Set disposed_ to true first, in case during the chain of disposal this
// gets disposed recursively.
this.disposed_ = true;
this.disposeInternal();
if (goog.Disposable.MONITORING_MODE != goog.Disposable.MonitoringMode.OFF) {
var uid = goog.getUid(this);
if (goog.Disposable.MONITORING_MODE ==
goog.Disposable.MonitoringMode.PERMANENT &&
!goog.Disposable.instances_.hasOwnProperty(uid)) {
throw Error(
this + ' did not call the goog.Disposable base ' +
'constructor or was disposed of after a clearUndisposedObjects ' +
'call');
}
delete goog.Disposable.instances_[uid];
}
}
};
/**
* Associates a disposable object with this object so that they will be disposed
* together.
* @param {goog.disposable.IDisposable} disposable that will be disposed when
* this object is disposed.
*/
goog.Disposable.prototype.registerDisposable = function(disposable) {
this.addOnDisposeCallback(goog.partial(goog.dispose, disposable));
};
/**
* Invokes a callback function when this object is disposed. Callbacks are
* invoked in the order in which they were added. If a callback is added to
* an already disposed Disposable, it will be called immediately.
* @param {function(this:T):?} callback The callback function.
* @param {T=} opt_scope An optional scope to call the callback in.
* @template T
*/
goog.Disposable.prototype.addOnDisposeCallback = function(callback, opt_scope) {
if (this.disposed_) {
goog.isDef(opt_scope) ? callback.call(opt_scope) : callback();
return;
}
if (!this.onDisposeCallbacks_) {
this.onDisposeCallbacks_ = [];
}
this.onDisposeCallbacks_.push(
goog.isDef(opt_scope) ? goog.bind(callback, opt_scope) : callback);
};
/**
* Deletes or nulls out any references to COM objects, DOM nodes, or other
* disposable objects. Classes that extend {@code goog.Disposable} should
* override this method.
* Not reentrant. To avoid calling it twice, it must only be called from the
* subclass' {@code disposeInternal} method. Everywhere else the public
* {@code dispose} method must be used.
* For example:
* <pre>
* mypackage.MyClass = function() {
* mypackage.MyClass.base(this, 'constructor');
* // Constructor logic specific to MyClass.
* ...
* };
* goog.inherits(mypackage.MyClass, goog.Disposable);
*
* mypackage.MyClass.prototype.disposeInternal = function() {
* // Dispose logic specific to MyClass.
* ...
* // Call superclass's disposeInternal at the end of the subclass's, like
* // in C++, to avoid hard-to-catch issues.
* mypackage.MyClass.base(this, 'disposeInternal');
* };
* </pre>
* @protected
*/
goog.Disposable.prototype.disposeInternal = function() {
if (this.onDisposeCallbacks_) {
while (this.onDisposeCallbacks_.length) {
this.onDisposeCallbacks_.shift()();
}
}
};
/**
* Returns True if we can verify the object is disposed.
* Calls {@code isDisposed} on the argument if it supports it. If obj
* is not an object with an isDisposed() method, return false.
* @param {*} obj The object to investigate.
* @return {boolean} True if we can verify the object is disposed.
*/
goog.Disposable.isDisposed = function(obj) {
if (obj && typeof obj.isDisposed == 'function') {
return obj.isDisposed();
}
return false;
};
/**
* Calls {@code dispose} on the argument if it supports it. If obj is not an
* object with a dispose() method, this is a no-op.
* @param {*} obj The object to dispose of.
*/
goog.dispose = function(obj) {
if (obj && typeof obj.dispose == 'function') {
obj.dispose();
}
};
/**
* Calls {@code dispose} on each member of the list that supports it. (If the
* member is an ArrayLike, then {@code goog.disposeAll()} will be called
* recursively on each of its members.) If the member is not an object with a
* {@code dispose()} method, then it is ignored.
* @param {...*} var_args The list.
*/
goog.disposeAll = function(var_args) {
for (var i = 0, len = arguments.length; i < len; ++i) {
var disposable = arguments[i];
if (goog.isArrayLike(disposable)) {
goog.disposeAll.apply(null, disposable);
} else {
goog.dispose(disposable);
}
}
};

View File

@ -0,0 +1,45 @@
// Copyright 2011 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Definition of the disposable interface. A disposable object
* has a dispose method to to clean up references and resources.
* @author nnaze@google.com (Nathan Naze)
*/
goog.provide('goog.disposable.IDisposable');
/**
* Interface for a disposable object. If a instance requires cleanup
* (references COM objects, DOM nodes, or other disposable objects), it should
* implement this interface (it may subclass goog.Disposable).
* @record
*/
goog.disposable.IDisposable = function() {};
/**
* Disposes of the object and its resources.
* @return {void} Nothing.
*/
goog.disposable.IDisposable.prototype.dispose = goog.abstractMethod;
/**
* @return {boolean} Whether the object has been disposed of.
*/
goog.disposable.IDisposable.prototype.isDisposed = goog.abstractMethod;

View File

@ -0,0 +1,311 @@
// Copyright 2017 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
goog.provide('goog.dom.asserts');
goog.require('goog.asserts');
/**
* @fileoverview Custom assertions to ensure that an element has the appropriate
* type.
*
* Using a goog.dom.safe wrapper on an object on the incorrect type (via an
* incorrect static type cast) can result in security bugs: For instance,
* g.d.s.setAnchorHref ensures that the URL assigned to the .href attribute
* satisfies the SafeUrl contract, i.e., is safe to dereference as a hyperlink.
* However, the value assigned to a HTMLLinkElement's .href property requires
* the stronger TrustedResourceUrl contract, since it can refer to a stylesheet.
* Thus, using g.d.s.setAnchorHref on an (incorrectly statically typed) object
* of type HTMLLinkElement can result in a security vulnerability.
* Assertions of the correct run-time type help prevent such incorrect use.
*
* In some cases, code using the DOM API is tested using mock objects (e.g., a
* plain object such as {'href': url} instead of an actual Location object).
* To allow such mocking, the assertions permit objects of types that are not
* relevant DOM API objects at all (for instance, not Element or Location).
*
* Note that instanceof checks don't work straightforwardly in older versions of
* IE, or across frames (see,
* http://stackoverflow.com/questions/384286/javascript-isdom-how-do-you-check-if-a-javascript-object-is-a-dom-object,
* http://stackoverflow.com/questions/26248599/instanceof-htmlelement-in-iframe-is-not-element-or-object).
*
* Hence, these assertions may pass vacuously in such scenarios. The resulting
* risk of security bugs is limited by the following factors:
* - A bug can only arise in scenarios involving incorrect static typing (the
* wrapper methods are statically typed to demand objects of the appropriate,
* precise type).
* - Typically, code is tested and exercised in multiple browsers.
*/
/**
* Asserts that a given object is a Location.
*
* To permit this assertion to pass in the context of tests where DOM APIs might
* be mocked, also accepts any other type except for subtypes of {!Element}.
* This is to ensure that, for instance, HTMLLinkElement is not being used in
* place of a Location, since this could result in security bugs due to stronger
* contracts required for assignments to the href property of the latter.
*
* @param {?Object} o The object whose type to assert.
* @return {!Location}
*/
goog.dom.asserts.assertIsLocation = function(o) {
if (goog.asserts.ENABLE_ASSERTS) {
var win = goog.dom.asserts.getWindow_(o);
if (typeof win.Location != 'undefined' &&
typeof win.Element != 'undefined') {
goog.asserts.assert(
o && (o instanceof win.Location || !(o instanceof win.Element)),
'Argument is not a Location (or a non-Element mock); got: %s',
goog.dom.asserts.debugStringForType_(o));
}
}
return /** @type {!Location} */ (o);
};
/**
* Asserts that a given object is a HTMLAnchorElement.
*
* To permit this assertion to pass in the context of tests where elements might
* be mocked, also accepts objects that are not of type Location nor a subtype
* of Element.
*
* @param {?Object} o The object whose type to assert.
* @return {!HTMLAnchorElement}
*/
goog.dom.asserts.assertIsHTMLAnchorElement = function(o) {
if (goog.asserts.ENABLE_ASSERTS) {
var win = goog.dom.asserts.getWindow_(o);
if (typeof win.HTMLAnchorElement != 'undefined' &&
typeof win.Location != 'undefined' &&
typeof win.Element != 'undefined') {
goog.asserts.assert(
o &&
(o instanceof win.HTMLAnchorElement ||
!((o instanceof win.Location) || (o instanceof win.Element))),
'Argument is not a HTMLAnchorElement (or a non-Element mock); ' +
'got: %s',
goog.dom.asserts.debugStringForType_(o));
}
}
return /** @type {!HTMLAnchorElement} */ (o);
};
/**
* Asserts that a given object is a HTMLLinkElement.
*
* To permit this assertion to pass in the context of tests where elements might
* be mocked, also accepts objects that are not a subtype of Element.
*
* @param {?Object} o The object whose type to assert.
* @return {!HTMLLinkElement}
*/
goog.dom.asserts.assertIsHTMLLinkElement = function(o) {
if (goog.asserts.ENABLE_ASSERTS) {
var win = goog.dom.asserts.getWindow_(o);
if (typeof win.HTMLLinkElement != 'undefined' &&
typeof win.Location != 'undefined' &&
typeof win.Element != 'undefined') {
goog.asserts.assert(
o &&
(o instanceof win.HTMLLinkElement ||
!((o instanceof win.Location) || (o instanceof win.Element))),
'Argument is not a HTMLLinkElement (or a non-Element mock); got: %s',
goog.dom.asserts.debugStringForType_(o));
}
}
return /** @type {!HTMLLinkElement} */ (o);
};
/**
* Asserts that a given object is a HTMLImageElement.
*
* To permit this assertion to pass in the context of tests where elements might
* be mocked, also accepts objects that are not a subtype of Element.
*
* @param {?Object} o The object whose type to assert.
* @return {!HTMLImageElement}
*/
goog.dom.asserts.assertIsHTMLImageElement = function(o) {
if (goog.asserts.ENABLE_ASSERTS) {
var win = goog.dom.asserts.getWindow_(o);
if (typeof win.HTMLImageElement != 'undefined' &&
typeof win.Element != 'undefined') {
goog.asserts.assert(
o &&
(o instanceof win.HTMLImageElement ||
!(o instanceof win.Element)),
'Argument is not a HTMLImageElement (or a non-Element mock); got: %s',
goog.dom.asserts.debugStringForType_(o));
}
}
return /** @type {!HTMLImageElement} */ (o);
};
/**
* Asserts that a given object is a HTMLEmbedElement.
*
* To permit this assertion to pass in the context of tests where elements might
* be mocked, also accepts objects that are not a subtype of Element.
*
* @param {?Object} o The object whose type to assert.
* @return {!HTMLEmbedElement}
*/
goog.dom.asserts.assertIsHTMLEmbedElement = function(o) {
if (goog.asserts.ENABLE_ASSERTS) {
var win = goog.dom.asserts.getWindow_(o);
if (typeof win.HTMLEmbedElement != 'undefined' &&
typeof win.Element != 'undefined') {
goog.asserts.assert(
o &&
(o instanceof win.HTMLEmbedElement ||
!(o instanceof win.Element)),
'Argument is not a HTMLEmbedElement (or a non-Element mock); got: %s',
goog.dom.asserts.debugStringForType_(o));
}
}
return /** @type {!HTMLEmbedElement} */ (o);
};
/**
* Asserts that a given object is a HTMLFrameElement.
*
* To permit this assertion to pass in the context of tests where elements might
* be mocked, also accepts objects that are not a subtype of Element.
*
* @param {?Object} o The object whose type to assert.
* @return {!HTMLFrameElement}
*/
goog.dom.asserts.assertIsHTMLFrameElement = function(o) {
if (goog.asserts.ENABLE_ASSERTS) {
var win = goog.dom.asserts.getWindow_(o);
if (typeof win.HTMLFrameElement != 'undefined' &&
typeof win.Element != 'undefined') {
goog.asserts.assert(
o &&
(o instanceof win.HTMLFrameElement ||
!(o instanceof win.Element)),
'Argument is not a HTMLFrameElement (or a non-Element mock); got: %s',
goog.dom.asserts.debugStringForType_(o));
}
}
return /** @type {!HTMLFrameElement} */ (o);
};
/**
* Asserts that a given object is a HTMLIFrameElement.
*
* To permit this assertion to pass in the context of tests where elements might
* be mocked, also accepts objects that are not a subtype of Element.
*
* @param {?Object} o The object whose type to assert.
* @return {!HTMLIFrameElement}
*/
goog.dom.asserts.assertIsHTMLIFrameElement = function(o) {
if (goog.asserts.ENABLE_ASSERTS) {
var win = goog.dom.asserts.getWindow_(o);
if (typeof win.HTMLIFrameElement != 'undefined' &&
typeof win.Element != 'undefined') {
goog.asserts.assert(
o &&
(o instanceof win.HTMLIFrameElement ||
!(o instanceof win.Element)),
'Argument is not a HTMLIFrameElement (or a non-Element mock); ' +
'got: %s',
goog.dom.asserts.debugStringForType_(o));
}
}
return /** @type {!HTMLIFrameElement} */ (o);
};
/**
* Asserts that a given object is a HTMLObjectElement.
*
* To permit this assertion to pass in the context of tests where elements might
* be mocked, also accepts objects that are not a subtype of Element.
*
* @param {?Object} o The object whose type to assert.
* @return {!HTMLObjectElement}
*/
goog.dom.asserts.assertIsHTMLObjectElement = function(o) {
if (goog.asserts.ENABLE_ASSERTS) {
var win = goog.dom.asserts.getWindow_(o);
if (typeof win.HTMLObjectElement != 'undefined' &&
typeof win.Element != 'undefined') {
goog.asserts.assert(
o &&
(o instanceof win.HTMLObjectElement ||
!(o instanceof win.Element)),
'Argument is not a HTMLObjectElement (or a non-Element mock); ' +
'got: %s',
goog.dom.asserts.debugStringForType_(o));
}
}
return /** @type {!HTMLObjectElement} */ (o);
};
/**
* Asserts that a given object is a HTMLScriptElement.
*
* To permit this assertion to pass in the context of tests where elements might
* be mocked, also accepts objects that are not a subtype of Element.
*
* @param {?Object} o The object whose type to assert.
* @return {!HTMLScriptElement}
*/
goog.dom.asserts.assertIsHTMLScriptElement = function(o) {
if (goog.asserts.ENABLE_ASSERTS) {
var win = goog.dom.asserts.getWindow_(o);
if (typeof win.HTMLScriptElement != 'undefined' &&
typeof win.Element != 'undefined') {
goog.asserts.assert(
o &&
(o instanceof win.HTMLScriptElement ||
!(o instanceof win.Element)),
'Argument is not a HTMLScriptElement (or a non-Element mock); ' +
'got: %s',
goog.dom.asserts.debugStringForType_(o));
}
}
return /** @type {!HTMLScriptElement} */ (o);
};
/**
* Returns a string representation of a value's type.
*
* @param {*} value An object, or primitive.
* @return {string} The best display name for the value.
* @private
*/
goog.dom.asserts.debugStringForType_ = function(value) {
if (goog.isObject(value)) {
return value.constructor.displayName || value.constructor.name ||
Object.prototype.toString.call(value);
} else {
return value === undefined ? 'undefined' :
value === null ? 'null' : typeof value;
}
};
/**
* Gets window of element.
* @param {?Object} o
* @return {!Window}
* @private
*/
goog.dom.asserts.getWindow_ = function(o) {
var doc = o && o.ownerDocument;
var win = doc && /** @type {?Window} */ (doc.defaultView || doc.parentWindow);
return win || /** @type {!Window} */ (goog.global);
};

View File

@ -0,0 +1,73 @@
// Copyright 2010 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Browser capability checks for the dom package.
*
*/
goog.provide('goog.dom.BrowserFeature');
goog.require('goog.userAgent');
/**
* Enum of browser capabilities.
* @enum {boolean}
*/
goog.dom.BrowserFeature = {
/**
* Whether attributes 'name' and 'type' can be added to an element after it's
* created. False in Internet Explorer prior to version 9.
*/
CAN_ADD_NAME_OR_TYPE_ATTRIBUTES:
!goog.userAgent.IE || goog.userAgent.isDocumentModeOrHigher(9),
/**
* Whether we can use element.children to access an element's Element
* children. Available since Gecko 1.9.1, IE 9. (IE<9 also includes comment
* nodes in the collection.)
*/
CAN_USE_CHILDREN_ATTRIBUTE: !goog.userAgent.GECKO && !goog.userAgent.IE ||
goog.userAgent.IE && goog.userAgent.isDocumentModeOrHigher(9) ||
goog.userAgent.GECKO && goog.userAgent.isVersionOrHigher('1.9.1'),
/**
* Opera, Safari 3, and Internet Explorer 9 all support innerText but they
* include text nodes in script and style tags. Not document-mode-dependent.
*/
CAN_USE_INNER_TEXT:
(goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9')),
/**
* MSIE, Opera, and Safari>=4 support element.parentElement to access an
* element's parent if it is an Element.
*/
CAN_USE_PARENT_ELEMENT_PROPERTY:
goog.userAgent.IE || goog.userAgent.OPERA || goog.userAgent.WEBKIT,
/**
* Whether NoScope elements need a scoped element written before them in
* innerHTML.
* MSDN: http://msdn.microsoft.com/en-us/library/ms533897(VS.85).aspx#1
*/
INNER_HTML_NEEDS_SCOPED_ELEMENT: goog.userAgent.IE,
/**
* Whether we use legacy IE range API.
*/
LEGACY_IE_RANGES:
goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,29 @@
// Copyright 2017 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
goog.provide('goog.dom.HtmlElement');
/**
* This subclass of HTMLElement is used when only a HTMLElement is possible and
* not any of its subclasses. Normally, a type can refer to an instance of
* itself or an instance of any subtype. More concretely, if HTMLElement is used
* then the compiler must assume that it might still be e.g. HTMLScriptElement.
* With this, the type check knows that it couldn't be any special element.
*
* @constructor
* @extends {HTMLElement}
*/
goog.dom.HtmlElement = function() {};

View File

@ -0,0 +1,48 @@
// Copyright 2006 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Definition of goog.dom.NodeType.
*/
goog.provide('goog.dom.NodeType');
/**
* Constants for the nodeType attribute in the Node interface.
*
* These constants match those specified in the Node interface. These are
* usually present on the Node object in recent browsers, but not in older
* browsers (specifically, early IEs) and thus are given here.
*
* In some browsers (early IEs), these are not defined on the Node object,
* so they are provided here.
*
* See http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-1950641247
* @enum {number}
*/
goog.dom.NodeType = {
ELEMENT: 1,
ATTRIBUTE: 2,
TEXT: 3,
CDATA_SECTION: 4,
ENTITY_REFERENCE: 5,
ENTITY: 6,
PROCESSING_INSTRUCTION: 7,
COMMENT: 8,
DOCUMENT: 9,
DOCUMENT_TYPE: 10,
DOCUMENT_FRAGMENT: 11,
NOTATION: 12
};

View File

@ -0,0 +1,458 @@
// Copyright 2013 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Type-safe wrappers for unsafe DOM APIs.
*
* This file provides type-safe wrappers for DOM APIs that can result in
* cross-site scripting (XSS) vulnerabilities, if the API is supplied with
* untrusted (attacker-controlled) input. Instead of plain strings, the type
* safe wrappers consume values of types from the goog.html package whose
* contract promises that values are safe to use in the corresponding context.
*
* Hence, a program that exclusively uses the wrappers in this file (i.e., whose
* only reference to security-sensitive raw DOM APIs are in this file) is
* guaranteed to be free of XSS due to incorrect use of such DOM APIs (modulo
* correctness of code that produces values of the respective goog.html types,
* and absent code that violates type safety).
*
* For example, assigning to an element's .innerHTML property a string that is
* derived (even partially) from untrusted input typically results in an XSS
* vulnerability. The type-safe wrapper goog.dom.safe.setInnerHtml consumes a
* value of type goog.html.SafeHtml, whose contract states that using its values
* in a HTML context will not result in XSS. Hence a program that is free of
* direct assignments to any element's innerHTML property (with the exception of
* the assignment to .innerHTML in this file) is guaranteed to be free of XSS
* due to assignment of untrusted strings to the innerHTML property.
*/
goog.provide('goog.dom.safe');
goog.provide('goog.dom.safe.InsertAdjacentHtmlPosition');
goog.require('goog.asserts');
goog.require('goog.dom.asserts');
goog.require('goog.html.SafeHtml');
goog.require('goog.html.SafeScript');
goog.require('goog.html.SafeStyle');
goog.require('goog.html.SafeUrl');
goog.require('goog.html.TrustedResourceUrl');
goog.require('goog.string');
goog.require('goog.string.Const');
/** @enum {string} */
goog.dom.safe.InsertAdjacentHtmlPosition = {
AFTERBEGIN: 'afterbegin',
AFTEREND: 'afterend',
BEFOREBEGIN: 'beforebegin',
BEFOREEND: 'beforeend'
};
/**
* Inserts known-safe HTML into a Node, at the specified position.
* @param {!Node} node The node on which to call insertAdjacentHTML.
* @param {!goog.dom.safe.InsertAdjacentHtmlPosition} position Position where
* to insert the HTML.
* @param {!goog.html.SafeHtml} html The known-safe HTML to insert.
*/
goog.dom.safe.insertAdjacentHtml = function(node, position, html) {
node.insertAdjacentHTML(position, goog.html.SafeHtml.unwrap(html));
};
/**
* Tags not allowed in goog.dom.safe.setInnerHtml.
* @private @const {!Object<string, boolean>}
*/
goog.dom.safe.SET_INNER_HTML_DISALLOWED_TAGS_ = {
'MATH': true,
'SCRIPT': true,
'STYLE': true,
'SVG': true,
'TEMPLATE': true
};
/**
* Assigns known-safe HTML to an element's innerHTML property.
* @param {!Element} elem The element whose innerHTML is to be assigned to.
* @param {!goog.html.SafeHtml} html The known-safe HTML to assign.
* @throws {Error} If called with one of these tags: math, script, style, svg,
* template.
*/
goog.dom.safe.setInnerHtml = function(elem, html) {
if (goog.asserts.ENABLE_ASSERTS) {
var tagName = elem.tagName.toUpperCase();
if (goog.dom.safe.SET_INNER_HTML_DISALLOWED_TAGS_[tagName]) {
throw Error(
'goog.dom.safe.setInnerHtml cannot be used to set content of ' +
elem.tagName + '.');
}
}
elem.innerHTML = goog.html.SafeHtml.unwrap(html);
};
/**
* Assigns known-safe HTML to an element's outerHTML property.
* @param {!Element} elem The element whose outerHTML is to be assigned to.
* @param {!goog.html.SafeHtml} html The known-safe HTML to assign.
*/
goog.dom.safe.setOuterHtml = function(elem, html) {
elem.outerHTML = goog.html.SafeHtml.unwrap(html);
};
/**
* Sets the given element's style property to the contents of the provided
* SafeStyle object.
* @param {!Element} elem
* @param {!goog.html.SafeStyle} style
*/
goog.dom.safe.setStyle = function(elem, style) {
elem.style.cssText = goog.html.SafeStyle.unwrap(style);
};
/**
* Writes known-safe HTML to a document.
* @param {!Document} doc The document to be written to.
* @param {!goog.html.SafeHtml} html The known-safe HTML to assign.
*/
goog.dom.safe.documentWrite = function(doc, html) {
doc.write(goog.html.SafeHtml.unwrap(html));
};
/**
* Safely assigns a URL to an anchor element's href property.
*
* If url is of type goog.html.SafeUrl, its value is unwrapped and assigned to
* anchor's href property. If url is of type string however, it is first
* sanitized using goog.html.SafeUrl.sanitize.
*
* Example usage:
* goog.dom.safe.setAnchorHref(anchorEl, url);
* which is a safe alternative to
* anchorEl.href = url;
* The latter can result in XSS vulnerabilities if url is a
* user-/attacker-controlled value.
*
* @param {!HTMLAnchorElement} anchor The anchor element whose href property
* is to be assigned to.
* @param {string|!goog.html.SafeUrl} url The URL to assign.
* @see goog.html.SafeUrl#sanitize
*/
goog.dom.safe.setAnchorHref = function(anchor, url) {
goog.dom.asserts.assertIsHTMLAnchorElement(anchor);
/** @type {!goog.html.SafeUrl} */
var safeUrl;
if (url instanceof goog.html.SafeUrl) {
safeUrl = url;
} else {
safeUrl = goog.html.SafeUrl.sanitizeAssertUnchanged(url);
}
anchor.href = goog.html.SafeUrl.unwrap(safeUrl);
};
/**
* Safely assigns a URL to an image element's src property.
*
* If url is of type goog.html.SafeUrl, its value is unwrapped and assigned to
* image's src property. If url is of type string however, it is first
* sanitized using goog.html.SafeUrl.sanitize.
*
* @param {!HTMLImageElement} imageElement The image element whose src property
* is to be assigned to.
* @param {string|!goog.html.SafeUrl} url The URL to assign.
* @see goog.html.SafeUrl#sanitize
*/
goog.dom.safe.setImageSrc = function(imageElement, url) {
goog.dom.asserts.assertIsHTMLImageElement(imageElement);
/** @type {!goog.html.SafeUrl} */
var safeUrl;
if (url instanceof goog.html.SafeUrl) {
safeUrl = url;
} else {
safeUrl = goog.html.SafeUrl.sanitizeAssertUnchanged(url);
}
imageElement.src = goog.html.SafeUrl.unwrap(safeUrl);
};
/**
* Safely assigns a URL to an embed element's src property.
*
* Example usage:
* goog.dom.safe.setEmbedSrc(embedEl, url);
* which is a safe alternative to
* embedEl.src = url;
* The latter can result in loading untrusted code unless it is ensured that
* the URL refers to a trustworthy resource.
*
* @param {!HTMLEmbedElement} embed The embed element whose src property
* is to be assigned to.
* @param {!goog.html.TrustedResourceUrl} url The URL to assign.
*/
goog.dom.safe.setEmbedSrc = function(embed, url) {
goog.dom.asserts.assertIsHTMLEmbedElement(embed);
embed.src = goog.html.TrustedResourceUrl.unwrap(url);
};
/**
* Safely assigns a URL to a frame element's src property.
*
* Example usage:
* goog.dom.safe.setFrameSrc(frameEl, url);
* which is a safe alternative to
* frameEl.src = url;
* The latter can result in loading untrusted code unless it is ensured that
* the URL refers to a trustworthy resource.
*
* @param {!HTMLFrameElement} frame The frame element whose src property
* is to be assigned to.
* @param {!goog.html.TrustedResourceUrl} url The URL to assign.
*/
goog.dom.safe.setFrameSrc = function(frame, url) {
goog.dom.asserts.assertIsHTMLFrameElement(frame);
frame.src = goog.html.TrustedResourceUrl.unwrap(url);
};
/**
* Safely assigns a URL to an iframe element's src property.
*
* Example usage:
* goog.dom.safe.setIframeSrc(iframeEl, url);
* which is a safe alternative to
* iframeEl.src = url;
* The latter can result in loading untrusted code unless it is ensured that
* the URL refers to a trustworthy resource.
*
* @param {!HTMLIFrameElement} iframe The iframe element whose src property
* is to be assigned to.
* @param {!goog.html.TrustedResourceUrl} url The URL to assign.
*/
goog.dom.safe.setIframeSrc = function(iframe, url) {
goog.dom.asserts.assertIsHTMLIFrameElement(iframe);
iframe.src = goog.html.TrustedResourceUrl.unwrap(url);
};
/**
* Safely assigns HTML to an iframe element's srcdoc property.
*
* Example usage:
* goog.dom.safe.setIframeSrcdoc(iframeEl, safeHtml);
* which is a safe alternative to
* iframeEl.srcdoc = html;
* The latter can result in loading untrusted code.
*
* @param {!HTMLIFrameElement} iframe The iframe element whose srcdoc property
* is to be assigned to.
* @param {!goog.html.SafeHtml} html The HTML to assign.
*/
goog.dom.safe.setIframeSrcdoc = function(iframe, html) {
goog.dom.asserts.assertIsHTMLIFrameElement(iframe);
iframe.srcdoc = goog.html.SafeHtml.unwrap(html);
};
/**
* Safely sets a link element's href and rel properties. Whether or not
* the URL assigned to href has to be a goog.html.TrustedResourceUrl
* depends on the value of the rel property. If rel contains "stylesheet"
* then a TrustedResourceUrl is required.
*
* Example usage:
* goog.dom.safe.setLinkHrefAndRel(linkEl, url, 'stylesheet');
* which is a safe alternative to
* linkEl.rel = 'stylesheet';
* linkEl.href = url;
* The latter can result in loading untrusted code unless it is ensured that
* the URL refers to a trustworthy resource.
*
* @param {!HTMLLinkElement} link The link element whose href property
* is to be assigned to.
* @param {string|!goog.html.SafeUrl|!goog.html.TrustedResourceUrl} url The URL
* to assign to the href property. Must be a TrustedResourceUrl if the
* value assigned to rel contains "stylesheet". A string value is
* sanitized with goog.html.SafeUrl.sanitize.
* @param {string} rel The value to assign to the rel property.
* @throws {Error} if rel contains "stylesheet" and url is not a
* TrustedResourceUrl
* @see goog.html.SafeUrl#sanitize
*/
goog.dom.safe.setLinkHrefAndRel = function(link, url, rel) {
goog.dom.asserts.assertIsHTMLLinkElement(link);
link.rel = rel;
if (goog.string.caseInsensitiveContains(rel, 'stylesheet')) {
goog.asserts.assert(
url instanceof goog.html.TrustedResourceUrl,
'URL must be TrustedResourceUrl because "rel" contains "stylesheet"');
link.href = goog.html.TrustedResourceUrl.unwrap(url);
} else if (url instanceof goog.html.TrustedResourceUrl) {
link.href = goog.html.TrustedResourceUrl.unwrap(url);
} else if (url instanceof goog.html.SafeUrl) {
link.href = goog.html.SafeUrl.unwrap(url);
} else { // string
// SafeUrl.sanitize must return legitimate SafeUrl when passed a string.
link.href =
goog.html.SafeUrl.sanitizeAssertUnchanged(url).getTypedStringValue();
}
};
/**
* Safely assigns a URL to an object element's data property.
*
* Example usage:
* goog.dom.safe.setObjectData(objectEl, url);
* which is a safe alternative to
* objectEl.data = url;
* The latter can result in loading untrusted code unless setit is ensured that
* the URL refers to a trustworthy resource.
*
* @param {!HTMLObjectElement} object The object element whose data property
* is to be assigned to.
* @param {!goog.html.TrustedResourceUrl} url The URL to assign.
*/
goog.dom.safe.setObjectData = function(object, url) {
goog.dom.asserts.assertIsHTMLObjectElement(object);
object.data = goog.html.TrustedResourceUrl.unwrap(url);
};
/**
* Safely assigns a URL to a script element's src property.
*
* Example usage:
* goog.dom.safe.setScriptSrc(scriptEl, url);
* which is a safe alternative to
* scriptEl.src = url;
* The latter can result in loading untrusted code unless it is ensured that
* the URL refers to a trustworthy resource.
*
* @param {!HTMLScriptElement} script The script element whose src property
* is to be assigned to.
* @param {!goog.html.TrustedResourceUrl} url The URL to assign.
*/
goog.dom.safe.setScriptSrc = function(script, url) {
goog.dom.asserts.assertIsHTMLScriptElement(script);
script.src = goog.html.TrustedResourceUrl.unwrap(url);
};
/**
* Safely assigns a value to a script element's content.
*
* Example usage:
* goog.dom.safe.setScriptContent(scriptEl, content);
* which is a safe alternative to
* scriptEl.text = content;
* The latter can result in executing untrusted code unless it is ensured that
* the code is loaded from a trustworthy resource.
*
* @param {!HTMLScriptElement} script The script element whose content is being
* set.
* @param {!goog.html.SafeScript} content The content to assign.
*/
goog.dom.safe.setScriptContent = function(script, content) {
goog.dom.asserts.assertIsHTMLScriptElement(script);
script.text = goog.html.SafeScript.unwrap(content);
};
/**
* Safely assigns a URL to a Location object's href property.
*
* If url is of type goog.html.SafeUrl, its value is unwrapped and assigned to
* loc's href property. If url is of type string however, it is first sanitized
* using goog.html.SafeUrl.sanitize.
*
* Example usage:
* goog.dom.safe.setLocationHref(document.location, redirectUrl);
* which is a safe alternative to
* document.location.href = redirectUrl;
* The latter can result in XSS vulnerabilities if redirectUrl is a
* user-/attacker-controlled value.
*
* @param {!Location} loc The Location object whose href property is to be
* assigned to.
* @param {string|!goog.html.SafeUrl} url The URL to assign.
* @see goog.html.SafeUrl#sanitize
*/
goog.dom.safe.setLocationHref = function(loc, url) {
goog.dom.asserts.assertIsLocation(loc);
/** @type {!goog.html.SafeUrl} */
var safeUrl;
if (url instanceof goog.html.SafeUrl) {
safeUrl = url;
} else {
safeUrl = goog.html.SafeUrl.sanitizeAssertUnchanged(url);
}
loc.href = goog.html.SafeUrl.unwrap(safeUrl);
};
/**
* Safely opens a URL in a new window (via window.open).
*
* If url is of type goog.html.SafeUrl, its value is unwrapped and passed in to
* window.open. If url is of type string however, it is first sanitized
* using goog.html.SafeUrl.sanitize.
*
* Note that this function does not prevent leakages via the referer that is
* sent by window.open. It is advised to only use this to open 1st party URLs.
*
* Example usage:
* goog.dom.safe.openInWindow(url);
* which is a safe alternative to
* window.open(url);
* The latter can result in XSS vulnerabilities if redirectUrl is a
* user-/attacker-controlled value.
*
* @param {string|!goog.html.SafeUrl} url The URL to open.
* @param {Window=} opt_openerWin Window of which to call the .open() method.
* Defaults to the global window.
* @param {!goog.string.Const=} opt_name Name of the window to open in. Can be
* _top, etc as allowed by window.open().
* @param {string=} opt_specs Comma-separated list of specifications, same as
* in window.open().
* @param {boolean=} opt_replace Whether to replace the current entry in browser
* history, same as in window.open().
* @return {Window} Window the url was opened in.
*/
goog.dom.safe.openInWindow = function(
url, opt_openerWin, opt_name, opt_specs, opt_replace) {
/** @type {!goog.html.SafeUrl} */
var safeUrl;
if (url instanceof goog.html.SafeUrl) {
safeUrl = url;
} else {
safeUrl = goog.html.SafeUrl.sanitizeAssertUnchanged(url);
}
var win = opt_openerWin || window;
return win.open(
goog.html.SafeUrl.unwrap(safeUrl),
// If opt_name is undefined, simply passing that in to open() causes IE to
// reuse the current window instead of opening a new one. Thus we pass ''
// in instead, which according to spec opens a new window. See
// https://html.spec.whatwg.org/multipage/browsers.html#dom-open .
opt_name ? goog.string.Const.unwrap(opt_name) : '', opt_specs,
opt_replace);
};

View File

@ -0,0 +1,562 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Defines the goog.dom.TagName class. Its constants enumerate
* all HTML tag names specified in either the the W3C HTML 4.01 index of
* elements or the HTML5 draft specification.
*
* References:
* http://www.w3.org/TR/html401/index/elements.html
* http://dev.w3.org/html5/spec/section-index.html
*/
goog.provide('goog.dom.TagName');
goog.require('goog.dom.HtmlElement');
/**
* A tag name with the type of the element stored in the generic.
* @param {string} tagName
* @constructor
* @template T
*/
goog.dom.TagName = function(tagName) {
/** @private {string} */
this.tagName_ = tagName;
};
/**
* Returns the tag name.
* @return {string}
* @override
*/
goog.dom.TagName.prototype.toString = function() {
return this.tagName_;
};
// Closure Compiler unconditionally converts the following constants to their
// string value (goog.dom.TagName.A -> 'A'). These are the consequences:
// 1. Don't add any members or static members to goog.dom.TagName as they
// couldn't be accessed after this optimization.
// 2. Keep the constant name and its string value the same:
// goog.dom.TagName.X = new goog.dom.TagName('Y');
// is converted to 'X', not 'Y'.
/** @type {!goog.dom.TagName<!HTMLAnchorElement>} */
goog.dom.TagName.A = new goog.dom.TagName('A');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.ABBR = new goog.dom.TagName('ABBR');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.ACRONYM = new goog.dom.TagName('ACRONYM');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.ADDRESS = new goog.dom.TagName('ADDRESS');
/** @type {!goog.dom.TagName<!HTMLAppletElement>} */
goog.dom.TagName.APPLET = new goog.dom.TagName('APPLET');
/** @type {!goog.dom.TagName<!HTMLAreaElement>} */
goog.dom.TagName.AREA = new goog.dom.TagName('AREA');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.ARTICLE = new goog.dom.TagName('ARTICLE');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.ASIDE = new goog.dom.TagName('ASIDE');
/** @type {!goog.dom.TagName<!HTMLAudioElement>} */
goog.dom.TagName.AUDIO = new goog.dom.TagName('AUDIO');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.B = new goog.dom.TagName('B');
/** @type {!goog.dom.TagName<!HTMLBaseElement>} */
goog.dom.TagName.BASE = new goog.dom.TagName('BASE');
/** @type {!goog.dom.TagName<!HTMLBaseFontElement>} */
goog.dom.TagName.BASEFONT = new goog.dom.TagName('BASEFONT');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.BDI = new goog.dom.TagName('BDI');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.BDO = new goog.dom.TagName('BDO');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.BIG = new goog.dom.TagName('BIG');
/** @type {!goog.dom.TagName<!HTMLQuoteElement>} */
goog.dom.TagName.BLOCKQUOTE = new goog.dom.TagName('BLOCKQUOTE');
/** @type {!goog.dom.TagName<!HTMLBodyElement>} */
goog.dom.TagName.BODY = new goog.dom.TagName('BODY');
/** @type {!goog.dom.TagName<!HTMLBRElement>} */
goog.dom.TagName.BR = new goog.dom.TagName('BR');
/** @type {!goog.dom.TagName<!HTMLButtonElement>} */
goog.dom.TagName.BUTTON = new goog.dom.TagName('BUTTON');
/** @type {!goog.dom.TagName<!HTMLCanvasElement>} */
goog.dom.TagName.CANVAS = new goog.dom.TagName('CANVAS');
/** @type {!goog.dom.TagName<!HTMLTableCaptionElement>} */
goog.dom.TagName.CAPTION = new goog.dom.TagName('CAPTION');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.CENTER = new goog.dom.TagName('CENTER');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.CITE = new goog.dom.TagName('CITE');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.CODE = new goog.dom.TagName('CODE');
/** @type {!goog.dom.TagName<!HTMLTableColElement>} */
goog.dom.TagName.COL = new goog.dom.TagName('COL');
/** @type {!goog.dom.TagName<!HTMLTableColElement>} */
goog.dom.TagName.COLGROUP = new goog.dom.TagName('COLGROUP');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.COMMAND = new goog.dom.TagName('COMMAND');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.DATA = new goog.dom.TagName('DATA');
/** @type {!goog.dom.TagName<!HTMLDataListElement>} */
goog.dom.TagName.DATALIST = new goog.dom.TagName('DATALIST');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.DD = new goog.dom.TagName('DD');
/** @type {!goog.dom.TagName<!HTMLModElement>} */
goog.dom.TagName.DEL = new goog.dom.TagName('DEL');
/** @type {!goog.dom.TagName<!HTMLDetailsElement>} */
goog.dom.TagName.DETAILS = new goog.dom.TagName('DETAILS');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.DFN = new goog.dom.TagName('DFN');
/** @type {!goog.dom.TagName<!HTMLDialogElement>} */
goog.dom.TagName.DIALOG = new goog.dom.TagName('DIALOG');
/** @type {!goog.dom.TagName<!HTMLDirectoryElement>} */
goog.dom.TagName.DIR = new goog.dom.TagName('DIR');
/** @type {!goog.dom.TagName<!HTMLDivElement>} */
goog.dom.TagName.DIV = new goog.dom.TagName('DIV');
/** @type {!goog.dom.TagName<!HTMLDListElement>} */
goog.dom.TagName.DL = new goog.dom.TagName('DL');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.DT = new goog.dom.TagName('DT');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.EM = new goog.dom.TagName('EM');
/** @type {!goog.dom.TagName<!HTMLEmbedElement>} */
goog.dom.TagName.EMBED = new goog.dom.TagName('EMBED');
/** @type {!goog.dom.TagName<!HTMLFieldSetElement>} */
goog.dom.TagName.FIELDSET = new goog.dom.TagName('FIELDSET');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.FIGCAPTION = new goog.dom.TagName('FIGCAPTION');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.FIGURE = new goog.dom.TagName('FIGURE');
/** @type {!goog.dom.TagName<!HTMLFontElement>} */
goog.dom.TagName.FONT = new goog.dom.TagName('FONT');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.FOOTER = new goog.dom.TagName('FOOTER');
/** @type {!goog.dom.TagName<!HTMLFormElement>} */
goog.dom.TagName.FORM = new goog.dom.TagName('FORM');
/** @type {!goog.dom.TagName<!HTMLFrameElement>} */
goog.dom.TagName.FRAME = new goog.dom.TagName('FRAME');
/** @type {!goog.dom.TagName<!HTMLFrameSetElement>} */
goog.dom.TagName.FRAMESET = new goog.dom.TagName('FRAMESET');
/** @type {!goog.dom.TagName<!HTMLHeadingElement>} */
goog.dom.TagName.H1 = new goog.dom.TagName('H1');
/** @type {!goog.dom.TagName<!HTMLHeadingElement>} */
goog.dom.TagName.H2 = new goog.dom.TagName('H2');
/** @type {!goog.dom.TagName<!HTMLHeadingElement>} */
goog.dom.TagName.H3 = new goog.dom.TagName('H3');
/** @type {!goog.dom.TagName<!HTMLHeadingElement>} */
goog.dom.TagName.H4 = new goog.dom.TagName('H4');
/** @type {!goog.dom.TagName<!HTMLHeadingElement>} */
goog.dom.TagName.H5 = new goog.dom.TagName('H5');
/** @type {!goog.dom.TagName<!HTMLHeadingElement>} */
goog.dom.TagName.H6 = new goog.dom.TagName('H6');
/** @type {!goog.dom.TagName<!HTMLHeadElement>} */
goog.dom.TagName.HEAD = new goog.dom.TagName('HEAD');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.HEADER = new goog.dom.TagName('HEADER');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.HGROUP = new goog.dom.TagName('HGROUP');
/** @type {!goog.dom.TagName<!HTMLHRElement>} */
goog.dom.TagName.HR = new goog.dom.TagName('HR');
/** @type {!goog.dom.TagName<!HTMLHtmlElement>} */
goog.dom.TagName.HTML = new goog.dom.TagName('HTML');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.I = new goog.dom.TagName('I');
/** @type {!goog.dom.TagName<!HTMLIFrameElement>} */
goog.dom.TagName.IFRAME = new goog.dom.TagName('IFRAME');
/** @type {!goog.dom.TagName<!HTMLImageElement>} */
goog.dom.TagName.IMG = new goog.dom.TagName('IMG');
/** @type {!goog.dom.TagName<!HTMLInputElement>} */
goog.dom.TagName.INPUT = new goog.dom.TagName('INPUT');
/** @type {!goog.dom.TagName<!HTMLModElement>} */
goog.dom.TagName.INS = new goog.dom.TagName('INS');
/** @type {!goog.dom.TagName<!HTMLIsIndexElement>} */
goog.dom.TagName.ISINDEX = new goog.dom.TagName('ISINDEX');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.KBD = new goog.dom.TagName('KBD');
// HTMLKeygenElement is deprecated.
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.KEYGEN = new goog.dom.TagName('KEYGEN');
/** @type {!goog.dom.TagName<!HTMLLabelElement>} */
goog.dom.TagName.LABEL = new goog.dom.TagName('LABEL');
/** @type {!goog.dom.TagName<!HTMLLegendElement>} */
goog.dom.TagName.LEGEND = new goog.dom.TagName('LEGEND');
/** @type {!goog.dom.TagName<!HTMLLIElement>} */
goog.dom.TagName.LI = new goog.dom.TagName('LI');
/** @type {!goog.dom.TagName<!HTMLLinkElement>} */
goog.dom.TagName.LINK = new goog.dom.TagName('LINK');
/** @type {!goog.dom.TagName<!HTMLMapElement>} */
goog.dom.TagName.MAP = new goog.dom.TagName('MAP');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.MARK = new goog.dom.TagName('MARK');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.MATH = new goog.dom.TagName('MATH');
/** @type {!goog.dom.TagName<!HTMLMenuElement>} */
goog.dom.TagName.MENU = new goog.dom.TagName('MENU');
/** @type {!goog.dom.TagName<!HTMLMetaElement>} */
goog.dom.TagName.META = new goog.dom.TagName('META');
/** @type {!goog.dom.TagName<!HTMLMeterElement>} */
goog.dom.TagName.METER = new goog.dom.TagName('METER');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.NAV = new goog.dom.TagName('NAV');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.NOFRAMES = new goog.dom.TagName('NOFRAMES');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.NOSCRIPT = new goog.dom.TagName('NOSCRIPT');
/** @type {!goog.dom.TagName<!HTMLObjectElement>} */
goog.dom.TagName.OBJECT = new goog.dom.TagName('OBJECT');
/** @type {!goog.dom.TagName<!HTMLOListElement>} */
goog.dom.TagName.OL = new goog.dom.TagName('OL');
/** @type {!goog.dom.TagName<!HTMLOptGroupElement>} */
goog.dom.TagName.OPTGROUP = new goog.dom.TagName('OPTGROUP');
/** @type {!goog.dom.TagName<!HTMLOptionElement>} */
goog.dom.TagName.OPTION = new goog.dom.TagName('OPTION');
/** @type {!goog.dom.TagName<!HTMLOutputElement>} */
goog.dom.TagName.OUTPUT = new goog.dom.TagName('OUTPUT');
/** @type {!goog.dom.TagName<!HTMLParagraphElement>} */
goog.dom.TagName.P = new goog.dom.TagName('P');
/** @type {!goog.dom.TagName<!HTMLParamElement>} */
goog.dom.TagName.PARAM = new goog.dom.TagName('PARAM');
/** @type {!goog.dom.TagName<!HTMLPreElement>} */
goog.dom.TagName.PRE = new goog.dom.TagName('PRE');
/** @type {!goog.dom.TagName<!HTMLProgressElement>} */
goog.dom.TagName.PROGRESS = new goog.dom.TagName('PROGRESS');
/** @type {!goog.dom.TagName<!HTMLQuoteElement>} */
goog.dom.TagName.Q = new goog.dom.TagName('Q');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.RP = new goog.dom.TagName('RP');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.RT = new goog.dom.TagName('RT');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.RUBY = new goog.dom.TagName('RUBY');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.S = new goog.dom.TagName('S');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.SAMP = new goog.dom.TagName('SAMP');
/** @type {!goog.dom.TagName<!HTMLScriptElement>} */
goog.dom.TagName.SCRIPT = new goog.dom.TagName('SCRIPT');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.SECTION = new goog.dom.TagName('SECTION');
/** @type {!goog.dom.TagName<!HTMLSelectElement>} */
goog.dom.TagName.SELECT = new goog.dom.TagName('SELECT');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.SMALL = new goog.dom.TagName('SMALL');
/** @type {!goog.dom.TagName<!HTMLSourceElement>} */
goog.dom.TagName.SOURCE = new goog.dom.TagName('SOURCE');
/** @type {!goog.dom.TagName<!HTMLSpanElement>} */
goog.dom.TagName.SPAN = new goog.dom.TagName('SPAN');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.STRIKE = new goog.dom.TagName('STRIKE');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.STRONG = new goog.dom.TagName('STRONG');
/** @type {!goog.dom.TagName<!HTMLStyleElement>} */
goog.dom.TagName.STYLE = new goog.dom.TagName('STYLE');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.SUB = new goog.dom.TagName('SUB');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.SUMMARY = new goog.dom.TagName('SUMMARY');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.SUP = new goog.dom.TagName('SUP');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.SVG = new goog.dom.TagName('SVG');
/** @type {!goog.dom.TagName<!HTMLTableElement>} */
goog.dom.TagName.TABLE = new goog.dom.TagName('TABLE');
/** @type {!goog.dom.TagName<!HTMLTableSectionElement>} */
goog.dom.TagName.TBODY = new goog.dom.TagName('TBODY');
/** @type {!goog.dom.TagName<!HTMLTableCellElement>} */
goog.dom.TagName.TD = new goog.dom.TagName('TD');
/** @type {!goog.dom.TagName<!HTMLTemplateElement>} */
goog.dom.TagName.TEMPLATE = new goog.dom.TagName('TEMPLATE');
/** @type {!goog.dom.TagName<!HTMLTextAreaElement>} */
goog.dom.TagName.TEXTAREA = new goog.dom.TagName('TEXTAREA');
/** @type {!goog.dom.TagName<!HTMLTableSectionElement>} */
goog.dom.TagName.TFOOT = new goog.dom.TagName('TFOOT');
/** @type {!goog.dom.TagName<!HTMLTableCellElement>} */
goog.dom.TagName.TH = new goog.dom.TagName('TH');
/** @type {!goog.dom.TagName<!HTMLTableSectionElement>} */
goog.dom.TagName.THEAD = new goog.dom.TagName('THEAD');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.TIME = new goog.dom.TagName('TIME');
/** @type {!goog.dom.TagName<!HTMLTitleElement>} */
goog.dom.TagName.TITLE = new goog.dom.TagName('TITLE');
/** @type {!goog.dom.TagName<!HTMLTableRowElement>} */
goog.dom.TagName.TR = new goog.dom.TagName('TR');
/** @type {!goog.dom.TagName<!HTMLTrackElement>} */
goog.dom.TagName.TRACK = new goog.dom.TagName('TRACK');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.TT = new goog.dom.TagName('TT');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.U = new goog.dom.TagName('U');
/** @type {!goog.dom.TagName<!HTMLUListElement>} */
goog.dom.TagName.UL = new goog.dom.TagName('UL');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.VAR = new goog.dom.TagName('VAR');
/** @type {!goog.dom.TagName<!HTMLVideoElement>} */
goog.dom.TagName.VIDEO = new goog.dom.TagName('VIDEO');
/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
goog.dom.TagName.WBR = new goog.dom.TagName('WBR');

View File

@ -0,0 +1,41 @@
// Copyright 2014 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Utilities for HTML element tag names.
*/
goog.provide('goog.dom.tags');
goog.require('goog.object');
/**
* The void elements specified by
* http://www.w3.org/TR/html-markup/syntax.html#void-elements.
* @const @private {!Object<string, boolean>}
*/
goog.dom.tags.VOID_TAGS_ = goog.object.createSet(
'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input',
'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr');
/**
* Checks whether the tag is void (with no contents allowed and no legal end
* tag), for example 'br'.
* @param {string} tagName The tag name in lower case.
* @return {boolean}
*/
goog.dom.tags.isVoidTag = function(tagName) {
return goog.dom.tags.VOID_TAGS_[tagName] === true;
};

View File

@ -0,0 +1,97 @@
// Copyright 2012 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Vendor prefix getters.
*/
goog.provide('goog.dom.vendor');
goog.require('goog.string');
goog.require('goog.userAgent');
/**
* Returns the JS vendor prefix used in CSS properties. Different vendors
* use different methods of changing the case of the property names.
*
* @return {?string} The JS vendor prefix or null if there is none.
*/
goog.dom.vendor.getVendorJsPrefix = function() {
if (goog.userAgent.WEBKIT) {
return 'Webkit';
} else if (goog.userAgent.GECKO) {
return 'Moz';
} else if (goog.userAgent.IE) {
return 'ms';
} else if (goog.userAgent.OPERA) {
return 'O';
}
return null;
};
/**
* Returns the vendor prefix used in CSS properties.
*
* @return {?string} The vendor prefix or null if there is none.
*/
goog.dom.vendor.getVendorPrefix = function() {
if (goog.userAgent.WEBKIT) {
return '-webkit';
} else if (goog.userAgent.GECKO) {
return '-moz';
} else if (goog.userAgent.IE) {
return '-ms';
} else if (goog.userAgent.OPERA) {
return '-o';
}
return null;
};
/**
* @param {string} propertyName A property name.
* @param {!Object=} opt_object If provided, we verify if the property exists in
* the object.
* @return {?string} A vendor prefixed property name, or null if it does not
* exist.
*/
goog.dom.vendor.getPrefixedPropertyName = function(propertyName, opt_object) {
// We first check for a non-prefixed property, if available.
if (opt_object && propertyName in opt_object) {
return propertyName;
}
var prefix = goog.dom.vendor.getVendorJsPrefix();
if (prefix) {
prefix = prefix.toLowerCase();
var prefixedPropertyName = prefix + goog.string.toTitleCase(propertyName);
return (!goog.isDef(opt_object) || prefixedPropertyName in opt_object) ?
prefixedPropertyName :
null;
}
return null;
};
/**
* @param {string} eventType An event type.
* @return {string} A lower-cased vendor prefixed event type.
*/
goog.dom.vendor.getPrefixedEventType = function(eventType) {
var prefix = goog.dom.vendor.getVendorJsPrefix() || '';
return (prefix + eventType).toLowerCase();
};

View File

@ -0,0 +1,409 @@
// Copyright 2005 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A patched, standardized event object for browser events.
*
* <pre>
* The patched event object contains the following members:
* - type {string} Event type, e.g. 'click'
* - target {Object} The element that actually triggered the event
* - currentTarget {Object} The element the listener is attached to
* - relatedTarget {Object} For mouseover and mouseout, the previous object
* - offsetX {number} X-coordinate relative to target
* - offsetY {number} Y-coordinate relative to target
* - clientX {number} X-coordinate relative to viewport
* - clientY {number} Y-coordinate relative to viewport
* - screenX {number} X-coordinate relative to the edge of the screen
* - screenY {number} Y-coordinate relative to the edge of the screen
* - button {number} Mouse button. Use isButton() to test.
* - keyCode {number} Key-code
* - ctrlKey {boolean} Was ctrl key depressed
* - altKey {boolean} Was alt key depressed
* - shiftKey {boolean} Was shift key depressed
* - metaKey {boolean} Was meta key depressed
* - defaultPrevented {boolean} Whether the default action has been prevented
* - state {Object} History state object
*
* NOTE: The keyCode member contains the raw browser keyCode. For normalized
* key and character code use {@link goog.events.KeyHandler}.
* </pre>
*
* @author arv@google.com (Erik Arvidsson)
*/
goog.provide('goog.events.BrowserEvent');
goog.provide('goog.events.BrowserEvent.MouseButton');
goog.require('goog.events.BrowserFeature');
goog.require('goog.events.Event');
goog.require('goog.events.EventType');
goog.require('goog.reflect');
goog.require('goog.userAgent');
/**
* Accepts a browser event object and creates a patched, cross browser event
* object.
* The content of this object will not be initialized if no event object is
* provided. If this is the case, init() needs to be invoked separately.
* @param {Event=} opt_e Browser event object.
* @param {EventTarget=} opt_currentTarget Current target for event.
* @constructor
* @extends {goog.events.Event}
*/
goog.events.BrowserEvent = function(opt_e, opt_currentTarget) {
goog.events.BrowserEvent.base(this, 'constructor', opt_e ? opt_e.type : '');
/**
* Target that fired the event.
* @override
* @type {Node}
*/
this.target = null;
/**
* Node that had the listener attached.
* @override
* @type {Node|undefined}
*/
this.currentTarget = null;
/**
* For mouseover and mouseout events, the related object for the event.
* @type {Node}
*/
this.relatedTarget = null;
/**
* X-coordinate relative to target.
* @type {number}
*/
this.offsetX = 0;
/**
* Y-coordinate relative to target.
* @type {number}
*/
this.offsetY = 0;
/**
* X-coordinate relative to the window.
* @type {number}
*/
this.clientX = 0;
/**
* Y-coordinate relative to the window.
* @type {number}
*/
this.clientY = 0;
/**
* X-coordinate relative to the monitor.
* @type {number}
*/
this.screenX = 0;
/**
* Y-coordinate relative to the monitor.
* @type {number}
*/
this.screenY = 0;
/**
* Which mouse button was pressed.
* @type {number}
*/
this.button = 0;
/**
* Key of key press.
* @type {string}
*/
this.key = '';
/**
* Keycode of key press.
* @type {number}
*/
this.keyCode = 0;
/**
* Keycode of key press.
* @type {number}
*/
this.charCode = 0;
/**
* Whether control was pressed at time of event.
* @type {boolean}
*/
this.ctrlKey = false;
/**
* Whether alt was pressed at time of event.
* @type {boolean}
*/
this.altKey = false;
/**
* Whether shift was pressed at time of event.
* @type {boolean}
*/
this.shiftKey = false;
/**
* Whether the meta key was pressed at time of event.
* @type {boolean}
*/
this.metaKey = false;
/**
* History state object, only set for PopState events where it's a copy of the
* state object provided to pushState or replaceState.
* @type {Object}
*/
this.state = null;
/**
* Whether the default platform modifier key was pressed at time of event.
* (This is control for all platforms except Mac, where it's Meta.)
* @type {boolean}
*/
this.platformModifierKey = false;
/**
* The browser event object.
* @private {Event}
*/
this.event_ = null;
if (opt_e) {
this.init(opt_e, opt_currentTarget);
}
};
goog.inherits(goog.events.BrowserEvent, goog.events.Event);
/**
* Normalized button constants for the mouse.
* @enum {number}
*/
goog.events.BrowserEvent.MouseButton = {
LEFT: 0,
MIDDLE: 1,
RIGHT: 2
};
/**
* Static data for mapping mouse buttons.
* @type {!Array<number>}
*/
goog.events.BrowserEvent.IEButtonMap = [
1, // LEFT
4, // MIDDLE
2 // RIGHT
];
/**
* Accepts a browser event object and creates a patched, cross browser event
* object.
* @param {Event} e Browser event object.
* @param {EventTarget=} opt_currentTarget Current target for event.
*/
goog.events.BrowserEvent.prototype.init = function(e, opt_currentTarget) {
var type = this.type = e.type;
/**
* On touch devices use the first "changed touch" as the relevant touch.
* @type {Touch}
*/
var relevantTouch = e.changedTouches ? e.changedTouches[0] : null;
// TODO(nicksantos): Change this.target to type EventTarget.
this.target = /** @type {Node} */ (e.target) || e.srcElement;
// TODO(nicksantos): Change this.currentTarget to type EventTarget.
this.currentTarget = /** @type {Node} */ (opt_currentTarget);
var relatedTarget = /** @type {Node} */ (e.relatedTarget);
if (relatedTarget) {
// There's a bug in FireFox where sometimes, relatedTarget will be a
// chrome element, and accessing any property of it will get a permission
// denied exception. See:
// https://bugzilla.mozilla.org/show_bug.cgi?id=497780
if (goog.userAgent.GECKO) {
if (!goog.reflect.canAccessProperty(relatedTarget, 'nodeName')) {
relatedTarget = null;
}
}
// TODO(arv): Use goog.events.EventType when it has been refactored into its
// own file.
} else if (type == goog.events.EventType.MOUSEOVER) {
relatedTarget = e.fromElement;
} else if (type == goog.events.EventType.MOUSEOUT) {
relatedTarget = e.toElement;
}
this.relatedTarget = relatedTarget;
if (!goog.isNull(relevantTouch)) {
this.clientX = relevantTouch.clientX !== undefined ? relevantTouch.clientX :
relevantTouch.pageX;
this.clientY = relevantTouch.clientY !== undefined ? relevantTouch.clientY :
relevantTouch.pageY;
this.screenX = relevantTouch.screenX || 0;
this.screenY = relevantTouch.screenY || 0;
} else {
// Webkit emits a lame warning whenever layerX/layerY is accessed.
// http://code.google.com/p/chromium/issues/detail?id=101733
this.offsetX = (goog.userAgent.WEBKIT || e.offsetX !== undefined) ?
e.offsetX :
e.layerX;
this.offsetY = (goog.userAgent.WEBKIT || e.offsetY !== undefined) ?
e.offsetY :
e.layerY;
this.clientX = e.clientX !== undefined ? e.clientX : e.pageX;
this.clientY = e.clientY !== undefined ? e.clientY : e.pageY;
this.screenX = e.screenX || 0;
this.screenY = e.screenY || 0;
}
this.button = e.button;
this.keyCode = e.keyCode || 0;
this.key = e.key || '';
this.charCode = e.charCode || (type == 'keypress' ? e.keyCode : 0);
this.ctrlKey = e.ctrlKey;
this.altKey = e.altKey;
this.shiftKey = e.shiftKey;
this.metaKey = e.metaKey;
this.platformModifierKey = goog.userAgent.MAC ? e.metaKey : e.ctrlKey;
this.state = e.state;
this.event_ = e;
if (e.defaultPrevented) {
this.preventDefault();
}
};
/**
* Tests to see which button was pressed during the event. This is really only
* useful in IE and Gecko browsers. And in IE, it's only useful for
* mousedown/mouseup events, because click only fires for the left mouse button.
*
* Safari 2 only reports the left button being clicked, and uses the value '1'
* instead of 0. Opera only reports a mousedown event for the middle button, and
* no mouse events for the right button. Opera has default behavior for left and
* middle click that can only be overridden via a configuration setting.
*
* There's a nice table of this mess at http://www.unixpapa.com/js/mouse.html.
*
* @param {goog.events.BrowserEvent.MouseButton} button The button
* to test for.
* @return {boolean} True if button was pressed.
*/
goog.events.BrowserEvent.prototype.isButton = function(button) {
if (!goog.events.BrowserFeature.HAS_W3C_BUTTON) {
if (this.type == 'click') {
return button == goog.events.BrowserEvent.MouseButton.LEFT;
} else {
return !!(
this.event_.button & goog.events.BrowserEvent.IEButtonMap[button]);
}
} else {
return this.event_.button == button;
}
};
/**
* Whether this has an "action"-producing mouse button.
*
* By definition, this includes left-click on windows/linux, and left-click
* without the ctrl key on Macs.
*
* @return {boolean} The result.
*/
goog.events.BrowserEvent.prototype.isMouseActionButton = function() {
// Webkit does not ctrl+click to be a right-click, so we
// normalize it to behave like Gecko and Opera.
return this.isButton(goog.events.BrowserEvent.MouseButton.LEFT) &&
!(goog.userAgent.WEBKIT && goog.userAgent.MAC && this.ctrlKey);
};
/**
* @override
*/
goog.events.BrowserEvent.prototype.stopPropagation = function() {
goog.events.BrowserEvent.superClass_.stopPropagation.call(this);
if (this.event_.stopPropagation) {
this.event_.stopPropagation();
} else {
this.event_.cancelBubble = true;
}
};
/**
* @override
*/
goog.events.BrowserEvent.prototype.preventDefault = function() {
goog.events.BrowserEvent.superClass_.preventDefault.call(this);
var be = this.event_;
if (!be.preventDefault) {
be.returnValue = false;
if (goog.events.BrowserFeature.SET_KEY_CODE_TO_PREVENT_DEFAULT) {
try {
// Most keys can be prevented using returnValue. Some special keys
// require setting the keyCode to -1 as well:
//
// In IE7:
// F3, F5, F10, F11, Ctrl+P, Crtl+O, Ctrl+F (these are taken from IE6)
//
// In IE8:
// Ctrl+P, Crtl+O, Ctrl+F (F1-F12 cannot be stopped through the event)
//
// We therefore do this for all function keys as well as when Ctrl key
// is pressed.
var VK_F1 = 112;
var VK_F12 = 123;
if (be.ctrlKey || be.keyCode >= VK_F1 && be.keyCode <= VK_F12) {
be.keyCode = -1;
}
} catch (ex) {
// IE throws an 'access denied' exception when trying to change
// keyCode in some situations (e.g. srcElement is input[type=file],
// or srcElement is an anchor tag rewritten by parent's innerHTML).
// Do nothing in this case.
}
}
} else {
be.preventDefault();
}
};
/**
* @return {Event} The underlying browser event object.
*/
goog.events.BrowserEvent.prototype.getBrowserEvent = function() {
return this.event_;
};

View File

@ -0,0 +1,122 @@
// Copyright 2010 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Browser capability checks for the events package.
*
*/
goog.provide('goog.events.BrowserFeature');
goog.require('goog.userAgent');
goog.scope(function() {
/**
* Enum of browser capabilities.
* @enum {boolean}
*/
goog.events.BrowserFeature = {
/**
* Whether the button attribute of the event is W3C compliant. False in
* Internet Explorer prior to version 9; document-version dependent.
*/
HAS_W3C_BUTTON:
!goog.userAgent.IE || goog.userAgent.isDocumentModeOrHigher(9),
/**
* Whether the browser supports full W3C event model.
*/
HAS_W3C_EVENT_SUPPORT:
!goog.userAgent.IE || goog.userAgent.isDocumentModeOrHigher(9),
/**
* To prevent default in IE7-8 for certain keydown events we need set the
* keyCode to -1.
*/
SET_KEY_CODE_TO_PREVENT_DEFAULT:
goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9'),
/**
* Whether the {@code navigator.onLine} property is supported.
*/
HAS_NAVIGATOR_ONLINE_PROPERTY:
!goog.userAgent.WEBKIT || goog.userAgent.isVersionOrHigher('528'),
/**
* Whether HTML5 network online/offline events are supported.
*/
HAS_HTML5_NETWORK_EVENT_SUPPORT:
goog.userAgent.GECKO && goog.userAgent.isVersionOrHigher('1.9b') ||
goog.userAgent.IE && goog.userAgent.isVersionOrHigher('8') ||
goog.userAgent.OPERA && goog.userAgent.isVersionOrHigher('9.5') ||
goog.userAgent.WEBKIT && goog.userAgent.isVersionOrHigher('528'),
/**
* Whether HTML5 network events fire on document.body, or otherwise the
* window.
*/
HTML5_NETWORK_EVENTS_FIRE_ON_BODY:
goog.userAgent.GECKO && !goog.userAgent.isVersionOrHigher('8') ||
goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9'),
/**
* Whether touch is enabled in the browser.
*/
TOUCH_ENABLED:
('ontouchstart' in goog.global ||
!!(goog.global['document'] && document.documentElement &&
'ontouchstart' in document.documentElement) ||
// IE10 uses non-standard touch events, so it has a different check.
!!(goog.global['navigator'] &&
goog.global['navigator']['msMaxTouchPoints'])),
/**
* Whether addEventListener supports {passive: true}.
* https://developers.google.com/web/updates/2016/06/passive-event-listeners
*/
PASSIVE_EVENTS: purify(function() {
// If we're in a web worker or other custom environment, we can't tell.
if (!goog.global.addEventListener || !Object.defineProperty) { // IE 8
return false;
}
var passive = false;
var options = Object.defineProperty({}, 'passive', {
get: function() {
passive = true;
}
});
goog.global.addEventListener('test', goog.nullFunction, options);
goog.global.removeEventListener('test', goog.nullFunction, options);
return passive;
})
};
/**
* Tricks Closure Compiler into believing that a function is pure. The compiler
* assumes that any `valueOf` function is pure, without analyzing its contents.
*
* @param {function(): T} fn
* @return {T}
* @template T
*/
function purify(fn) {
return ({valueOf: fn}).valueOf();
}
}); // goog.scope

View File

@ -0,0 +1,143 @@
// Copyright 2005 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A base class for event objects.
*
*/
goog.provide('goog.events.Event');
goog.provide('goog.events.EventLike');
/**
* goog.events.Event no longer depends on goog.Disposable. Keep requiring
* goog.Disposable here to not break projects which assume this dependency.
* @suppress {extraRequire}
*/
goog.require('goog.Disposable');
goog.require('goog.events.EventId');
/**
* A typedef for event like objects that are dispatchable via the
* goog.events.dispatchEvent function. strings are treated as the type for a
* goog.events.Event. Objects are treated as an extension of a new
* goog.events.Event with the type property of the object being used as the type
* of the Event.
* @typedef {string|Object|goog.events.Event|goog.events.EventId}
*/
goog.events.EventLike;
/**
* A base class for event objects, so that they can support preventDefault and
* stopPropagation.
*
* @suppress {underscore} Several properties on this class are technically
* public, but referencing these properties outside this package is strongly
* discouraged.
*
* @param {string|!goog.events.EventId} type Event Type.
* @param {Object=} opt_target Reference to the object that is the target of
* this event. It has to implement the {@code EventTarget} interface
* declared at {@link http://developer.mozilla.org/en/DOM/EventTarget}.
* @constructor
*/
goog.events.Event = function(type, opt_target) {
/**
* Event type.
* @type {string}
*/
this.type = type instanceof goog.events.EventId ? String(type) : type;
/**
* TODO(tbreisacher): The type should probably be
* EventTarget|goog.events.EventTarget.
*
* Target of the event.
* @type {Object|undefined}
*/
this.target = opt_target;
/**
* Object that had the listener attached.
* @type {Object|undefined}
*/
this.currentTarget = this.target;
/**
* Whether to cancel the event in internal capture/bubble processing for IE.
* @type {boolean}
* @public
*/
this.propagationStopped_ = false;
/**
* Whether the default action has been prevented.
* This is a property to match the W3C specification at
* {@link http://www.w3.org/TR/DOM-Level-3-Events/
* #events-event-type-defaultPrevented}.
* Must be treated as read-only outside the class.
* @type {boolean}
*/
this.defaultPrevented = false;
/**
* Return value for in internal capture/bubble processing for IE.
* @type {boolean}
* @public
*/
this.returnValue_ = true;
};
/**
* Stops event propagation.
*/
goog.events.Event.prototype.stopPropagation = function() {
this.propagationStopped_ = true;
};
/**
* Prevents the default action, for example a link redirecting to a url.
*/
goog.events.Event.prototype.preventDefault = function() {
this.defaultPrevented = true;
this.returnValue_ = false;
};
/**
* Stops the propagation of the event. It is equivalent to
* {@code e.stopPropagation()}, but can be used as the callback argument of
* {@link goog.events.listen} without declaring another function.
* @param {!goog.events.Event} e An event.
*/
goog.events.Event.stopPropagation = function(e) {
e.stopPropagation();
};
/**
* Prevents the default action. It is equivalent to
* {@code e.preventDefault()}, but can be used as the callback argument of
* {@link goog.events.listen} without declaring another function.
* @param {!goog.events.Event} e An event.
*/
goog.events.Event.preventDefault = function(e) {
e.preventDefault();
};

View File

@ -0,0 +1,46 @@
// Copyright 2013 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
goog.provide('goog.events.EventId');
/**
* A templated class that is used when registering for events. Typical usage:
*
* /** @type {goog.events.EventId<MyEventObj>} *\
* var myEventId = new goog.events.EventId(
* goog.events.getUniqueId(('someEvent'));
*
* // No need to cast or declare here since the compiler knows the
* // correct type of 'evt' (MyEventObj).
* something.listen(myEventId, function(evt) {});
*
* @param {string} eventId
* @template T
* @constructor
* @struct
* @final
*/
goog.events.EventId = function(eventId) {
/** @const */ this.id = eventId;
};
/**
* @override
*/
goog.events.EventId.prototype.toString = function() {
return this.id;
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,295 @@
// Copyright 2010 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Event Types.
*
* @author arv@google.com (Erik Arvidsson)
*/
goog.provide('goog.events.EventType');
goog.require('goog.userAgent');
/**
* Returns a prefixed event name for the current browser.
* @param {string} eventName The name of the event.
* @return {string} The prefixed event name.
* @suppress {missingRequire|missingProvide}
* @private
*/
goog.events.getVendorPrefixedName_ = function(eventName) {
return goog.userAgent.WEBKIT ?
'webkit' + eventName :
(goog.userAgent.OPERA ? 'o' + eventName.toLowerCase() :
eventName.toLowerCase());
};
/**
* Constants for event names.
* @enum {string}
*/
goog.events.EventType = {
// Mouse events
CLICK: 'click',
RIGHTCLICK: 'rightclick',
DBLCLICK: 'dblclick',
MOUSEDOWN: 'mousedown',
MOUSEUP: 'mouseup',
MOUSEOVER: 'mouseover',
MOUSEOUT: 'mouseout',
MOUSEMOVE: 'mousemove',
MOUSEENTER: 'mouseenter',
MOUSELEAVE: 'mouseleave',
// Selection events.
// https://www.w3.org/TR/selection-api/
SELECTIONCHANGE: 'selectionchange',
SELECTSTART: 'selectstart', // IE, Safari, Chrome
// Wheel events
// http://www.w3.org/TR/DOM-Level-3-Events/#events-wheelevents
WHEEL: 'wheel',
// Key events
KEYPRESS: 'keypress',
KEYDOWN: 'keydown',
KEYUP: 'keyup',
// Focus
BLUR: 'blur',
FOCUS: 'focus',
DEACTIVATE: 'deactivate', // IE only
// NOTE: The following two events are not stable in cross-browser usage.
// WebKit and Opera implement DOMFocusIn/Out.
// IE implements focusin/out.
// Gecko implements neither see bug at
// https://bugzilla.mozilla.org/show_bug.cgi?id=396927.
// The DOM Events Level 3 Draft deprecates DOMFocusIn in favor of focusin:
// http://dev.w3.org/2006/webapi/DOM-Level-3-Events/html/DOM3-Events.html
// You can use FOCUS in Capture phase until implementations converge.
FOCUSIN: goog.userAgent.IE ? 'focusin' : 'DOMFocusIn',
FOCUSOUT: goog.userAgent.IE ? 'focusout' : 'DOMFocusOut',
// Forms
CHANGE: 'change',
RESET: 'reset',
SELECT: 'select',
SUBMIT: 'submit',
INPUT: 'input',
PROPERTYCHANGE: 'propertychange', // IE only
// Drag and drop
DRAGSTART: 'dragstart',
DRAG: 'drag',
DRAGENTER: 'dragenter',
DRAGOVER: 'dragover',
DRAGLEAVE: 'dragleave',
DROP: 'drop',
DRAGEND: 'dragend',
// Touch events
// Note that other touch events exist, but we should follow the W3C list here.
// http://www.w3.org/TR/touch-events/#list-of-touchevent-types
TOUCHSTART: 'touchstart',
TOUCHMOVE: 'touchmove',
TOUCHEND: 'touchend',
TOUCHCANCEL: 'touchcancel',
// Misc
BEFOREUNLOAD: 'beforeunload',
CONSOLEMESSAGE: 'consolemessage',
CONTEXTMENU: 'contextmenu',
DEVICEMOTION: 'devicemotion',
DEVICEORIENTATION: 'deviceorientation',
DOMCONTENTLOADED: 'DOMContentLoaded',
ERROR: 'error',
HELP: 'help',
LOAD: 'load',
LOSECAPTURE: 'losecapture',
ORIENTATIONCHANGE: 'orientationchange',
READYSTATECHANGE: 'readystatechange',
RESIZE: 'resize',
SCROLL: 'scroll',
UNLOAD: 'unload',
// Media events
CANPLAY: 'canplay',
CANPLAYTHROUGH: 'canplaythrough',
DURATIONCHANGE: 'durationchange',
EMPTIED: 'emptied',
ENDED: 'ended',
LOADEDDATA: 'loadeddata',
LOADEDMETADATA: 'loadedmetadata',
PAUSE: 'pause',
PLAY: 'play',
PLAYING: 'playing',
RATECHANGE: 'ratechange',
SEEKED: 'seeked',
SEEKING: 'seeking',
STALLED: 'stalled',
SUSPEND: 'suspend',
TIMEUPDATE: 'timeupdate',
VOLUMECHANGE: 'volumechange',
WAITING: 'waiting',
// Media Source Extensions events
// https://www.w3.org/TR/media-source/#mediasource-events
SOURCEOPEN: 'sourceopen',
SOURCEENDED: 'sourceended',
SOURCECLOSED: 'sourceclosed',
// https://www.w3.org/TR/media-source/#sourcebuffer-events
ABORT: 'abort',
UPDATE: 'update',
UPDATESTART: 'updatestart',
UPDATEEND: 'updateend',
// HTML 5 History events
// See http://www.w3.org/TR/html5/browsers.html#event-definitions-0
HASHCHANGE: 'hashchange',
PAGEHIDE: 'pagehide',
PAGESHOW: 'pageshow',
POPSTATE: 'popstate',
// Copy and Paste
// Support is limited. Make sure it works on your favorite browser
// before using.
// http://www.quirksmode.org/dom/events/cutcopypaste.html
COPY: 'copy',
PASTE: 'paste',
CUT: 'cut',
BEFORECOPY: 'beforecopy',
BEFORECUT: 'beforecut',
BEFOREPASTE: 'beforepaste',
// HTML5 online/offline events.
// http://www.w3.org/TR/offline-webapps/#related
ONLINE: 'online',
OFFLINE: 'offline',
// HTML 5 worker events
MESSAGE: 'message',
CONNECT: 'connect',
// Service Worker Events - ServiceWorkerGlobalScope context
// See https://w3c.github.io/ServiceWorker/#execution-context-events
// Note: message event defined in worker events section
INSTALL: 'install',
ACTIVATE: 'activate',
FETCH: 'fetch',
FOREIGNFETCH: 'foreignfetch',
MESSAGEERROR: 'messageerror',
// Service Worker Events - Document context
// See https://w3c.github.io/ServiceWorker/#document-context-events
STATECHANGE: 'statechange',
UPDATEFOUND: 'updatefound',
CONTROLLERCHANGE: 'controllerchange',
// CSS animation events.
/** @suppress {missingRequire} */
ANIMATIONSTART: goog.events.getVendorPrefixedName_('AnimationStart'),
/** @suppress {missingRequire} */
ANIMATIONEND: goog.events.getVendorPrefixedName_('AnimationEnd'),
/** @suppress {missingRequire} */
ANIMATIONITERATION: goog.events.getVendorPrefixedName_('AnimationIteration'),
// CSS transition events. Based on the browser support described at:
// https://developer.mozilla.org/en/css/css_transitions#Browser_compatibility
/** @suppress {missingRequire} */
TRANSITIONEND: goog.events.getVendorPrefixedName_('TransitionEnd'),
// W3C Pointer Events
// http://www.w3.org/TR/pointerevents/
POINTERDOWN: 'pointerdown',
POINTERUP: 'pointerup',
POINTERCANCEL: 'pointercancel',
POINTERMOVE: 'pointermove',
POINTEROVER: 'pointerover',
POINTEROUT: 'pointerout',
POINTERENTER: 'pointerenter',
POINTERLEAVE: 'pointerleave',
GOTPOINTERCAPTURE: 'gotpointercapture',
LOSTPOINTERCAPTURE: 'lostpointercapture',
// IE specific events.
// See http://msdn.microsoft.com/en-us/library/ie/hh772103(v=vs.85).aspx
// Note: these events will be supplanted in IE11.
MSGESTURECHANGE: 'MSGestureChange',
MSGESTUREEND: 'MSGestureEnd',
MSGESTUREHOLD: 'MSGestureHold',
MSGESTURESTART: 'MSGestureStart',
MSGESTURETAP: 'MSGestureTap',
MSGOTPOINTERCAPTURE: 'MSGotPointerCapture',
MSINERTIASTART: 'MSInertiaStart',
MSLOSTPOINTERCAPTURE: 'MSLostPointerCapture',
MSPOINTERCANCEL: 'MSPointerCancel',
MSPOINTERDOWN: 'MSPointerDown',
MSPOINTERENTER: 'MSPointerEnter',
MSPOINTERHOVER: 'MSPointerHover',
MSPOINTERLEAVE: 'MSPointerLeave',
MSPOINTERMOVE: 'MSPointerMove',
MSPOINTEROUT: 'MSPointerOut',
MSPOINTEROVER: 'MSPointerOver',
MSPOINTERUP: 'MSPointerUp',
// Native IMEs/input tools events.
TEXT: 'text',
// The textInput event is supported in IE9+, but only in lower case. All other
// browsers use the camel-case event name.
TEXTINPUT: goog.userAgent.IE ? 'textinput' : 'textInput',
COMPOSITIONSTART: 'compositionstart',
COMPOSITIONUPDATE: 'compositionupdate',
COMPOSITIONEND: 'compositionend',
// The beforeinput event is initially only supported in Safari. See
// https://bugs.chromium.org/p/chromium/issues/detail?id=342670 for Chrome
// implementation tracking.
BEFOREINPUT: 'beforeinput',
// Webview tag events
// See http://developer.chrome.com/dev/apps/webview_tag.html
EXIT: 'exit',
LOADABORT: 'loadabort',
LOADCOMMIT: 'loadcommit',
LOADREDIRECT: 'loadredirect',
LOADSTART: 'loadstart',
LOADSTOP: 'loadstop',
RESPONSIVE: 'responsive',
SIZECHANGED: 'sizechanged',
UNRESPONSIVE: 'unresponsive',
// HTML5 Page Visibility API. See details at
// {@code goog.labs.dom.PageVisibilityMonitor}.
VISIBILITYCHANGE: 'visibilitychange',
// LocalStorage event.
STORAGE: 'storage',
// DOM Level 2 mutation events (deprecated).
DOMSUBTREEMODIFIED: 'DOMSubtreeModified',
DOMNODEINSERTED: 'DOMNodeInserted',
DOMNODEREMOVED: 'DOMNodeRemoved',
DOMNODEREMOVEDFROMDOCUMENT: 'DOMNodeRemovedFromDocument',
DOMNODEINSERTEDINTODOCUMENT: 'DOMNodeInsertedIntoDocument',
DOMATTRMODIFIED: 'DOMAttrModified',
DOMCHARACTERDATAMODIFIED: 'DOMCharacterDataModified',
// Print events.
BEFOREPRINT: 'beforeprint',
AFTERPRINT: 'afterprint'
};

View File

@ -0,0 +1,338 @@
// Copyright 2012 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview An interface for a listenable JavaScript object.
* @author chrishenry@google.com (Chris Henry)
*/
goog.provide('goog.events.Listenable');
goog.provide('goog.events.ListenableKey');
/** @suppress {extraRequire} */
goog.require('goog.events.EventId');
goog.forwardDeclare('goog.events.EventLike');
goog.forwardDeclare('goog.events.EventTarget');
/**
* A listenable interface. A listenable is an object with the ability
* to dispatch/broadcast events to "event listeners" registered via
* listen/listenOnce.
*
* The interface allows for an event propagation mechanism similar
* to one offered by native browser event targets, such as
* capture/bubble mechanism, stopping propagation, and preventing
* default actions. Capture/bubble mechanism depends on the ancestor
* tree constructed via {@code #getParentEventTarget}; this tree
* must be directed acyclic graph. The meaning of default action(s)
* in preventDefault is specific to a particular use case.
*
* Implementations that do not support capture/bubble or can not have
* a parent listenable can simply not implement any ability to set the
* parent listenable (and have {@code #getParentEventTarget} return
* null).
*
* Implementation of this class can be used with or independently from
* goog.events.
*
* Implementation must call {@code #addImplementation(implClass)}.
*
* @interface
* @see goog.events
* @see http://www.w3.org/TR/DOM-Level-2-Events/events.html
*/
goog.events.Listenable = function() {};
/**
* An expando property to indicate that an object implements
* goog.events.Listenable.
*
* See addImplementation/isImplementedBy.
*
* @type {string}
* @const
*/
goog.events.Listenable.IMPLEMENTED_BY_PROP =
'closure_listenable_' + ((Math.random() * 1e6) | 0);
/**
* Marks a given class (constructor) as an implementation of
* Listenable, do that we can query that fact at runtime. The class
* must have already implemented the interface.
* @param {!function(new:goog.events.Listenable,...)} cls The class constructor.
* The corresponding class must have already implemented the interface.
*/
goog.events.Listenable.addImplementation = function(cls) {
cls.prototype[goog.events.Listenable.IMPLEMENTED_BY_PROP] = true;
};
/**
* @param {Object} obj The object to check.
* @return {boolean} Whether a given instance implements Listenable. The
* class/superclass of the instance must call addImplementation.
*/
goog.events.Listenable.isImplementedBy = function(obj) {
return !!(obj && obj[goog.events.Listenable.IMPLEMENTED_BY_PROP]);
};
/**
* Adds an event listener. A listener can only be added once to an
* object and if it is added again the key for the listener is
* returned. Note that if the existing listener is a one-off listener
* (registered via listenOnce), it will no longer be a one-off
* listener after a call to listen().
*
* @param {string|!goog.events.EventId<EVENTOBJ>} type The event type id.
* @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener Callback
* method.
* @param {boolean=} opt_useCapture Whether to fire in capture phase
* (defaults to false).
* @param {SCOPE=} opt_listenerScope Object in whose scope to call the
* listener.
* @return {!goog.events.ListenableKey} Unique key for the listener.
* @template SCOPE,EVENTOBJ
*/
goog.events.Listenable.prototype.listen;
/**
* Adds an event listener that is removed automatically after the
* listener fired once.
*
* If an existing listener already exists, listenOnce will do
* nothing. In particular, if the listener was previously registered
* via listen(), listenOnce() will not turn the listener into a
* one-off listener. Similarly, if there is already an existing
* one-off listener, listenOnce does not modify the listeners (it is
* still a once listener).
*
* @param {string|!goog.events.EventId<EVENTOBJ>} type The event type id.
* @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener Callback
* method.
* @param {boolean=} opt_useCapture Whether to fire in capture phase
* (defaults to false).
* @param {SCOPE=} opt_listenerScope Object in whose scope to call the
* listener.
* @return {!goog.events.ListenableKey} Unique key for the listener.
* @template SCOPE,EVENTOBJ
*/
goog.events.Listenable.prototype.listenOnce;
/**
* Removes an event listener which was added with listen() or listenOnce().
*
* @param {string|!goog.events.EventId<EVENTOBJ>} type The event type id.
* @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener Callback
* method.
* @param {boolean=} opt_useCapture Whether to fire in capture phase
* (defaults to false).
* @param {SCOPE=} opt_listenerScope Object in whose scope to call
* the listener.
* @return {boolean} Whether any listener was removed.
* @template SCOPE,EVENTOBJ
*/
goog.events.Listenable.prototype.unlisten;
/**
* Removes an event listener which was added with listen() by the key
* returned by listen().
*
* @param {!goog.events.ListenableKey} key The key returned by
* listen() or listenOnce().
* @return {boolean} Whether any listener was removed.
*/
goog.events.Listenable.prototype.unlistenByKey;
/**
* Dispatches an event (or event like object) and calls all listeners
* listening for events of this type. The type of the event is decided by the
* type property on the event object.
*
* If any of the listeners returns false OR calls preventDefault then this
* function will return false. If one of the capture listeners calls
* stopPropagation, then the bubble listeners won't fire.
*
* @param {goog.events.EventLike} e Event object.
* @return {boolean} If anyone called preventDefault on the event object (or
* if any of the listeners returns false) this will also return false.
*/
goog.events.Listenable.prototype.dispatchEvent;
/**
* Removes all listeners from this listenable. If type is specified,
* it will only remove listeners of the particular type. otherwise all
* registered listeners will be removed.
*
* @param {string=} opt_type Type of event to remove, default is to
* remove all types.
* @return {number} Number of listeners removed.
*/
goog.events.Listenable.prototype.removeAllListeners;
/**
* Returns the parent of this event target to use for capture/bubble
* mechanism.
*
* NOTE(chrishenry): The name reflects the original implementation of
* custom event target ({@code goog.events.EventTarget}). We decided
* that changing the name is not worth it.
*
* @return {goog.events.Listenable} The parent EventTarget or null if
* there is no parent.
*/
goog.events.Listenable.prototype.getParentEventTarget;
/**
* Fires all registered listeners in this listenable for the given
* type and capture mode, passing them the given eventObject. This
* does not perform actual capture/bubble. Only implementors of the
* interface should be using this.
*
* @param {string|!goog.events.EventId<EVENTOBJ>} type The type of the
* listeners to fire.
* @param {boolean} capture The capture mode of the listeners to fire.
* @param {EVENTOBJ} eventObject The event object to fire.
* @return {boolean} Whether all listeners succeeded without
* attempting to prevent default behavior. If any listener returns
* false or called goog.events.Event#preventDefault, this returns
* false.
* @template EVENTOBJ
*/
goog.events.Listenable.prototype.fireListeners;
/**
* Gets all listeners in this listenable for the given type and
* capture mode.
*
* @param {string|!goog.events.EventId} type The type of the listeners to fire.
* @param {boolean} capture The capture mode of the listeners to fire.
* @return {!Array<!goog.events.ListenableKey>} An array of registered
* listeners.
* @template EVENTOBJ
*/
goog.events.Listenable.prototype.getListeners;
/**
* Gets the goog.events.ListenableKey for the event or null if no such
* listener is in use.
*
* @param {string|!goog.events.EventId<EVENTOBJ>} type The name of the event
* without the 'on' prefix.
* @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener The
* listener function to get.
* @param {boolean} capture Whether the listener is a capturing listener.
* @param {SCOPE=} opt_listenerScope Object in whose scope to call the
* listener.
* @return {goog.events.ListenableKey} the found listener or null if not found.
* @template SCOPE,EVENTOBJ
*/
goog.events.Listenable.prototype.getListener;
/**
* Whether there is any active listeners matching the specified
* signature. If either the type or capture parameters are
* unspecified, the function will match on the remaining criteria.
*
* @param {string|!goog.events.EventId<EVENTOBJ>=} opt_type Event type.
* @param {boolean=} opt_capture Whether to check for capture or bubble
* listeners.
* @return {boolean} Whether there is any active listeners matching
* the requested type and/or capture phase.
* @template EVENTOBJ
*/
goog.events.Listenable.prototype.hasListener;
/**
* An interface that describes a single registered listener.
* @interface
*/
goog.events.ListenableKey = function() {};
/**
* Counter used to create a unique key
* @type {number}
* @private
*/
goog.events.ListenableKey.counter_ = 0;
/**
* Reserves a key to be used for ListenableKey#key field.
* @return {number} A number to be used to fill ListenableKey#key
* field.
*/
goog.events.ListenableKey.reserveKey = function() {
return ++goog.events.ListenableKey.counter_;
};
/**
* The source event target.
* @type {Object|goog.events.Listenable|goog.events.EventTarget}
*/
goog.events.ListenableKey.prototype.src;
/**
* The event type the listener is listening to.
* @type {string}
*/
goog.events.ListenableKey.prototype.type;
/**
* The listener function.
* @type {function(?):?|{handleEvent:function(?):?}|null}
*/
goog.events.ListenableKey.prototype.listener;
/**
* Whether the listener works on capture phase.
* @type {boolean}
*/
goog.events.ListenableKey.prototype.capture;
/**
* The 'this' object for the listener function's scope.
* @type {Object|undefined}
*/
goog.events.ListenableKey.prototype.handler;
/**
* A globally unique number to identify the key.
* @type {number}
*/
goog.events.ListenableKey.prototype.key;

View File

@ -0,0 +1,128 @@
// Copyright 2005 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Listener object.
* @see ../demos/events.html
*/
goog.provide('goog.events.Listener');
goog.require('goog.events.ListenableKey');
/**
* Simple class that stores information about a listener
* @param {function(?):?} listener Callback function.
* @param {Function} proxy Wrapper for the listener that patches the event.
* @param {EventTarget|goog.events.Listenable} src Source object for
* the event.
* @param {string} type Event type.
* @param {boolean} capture Whether in capture or bubble phase.
* @param {Object=} opt_handler Object in whose context to execute the callback.
* @implements {goog.events.ListenableKey}
* @constructor
*/
goog.events.Listener = function(
listener, proxy, src, type, capture, opt_handler) {
if (goog.events.Listener.ENABLE_MONITORING) {
this.creationStack = new Error().stack;
}
/** @override */
this.listener = listener;
/**
* A wrapper over the original listener. This is used solely to
* handle native browser events (it is used to simulate the capture
* phase and to patch the event object).
* @type {Function}
*/
this.proxy = proxy;
/**
* Object or node that callback is listening to
* @type {EventTarget|goog.events.Listenable}
*/
this.src = src;
/**
* The event type.
* @const {string}
*/
this.type = type;
/**
* Whether the listener is being called in the capture or bubble phase
* @const {boolean}
*/
this.capture = !!capture;
/**
* Optional object whose context to execute the listener in
* @type {Object|undefined}
*/
this.handler = opt_handler;
/**
* The key of the listener.
* @const {number}
* @override
*/
this.key = goog.events.ListenableKey.reserveKey();
/**
* Whether to remove the listener after it has been called.
* @type {boolean}
*/
this.callOnce = false;
/**
* Whether the listener has been removed.
* @type {boolean}
*/
this.removed = false;
};
/**
* @define {boolean} Whether to enable the monitoring of the
* goog.events.Listener instances. Switching on the monitoring is only
* recommended for debugging because it has a significant impact on
* performance and memory usage. If switched off, the monitoring code
* compiles down to 0 bytes.
*/
goog.define('goog.events.Listener.ENABLE_MONITORING', false);
/**
* If monitoring the goog.events.Listener instances is enabled, stores the
* creation stack trace of the Disposable instance.
* @type {string}
*/
goog.events.Listener.prototype.creationStack;
/**
* Marks this listener as removed. This also remove references held by
* this listener object (such as listener and event source).
*/
goog.events.Listener.prototype.markAsRemoved = function() {
this.removed = true;
this.listener = null;
this.proxy = null;
this.src = null;
this.handler = null;
};

View File

@ -0,0 +1,307 @@
// Copyright 2013 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A map of listeners that provides utility functions to
* deal with listeners on an event target. Used by
* {@code goog.events.EventTarget}.
*
* WARNING: Do not use this class from outside goog.events package.
*
* @visibility {//closure/goog/bin/sizetests:__pkg__}
* @visibility {//closure/goog:__pkg__}
* @visibility {//closure/goog/events:__pkg__}
* @visibility {//closure/goog/labs/events:__pkg__}
*/
goog.provide('goog.events.ListenerMap');
goog.require('goog.array');
goog.require('goog.events.Listener');
goog.require('goog.object');
/**
* Creates a new listener map.
* @param {EventTarget|goog.events.Listenable} src The src object.
* @constructor
* @final
*/
goog.events.ListenerMap = function(src) {
/** @type {EventTarget|goog.events.Listenable} */
this.src = src;
/**
* Maps of event type to an array of listeners.
* @type {!Object<string, !Array<!goog.events.Listener>>}
*/
this.listeners = {};
/**
* The count of types in this map that have registered listeners.
* @private {number}
*/
this.typeCount_ = 0;
};
/**
* @return {number} The count of event types in this map that actually
* have registered listeners.
*/
goog.events.ListenerMap.prototype.getTypeCount = function() {
return this.typeCount_;
};
/**
* @return {number} Total number of registered listeners.
*/
goog.events.ListenerMap.prototype.getListenerCount = function() {
var count = 0;
for (var type in this.listeners) {
count += this.listeners[type].length;
}
return count;
};
/**
* Adds an event listener. A listener can only be added once to an
* object and if it is added again the key for the listener is
* returned.
*
* Note that a one-off listener will not change an existing listener,
* if any. On the other hand a normal listener will change existing
* one-off listener to become a normal listener.
*
* @param {string|!goog.events.EventId} type The listener event type.
* @param {!Function} listener This listener callback method.
* @param {boolean} callOnce Whether the listener is a one-off
* listener.
* @param {boolean=} opt_useCapture The capture mode of the listener.
* @param {Object=} opt_listenerScope Object in whose scope to call the
* listener.
* @return {!goog.events.ListenableKey} Unique key for the listener.
*/
goog.events.ListenerMap.prototype.add = function(
type, listener, callOnce, opt_useCapture, opt_listenerScope) {
var typeStr = type.toString();
var listenerArray = this.listeners[typeStr];
if (!listenerArray) {
listenerArray = this.listeners[typeStr] = [];
this.typeCount_++;
}
var listenerObj;
var index = goog.events.ListenerMap.findListenerIndex_(
listenerArray, listener, opt_useCapture, opt_listenerScope);
if (index > -1) {
listenerObj = listenerArray[index];
if (!callOnce) {
// Ensure that, if there is an existing callOnce listener, it is no
// longer a callOnce listener.
listenerObj.callOnce = false;
}
} else {
listenerObj = new goog.events.Listener(
listener, null, this.src, typeStr, !!opt_useCapture, opt_listenerScope);
listenerObj.callOnce = callOnce;
listenerArray.push(listenerObj);
}
return listenerObj;
};
/**
* Removes a matching listener.
* @param {string|!goog.events.EventId} type The listener event type.
* @param {!Function} listener This listener callback method.
* @param {boolean=} opt_useCapture The capture mode of the listener.
* @param {Object=} opt_listenerScope Object in whose scope to call the
* listener.
* @return {boolean} Whether any listener was removed.
*/
goog.events.ListenerMap.prototype.remove = function(
type, listener, opt_useCapture, opt_listenerScope) {
var typeStr = type.toString();
if (!(typeStr in this.listeners)) {
return false;
}
var listenerArray = this.listeners[typeStr];
var index = goog.events.ListenerMap.findListenerIndex_(
listenerArray, listener, opt_useCapture, opt_listenerScope);
if (index > -1) {
var listenerObj = listenerArray[index];
listenerObj.markAsRemoved();
goog.array.removeAt(listenerArray, index);
if (listenerArray.length == 0) {
delete this.listeners[typeStr];
this.typeCount_--;
}
return true;
}
return false;
};
/**
* Removes the given listener object.
* @param {!goog.events.ListenableKey} listener The listener to remove.
* @return {boolean} Whether the listener is removed.
*/
goog.events.ListenerMap.prototype.removeByKey = function(listener) {
var type = listener.type;
if (!(type in this.listeners)) {
return false;
}
var removed = goog.array.remove(this.listeners[type], listener);
if (removed) {
/** @type {!goog.events.Listener} */ (listener).markAsRemoved();
if (this.listeners[type].length == 0) {
delete this.listeners[type];
this.typeCount_--;
}
}
return removed;
};
/**
* Removes all listeners from this map. If opt_type is provided, only
* listeners that match the given type are removed.
* @param {string|!goog.events.EventId=} opt_type Type of event to remove.
* @return {number} Number of listeners removed.
*/
goog.events.ListenerMap.prototype.removeAll = function(opt_type) {
var typeStr = opt_type && opt_type.toString();
var count = 0;
for (var type in this.listeners) {
if (!typeStr || type == typeStr) {
var listenerArray = this.listeners[type];
for (var i = 0; i < listenerArray.length; i++) {
++count;
listenerArray[i].markAsRemoved();
}
delete this.listeners[type];
this.typeCount_--;
}
}
return count;
};
/**
* Gets all listeners that match the given type and capture mode. The
* returned array is a copy (but the listener objects are not).
* @param {string|!goog.events.EventId} type The type of the listeners
* to retrieve.
* @param {boolean} capture The capture mode of the listeners to retrieve.
* @return {!Array<!goog.events.ListenableKey>} An array of matching
* listeners.
*/
goog.events.ListenerMap.prototype.getListeners = function(type, capture) {
var listenerArray = this.listeners[type.toString()];
var rv = [];
if (listenerArray) {
for (var i = 0; i < listenerArray.length; ++i) {
var listenerObj = listenerArray[i];
if (listenerObj.capture == capture) {
rv.push(listenerObj);
}
}
}
return rv;
};
/**
* Gets the goog.events.ListenableKey for the event or null if no such
* listener is in use.
*
* @param {string|!goog.events.EventId} type The type of the listener
* to retrieve.
* @param {!Function} listener The listener function to get.
* @param {boolean} capture Whether the listener is a capturing listener.
* @param {Object=} opt_listenerScope Object in whose scope to call the
* listener.
* @return {goog.events.ListenableKey} the found listener or null if not found.
*/
goog.events.ListenerMap.prototype.getListener = function(
type, listener, capture, opt_listenerScope) {
var listenerArray = this.listeners[type.toString()];
var i = -1;
if (listenerArray) {
i = goog.events.ListenerMap.findListenerIndex_(
listenerArray, listener, capture, opt_listenerScope);
}
return i > -1 ? listenerArray[i] : null;
};
/**
* Whether there is a matching listener. If either the type or capture
* parameters are unspecified, the function will match on the
* remaining criteria.
*
* @param {string|!goog.events.EventId=} opt_type The type of the listener.
* @param {boolean=} opt_capture The capture mode of the listener.
* @return {boolean} Whether there is an active listener matching
* the requested type and/or capture phase.
*/
goog.events.ListenerMap.prototype.hasListener = function(
opt_type, opt_capture) {
var hasType = goog.isDef(opt_type);
var typeStr = hasType ? opt_type.toString() : '';
var hasCapture = goog.isDef(opt_capture);
return goog.object.some(this.listeners, function(listenerArray, type) {
for (var i = 0; i < listenerArray.length; ++i) {
if ((!hasType || listenerArray[i].type == typeStr) &&
(!hasCapture || listenerArray[i].capture == opt_capture)) {
return true;
}
}
return false;
});
};
/**
* Finds the index of a matching goog.events.Listener in the given
* listenerArray.
* @param {!Array<!goog.events.Listener>} listenerArray Array of listener.
* @param {!Function} listener The listener function.
* @param {boolean=} opt_useCapture The capture flag for the listener.
* @param {Object=} opt_listenerScope The listener scope.
* @return {number} The index of the matching listener within the
* listenerArray.
* @private
*/
goog.events.ListenerMap.findListenerIndex_ = function(
listenerArray, listener, opt_useCapture, opt_listenerScope) {
for (var i = 0; i < listenerArray.length; ++i) {
var listenerObj = listenerArray[i];
if (!listenerObj.removed && listenerObj.listener == listener &&
listenerObj.capture == !!opt_useCapture &&
listenerObj.handler == opt_listenerScope) {
return i;
}
}
return -1;
};

View File

@ -0,0 +1,106 @@
// Copyright 2015 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Wrapper for URL and its createObjectUrl and revokeObjectUrl
* methods that are part of the HTML5 File API.
*/
goog.provide('goog.fs.url');
/**
* Creates a blob URL for a blob object.
* Throws an error if the browser does not support Object Urls.
*
* @param {!Blob} blob The object for which to create the URL.
* @return {string} The URL for the object.
*/
goog.fs.url.createObjectUrl = function(blob) {
return goog.fs.url.getUrlObject_().createObjectURL(blob);
};
/**
* Revokes a URL created by {@link goog.fs.url.createObjectUrl}.
* Throws an error if the browser does not support Object Urls.
*
* @param {string} url The URL to revoke.
*/
goog.fs.url.revokeObjectUrl = function(url) {
goog.fs.url.getUrlObject_().revokeObjectURL(url);
};
/**
* @typedef {{createObjectURL: (function(!Blob): string),
* revokeObjectURL: function(string): void}}
*/
goog.fs.url.UrlObject_;
/**
* Get the object that has the createObjectURL and revokeObjectURL functions for
* this browser.
*
* @return {goog.fs.url.UrlObject_} The object for this browser.
* @private
*/
goog.fs.url.getUrlObject_ = function() {
var urlObject = goog.fs.url.findUrlObject_();
if (urlObject != null) {
return urlObject;
} else {
throw Error('This browser doesn\'t seem to support blob URLs');
}
};
/**
* Finds the object that has the createObjectURL and revokeObjectURL functions
* for this browser.
*
* @return {?goog.fs.url.UrlObject_} The object for this browser or null if the
* browser does not support Object Urls.
* @private
*/
goog.fs.url.findUrlObject_ = function() {
// This is what the spec says to do
// http://dev.w3.org/2006/webapi/FileAPI/#dfn-createObjectURL
if (goog.isDef(goog.global.URL) &&
goog.isDef(goog.global.URL.createObjectURL)) {
return /** @type {goog.fs.url.UrlObject_} */ (goog.global.URL);
// This is what Chrome does (as of 10.0.648.6 dev)
} else if (
goog.isDef(goog.global.webkitURL) &&
goog.isDef(goog.global.webkitURL.createObjectURL)) {
return /** @type {goog.fs.url.UrlObject_} */ (goog.global.webkitURL);
// This is what the spec used to say to do
} else if (goog.isDef(goog.global.createObjectURL)) {
return /** @type {goog.fs.url.UrlObject_} */ (goog.global);
} else {
return null;
}
};
/**
* Checks whether this browser supports Object Urls. If not, calls to
* createObjectUrl and revokeObjectUrl will result in an error.
*
* @return {boolean} True if this browser supports Object Urls.
*/
goog.fs.url.browserSupportsObjectUrls = function() {
return goog.fs.url.findUrlObject_() != null;
};

View File

@ -0,0 +1,483 @@
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Utilities for creating functions. Loosely inspired by the
* java classes: http://goo.gl/GM0Hmu and http://goo.gl/6k7nI8.
*
* @author nicksantos@google.com (Nick Santos)
*/
goog.provide('goog.functions');
/**
* Creates a function that always returns the same value.
* @param {T} retValue The value to return.
* @return {function():T} The new function.
* @template T
*/
goog.functions.constant = function(retValue) {
return function() { return retValue; };
};
/**
* Always returns false.
* @type {function(...): boolean}
*/
goog.functions.FALSE = goog.functions.constant(false);
/**
* Always returns true.
* @type {function(...): boolean}
*/
goog.functions.TRUE = goog.functions.constant(true);
/**
* Always returns NULL.
* @type {function(...): null}
*/
goog.functions.NULL = goog.functions.constant(null);
/**
* A simple function that returns the first argument of whatever is passed
* into it.
* @param {T=} opt_returnValue The single value that will be returned.
* @param {...*} var_args Optional trailing arguments. These are ignored.
* @return {T} The first argument passed in, or undefined if nothing was passed.
* @template T
*/
goog.functions.identity = function(opt_returnValue, var_args) {
return opt_returnValue;
};
/**
* Creates a function that always throws an error with the given message.
* @param {string} message The error message.
* @return {!Function} The error-throwing function.
*/
goog.functions.error = function(message) {
return function() { throw Error(message); };
};
/**
* Creates a function that throws the given object.
* @param {*} err An object to be thrown.
* @return {!Function} The error-throwing function.
*/
goog.functions.fail = function(err) {
return function() { throw err; };
};
/**
* Given a function, create a function that keeps opt_numArgs arguments and
* silently discards all additional arguments.
* @param {Function} f The original function.
* @param {number=} opt_numArgs The number of arguments to keep. Defaults to 0.
* @return {!Function} A version of f that only keeps the first opt_numArgs
* arguments.
*/
goog.functions.lock = function(f, opt_numArgs) {
opt_numArgs = opt_numArgs || 0;
return function() {
return f.apply(this, Array.prototype.slice.call(arguments, 0, opt_numArgs));
};
};
/**
* Creates a function that returns its nth argument.
* @param {number} n The position of the return argument.
* @return {!Function} A new function.
*/
goog.functions.nth = function(n) {
return function() { return arguments[n]; };
};
/**
* Like goog.partial(), except that arguments are added after arguments to the
* returned function.
*
* Usage:
* function f(arg1, arg2, arg3, arg4) { ... }
* var g = goog.functions.partialRight(f, arg3, arg4);
* g(arg1, arg2);
*
* @param {!Function} fn A function to partially apply.
* @param {...*} var_args Additional arguments that are partially applied to fn
* at the end.
* @return {!Function} A partially-applied form of the function goog.partial()
* was invoked as a method of.
*/
goog.functions.partialRight = function(fn, var_args) {
var rightArgs = Array.prototype.slice.call(arguments, 1);
return function() {
var newArgs = Array.prototype.slice.call(arguments);
newArgs.push.apply(newArgs, rightArgs);
return fn.apply(this, newArgs);
};
};
/**
* Given a function, create a new function that swallows its return value
* and replaces it with a new one.
* @param {Function} f A function.
* @param {T} retValue A new return value.
* @return {function(...?):T} A new function.
* @template T
*/
goog.functions.withReturnValue = function(f, retValue) {
return goog.functions.sequence(f, goog.functions.constant(retValue));
};
/**
* Creates a function that returns whether its argument equals the given value.
*
* Example:
* var key = goog.object.findKey(obj, goog.functions.equalTo('needle'));
*
* @param {*} value The value to compare to.
* @param {boolean=} opt_useLooseComparison Whether to use a loose (==)
* comparison rather than a strict (===) one. Defaults to false.
* @return {function(*):boolean} The new function.
*/
goog.functions.equalTo = function(value, opt_useLooseComparison) {
return function(other) {
return opt_useLooseComparison ? (value == other) : (value === other);
};
};
/**
* Creates the composition of the functions passed in.
* For example, (goog.functions.compose(f, g))(a) is equivalent to f(g(a)).
* @param {function(...?):T} fn The final function.
* @param {...Function} var_args A list of functions.
* @return {function(...?):T} The composition of all inputs.
* @template T
*/
goog.functions.compose = function(fn, var_args) {
var functions = arguments;
var length = functions.length;
return function() {
var result;
if (length) {
result = functions[length - 1].apply(this, arguments);
}
for (var i = length - 2; i >= 0; i--) {
result = functions[i].call(this, result);
}
return result;
};
};
/**
* Creates a function that calls the functions passed in in sequence, and
* returns the value of the last function. For example,
* (goog.functions.sequence(f, g))(x) is equivalent to f(x),g(x).
* @param {...Function} var_args A list of functions.
* @return {!Function} A function that calls all inputs in sequence.
*/
goog.functions.sequence = function(var_args) {
var functions = arguments;
var length = functions.length;
return function() {
var result;
for (var i = 0; i < length; i++) {
result = functions[i].apply(this, arguments);
}
return result;
};
};
/**
* Creates a function that returns true if each of its components evaluates
* to true. The components are evaluated in order, and the evaluation will be
* short-circuited as soon as a function returns false.
* For example, (goog.functions.and(f, g))(x) is equivalent to f(x) && g(x).
* @param {...Function} var_args A list of functions.
* @return {function(...?):boolean} A function that ANDs its component
* functions.
*/
goog.functions.and = function(var_args) {
var functions = arguments;
var length = functions.length;
return function() {
for (var i = 0; i < length; i++) {
if (!functions[i].apply(this, arguments)) {
return false;
}
}
return true;
};
};
/**
* Creates a function that returns true if any of its components evaluates
* to true. The components are evaluated in order, and the evaluation will be
* short-circuited as soon as a function returns true.
* For example, (goog.functions.or(f, g))(x) is equivalent to f(x) || g(x).
* @param {...Function} var_args A list of functions.
* @return {function(...?):boolean} A function that ORs its component
* functions.
*/
goog.functions.or = function(var_args) {
var functions = arguments;
var length = functions.length;
return function() {
for (var i = 0; i < length; i++) {
if (functions[i].apply(this, arguments)) {
return true;
}
}
return false;
};
};
/**
* Creates a function that returns the Boolean opposite of a provided function.
* For example, (goog.functions.not(f))(x) is equivalent to !f(x).
* @param {!Function} f The original function.
* @return {function(...?):boolean} A function that delegates to f and returns
* opposite.
*/
goog.functions.not = function(f) {
return function() { return !f.apply(this, arguments); };
};
/**
* Generic factory function to construct an object given the constructor
* and the arguments. Intended to be bound to create object factories.
*
* Example:
*
* var factory = goog.partial(goog.functions.create, Class);
*
* @param {function(new:T, ...)} constructor The constructor for the Object.
* @param {...*} var_args The arguments to be passed to the constructor.
* @return {T} A new instance of the class given in {@code constructor}.
* @template T
*/
goog.functions.create = function(constructor, var_args) {
/**
* @constructor
* @final
*/
var temp = function() {};
temp.prototype = constructor.prototype;
// obj will have constructor's prototype in its chain and
// 'obj instanceof constructor' will be true.
var obj = new temp();
// obj is initialized by constructor.
// arguments is only array-like so lacks shift(), but can be used with
// the Array prototype function.
constructor.apply(obj, Array.prototype.slice.call(arguments, 1));
return obj;
};
/**
* @define {boolean} Whether the return value cache should be used.
* This should only be used to disable caches when testing.
*/
goog.define('goog.functions.CACHE_RETURN_VALUE', true);
/**
* Gives a wrapper function that caches the return value of a parameterless
* function when first called.
*
* When called for the first time, the given function is called and its
* return value is cached (thus this is only appropriate for idempotent
* functions). Subsequent calls will return the cached return value. This
* allows the evaluation of expensive functions to be delayed until first used.
*
* To cache the return values of functions with parameters, see goog.memoize.
*
* @param {function():T} fn A function to lazily evaluate.
* @return {function():T} A wrapped version the function.
* @template T
*/
goog.functions.cacheReturnValue = function(fn) {
var called = false;
var value;
return function() {
if (!goog.functions.CACHE_RETURN_VALUE) {
return fn();
}
if (!called) {
value = fn();
called = true;
}
return value;
};
};
/**
* Wraps a function to allow it to be called, at most, once. All
* additional calls are no-ops.
*
* This is particularly useful for initialization functions
* that should be called, at most, once.
*
* @param {function():*} f Function to call.
* @return {function():undefined} Wrapped function.
*/
goog.functions.once = function(f) {
// Keep a reference to the function that we null out when we're done with
// it -- that way, the function can be GC'd when we're done with it.
var inner = f;
return function() {
if (inner) {
var tmp = inner;
inner = null;
tmp();
}
};
};
/**
* Wraps a function to allow it to be called, at most, once per interval
* (specified in milliseconds). If the wrapper function is called N times within
* that interval, only the Nth call will go through.
*
* This is particularly useful for batching up repeated actions where the
* last action should win. This can be used, for example, for refreshing an
* autocomplete pop-up every so often rather than updating with every keystroke,
* since the final text typed by the user is the one that should produce the
* final autocomplete results. For more stateful debouncing with support for
* pausing, resuming, and canceling debounced actions, use {@code
* goog.async.Debouncer}.
*
* @param {function(this:SCOPE, ...?)} f Function to call.
* @param {number} interval Interval over which to debounce. The function will
* only be called after the full interval has elapsed since the last call.
* @param {SCOPE=} opt_scope Object in whose scope to call the function.
* @return {function(...?): undefined} Wrapped function.
* @template SCOPE
*/
goog.functions.debounce = function(f, interval, opt_scope) {
var timeout = 0;
return /** @type {function(...?)} */ (function(var_args) {
goog.global.clearTimeout(timeout);
var args = arguments;
timeout = goog.global.setTimeout(function() {
f.apply(opt_scope, args);
}, interval);
});
};
/**
* Wraps a function to allow it to be called, at most, once per interval
* (specified in milliseconds). If the wrapper function is called N times in
* that interval, both the 1st and the Nth calls will go through.
*
* This is particularly useful for limiting repeated user requests where the
* the last action should win, but you also don't want to wait until the end of
* the interval before sending a request out, as it leads to a perception of
* slowness for the user.
*
* @param {function(this:SCOPE, ...?)} f Function to call.
* @param {number} interval Interval over which to throttle. The function can
* only be called once per interval.
* @param {SCOPE=} opt_scope Object in whose scope to call the function.
* @return {function(...?): undefined} Wrapped function.
* @template SCOPE
*/
goog.functions.throttle = function(f, interval, opt_scope) {
var timeout = 0;
var shouldFire = false;
var args = [];
var handleTimeout = function() {
timeout = 0;
if (shouldFire) {
shouldFire = false;
fire();
}
};
var fire = function() {
timeout = goog.global.setTimeout(handleTimeout, interval);
f.apply(opt_scope, args);
};
return /** @type {function(...?)} */ (function(var_args) {
args = arguments;
if (!timeout) {
fire();
} else {
shouldFire = true;
}
});
};
/**
* Wraps a function to allow it to be called, at most, once per interval
* (specified in milliseconds). If the wrapper function is called N times within
* that interval, only the 1st call will go through.
*
* This is particularly useful for limiting repeated user requests where the
* first request is guaranteed to have all the data required to perform the
* final action, so there's no need to wait until the end of the interval before
* sending the request out.
*
* @param {function(this:SCOPE, ...?)} f Function to call.
* @param {number} interval Interval over which to rate-limit. The function will
* only be called once per interval, and ignored for the remainer of the
* interval.
* @param {SCOPE=} opt_scope Object in whose scope to call the function.
* @return {function(...?): undefined} Wrapped function.
* @template SCOPE
*/
goog.functions.rateLimit = function(f, interval, opt_scope) {
var timeout = 0;
var handleTimeout = function() {
timeout = 0;
};
return /** @type {function(...?)} */ (function(var_args) {
if (!timeout) {
timeout = goog.global.setTimeout(handleTimeout, interval);
f.apply(opt_scope, arguments);
}
});
};

View File

@ -0,0 +1,195 @@
// Copyright 2013 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Transitional utilities to unsafely trust random strings as
* goog.html types. Intended for temporary use when upgrading a library that
* used to accept plain strings to use safe types, but where it's not
* practical to transitively update callers.
*
* IMPORTANT: No new code should use the conversion functions in this file,
* they are intended for refactoring old code to use goog.html types. New code
* should construct goog.html types via their APIs, template systems or
* sanitizers. If thats not possible it should use
* goog.html.uncheckedconversions and undergo security review.
*
* The semantics of the conversions in goog.html.legacyconversions are very
* different from the ones provided by goog.html.uncheckedconversions. The
* latter are for use in code where it has been established through manual
* security review that the value produced by a piece of code will always
* satisfy the SafeHtml contract (e.g., the output of a secure HTML sanitizer).
* In uses of goog.html.legacyconversions, this guarantee is not given -- the
* value in question originates in unreviewed legacy code and there is no
* guarantee that it satisfies the SafeHtml contract.
*
* There are only three valid uses of legacyconversions:
*
* 1. Introducing a goog.html version of a function which currently consumes
* string and passes that string to a DOM API which can execute script - and
* hence cause XSS - like innerHTML. For example, Dialog might expose a
* setContent method which takes a string and sets the innerHTML property of
* an element with it. In this case a setSafeHtmlContent function could be
* added, consuming goog.html.SafeHtml instead of string, and using
* goog.dom.safe.setInnerHtml instead of directly setting innerHTML.
* setContent could then internally use legacyconversions to create a SafeHtml
* from string and pass the SafeHtml to setSafeHtmlContent. In this scenario
* remember to document the use of legacyconversions in the modified setContent
* and consider deprecating it as well.
*
* 2. Automated refactoring of application code which handles HTML as string
* but needs to call a function which only takes goog.html types. For example,
* in the Dialog scenario from (1) an alternative option would be to refactor
* setContent to accept goog.html.SafeHtml instead of string and then refactor
* all current callers to use legacyconversions to pass SafeHtml. This is
* generally preferable to (1) because it keeps the library clean of
* legacyconversions, and makes code sites in application code that are
* potentially vulnerable to XSS more apparent.
*
* 3. Old code which needs to call APIs which consume goog.html types and for
* which it is prohibitively expensive to refactor to use goog.html types.
* Generally, this is code where safety from XSS is either hopeless or
* unimportant.
*
* @visibility {//closure/goog/html:approved_for_legacy_conversion}
* @visibility {//closure/goog/bin/sizetests:__pkg__}
*/
goog.provide('goog.html.legacyconversions');
goog.require('goog.html.SafeHtml');
goog.require('goog.html.SafeScript');
goog.require('goog.html.SafeStyle');
goog.require('goog.html.SafeStyleSheet');
goog.require('goog.html.SafeUrl');
goog.require('goog.html.TrustedResourceUrl');
/**
* Performs an "unchecked conversion" from string to SafeHtml for legacy API
* purposes.
*
* Please read fileoverview documentation before using.
*
* @param {string} html A string to be converted to SafeHtml.
* @return {!goog.html.SafeHtml} The value of html, wrapped in a SafeHtml
* object.
*/
goog.html.legacyconversions.safeHtmlFromString = function(html) {
goog.html.legacyconversions.reportCallback_();
return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
html, null /* dir */);
};
/**
* Performs an "unchecked conversion" from string to SafeScript for legacy API
* purposes.
*
* Please read fileoverview documentation before using.
*
* @param {string} script A string to be converted to SafeScript.
* @return {!goog.html.SafeScript} The value of script, wrapped in a SafeScript
* object.
*/
goog.html.legacyconversions.safeScriptFromString = function(script) {
goog.html.legacyconversions.reportCallback_();
return goog.html.SafeScript.createSafeScriptSecurityPrivateDoNotAccessOrElse(
script);
};
/**
* Performs an "unchecked conversion" from string to SafeStyle for legacy API
* purposes.
*
* Please read fileoverview documentation before using.
*
* @param {string} style A string to be converted to SafeStyle.
* @return {!goog.html.SafeStyle} The value of style, wrapped in a SafeStyle
* object.
*/
goog.html.legacyconversions.safeStyleFromString = function(style) {
goog.html.legacyconversions.reportCallback_();
return goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse(
style);
};
/**
* Performs an "unchecked conversion" from string to SafeStyleSheet for legacy
* API purposes.
*
* Please read fileoverview documentation before using.
*
* @param {string} styleSheet A string to be converted to SafeStyleSheet.
* @return {!goog.html.SafeStyleSheet} The value of style sheet, wrapped in
* a SafeStyleSheet object.
*/
goog.html.legacyconversions.safeStyleSheetFromString = function(styleSheet) {
goog.html.legacyconversions.reportCallback_();
return goog.html.SafeStyleSheet
.createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(styleSheet);
};
/**
* Performs an "unchecked conversion" from string to SafeUrl for legacy API
* purposes.
*
* Please read fileoverview documentation before using.
*
* @param {string} url A string to be converted to SafeUrl.
* @return {!goog.html.SafeUrl} The value of url, wrapped in a SafeUrl
* object.
*/
goog.html.legacyconversions.safeUrlFromString = function(url) {
goog.html.legacyconversions.reportCallback_();
return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);
};
/**
* Performs an "unchecked conversion" from string to TrustedResourceUrl for
* legacy API purposes.
*
* Please read fileoverview documentation before using.
*
* @param {string} url A string to be converted to TrustedResourceUrl.
* @return {!goog.html.TrustedResourceUrl} The value of url, wrapped in a
* TrustedResourceUrl object.
*/
goog.html.legacyconversions.trustedResourceUrlFromString = function(url) {
goog.html.legacyconversions.reportCallback_();
return goog.html.TrustedResourceUrl
.createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(url);
};
/**
* @private {function(): undefined}
*/
goog.html.legacyconversions.reportCallback_ = goog.nullFunction;
/**
* Sets a function that will be called every time a legacy conversion is
* performed. The function is called with no parameters but it can use
* goog.debug.getStacktrace to get a stacktrace.
*
* @param {function(): undefined} callback Error callback as defined above.
*/
goog.html.legacyconversions.setReportCallback = function(callback) {
goog.html.legacyconversions.reportCallback_ = callback;
};

View File

@ -0,0 +1,994 @@
// Copyright 2013 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview The SafeHtml type and its builders.
*
* TODO(xtof): Link to document stating type contract.
*/
goog.provide('goog.html.SafeHtml');
goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.dom.TagName');
goog.require('goog.dom.tags');
goog.require('goog.html.SafeScript');
goog.require('goog.html.SafeStyle');
goog.require('goog.html.SafeStyleSheet');
goog.require('goog.html.SafeUrl');
goog.require('goog.html.TrustedResourceUrl');
goog.require('goog.i18n.bidi.Dir');
goog.require('goog.i18n.bidi.DirectionalString');
goog.require('goog.labs.userAgent.browser');
goog.require('goog.object');
goog.require('goog.string');
goog.require('goog.string.Const');
goog.require('goog.string.TypedString');
/**
* A string that is safe to use in HTML context in DOM APIs and HTML documents.
*
* A SafeHtml is a string-like object that carries the security type contract
* that its value as a string will not cause untrusted script execution when
* evaluated as HTML in a browser.
*
* Values of this type are guaranteed to be safe to use in HTML contexts,
* such as, assignment to the innerHTML DOM property, or interpolation into
* a HTML template in HTML PC_DATA context, in the sense that the use will not
* result in a Cross-Site-Scripting vulnerability.
*
* Instances of this type must be created via the factory methods
* ({@code goog.html.SafeHtml.create}, {@code goog.html.SafeHtml.htmlEscape}),
* etc and not by invoking its constructor. The constructor intentionally
* takes no parameters and the type is immutable; hence only a default instance
* corresponding to the empty string can be obtained via constructor invocation.
*
* @see goog.html.SafeHtml#create
* @see goog.html.SafeHtml#htmlEscape
* @constructor
* @final
* @struct
* @implements {goog.i18n.bidi.DirectionalString}
* @implements {goog.string.TypedString}
*/
goog.html.SafeHtml = function() {
/**
* The contained value of this SafeHtml. The field has a purposely ugly
* name to make (non-compiled) code that attempts to directly access this
* field stand out.
* @private {string}
*/
this.privateDoNotAccessOrElseSafeHtmlWrappedValue_ = '';
/**
* A type marker used to implement additional run-time type checking.
* @see goog.html.SafeHtml#unwrap
* @const {!Object}
* @private
*/
this.SAFE_HTML_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ =
goog.html.SafeHtml.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_;
/**
* This SafeHtml's directionality, or null if unknown.
* @private {?goog.i18n.bidi.Dir}
*/
this.dir_ = null;
};
/**
* @override
* @const
*/
goog.html.SafeHtml.prototype.implementsGoogI18nBidiDirectionalString = true;
/** @override */
goog.html.SafeHtml.prototype.getDirection = function() {
return this.dir_;
};
/**
* @override
* @const
*/
goog.html.SafeHtml.prototype.implementsGoogStringTypedString = true;
/**
* Returns this SafeHtml's value as string.
*
* IMPORTANT: In code where it is security relevant that an object's type is
* indeed {@code SafeHtml}, use {@code goog.html.SafeHtml.unwrap} instead of
* this method. If in doubt, assume that it's security relevant. In particular,
* note that goog.html functions which return a goog.html type do not guarantee
* that the returned instance is of the right type. For example:
*
* <pre>
* var fakeSafeHtml = new String('fake');
* fakeSafeHtml.__proto__ = goog.html.SafeHtml.prototype;
* var newSafeHtml = goog.html.SafeHtml.htmlEscape(fakeSafeHtml);
* // newSafeHtml is just an alias for fakeSafeHtml, it's passed through by
* // goog.html.SafeHtml.htmlEscape() as fakeSafeHtml
* // instanceof goog.html.SafeHtml.
* </pre>
*
* @see goog.html.SafeHtml#unwrap
* @override
*/
goog.html.SafeHtml.prototype.getTypedStringValue = function() {
return this.privateDoNotAccessOrElseSafeHtmlWrappedValue_;
};
if (goog.DEBUG) {
/**
* Returns a debug string-representation of this value.
*
* To obtain the actual string value wrapped in a SafeHtml, use
* {@code goog.html.SafeHtml.unwrap}.
*
* @see goog.html.SafeHtml#unwrap
* @override
*/
goog.html.SafeHtml.prototype.toString = function() {
return 'SafeHtml{' + this.privateDoNotAccessOrElseSafeHtmlWrappedValue_ +
'}';
};
}
/**
* Performs a runtime check that the provided object is indeed a SafeHtml
* object, and returns its value.
* @param {!goog.html.SafeHtml} safeHtml The object to extract from.
* @return {string} The SafeHtml object's contained string, unless the run-time
* type check fails. In that case, {@code unwrap} returns an innocuous
* string, or, if assertions are enabled, throws
* {@code goog.asserts.AssertionError}.
*/
goog.html.SafeHtml.unwrap = function(safeHtml) {
// Perform additional run-time type-checking to ensure that safeHtml is indeed
// an instance of the expected type. This provides some additional protection
// against security bugs due to application code that disables type checks.
// Specifically, the following checks are performed:
// 1. The object is an instance of the expected type.
// 2. The object is not an instance of a subclass.
// 3. The object carries a type marker for the expected type. "Faking" an
// object requires a reference to the type marker, which has names intended
// to stand out in code reviews.
if (safeHtml instanceof goog.html.SafeHtml &&
safeHtml.constructor === goog.html.SafeHtml &&
safeHtml.SAFE_HTML_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ ===
goog.html.SafeHtml.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_) {
return safeHtml.privateDoNotAccessOrElseSafeHtmlWrappedValue_;
} else {
goog.asserts.fail('expected object of type SafeHtml, got \'' +
safeHtml + '\' of type ' + goog.typeOf(safeHtml));
return 'type_error:SafeHtml';
}
};
/**
* Shorthand for union of types that can sensibly be converted to strings
* or might already be SafeHtml (as SafeHtml is a goog.string.TypedString).
* @private
* @typedef {string|number|boolean|!goog.string.TypedString|
* !goog.i18n.bidi.DirectionalString}
*/
goog.html.SafeHtml.TextOrHtml_;
/**
* Returns HTML-escaped text as a SafeHtml object.
*
* If text is of a type that implements
* {@code goog.i18n.bidi.DirectionalString}, the directionality of the new
* {@code SafeHtml} object is set to {@code text}'s directionality, if known.
* Otherwise, the directionality of the resulting SafeHtml is unknown (i.e.,
* {@code null}).
*
* @param {!goog.html.SafeHtml.TextOrHtml_} textOrHtml The text to escape. If
* the parameter is of type SafeHtml it is returned directly (no escaping
* is done).
* @return {!goog.html.SafeHtml} The escaped text, wrapped as a SafeHtml.
*/
goog.html.SafeHtml.htmlEscape = function(textOrHtml) {
if (textOrHtml instanceof goog.html.SafeHtml) {
return textOrHtml;
}
var dir = null;
if (textOrHtml.implementsGoogI18nBidiDirectionalString) {
dir = textOrHtml.getDirection();
}
var textAsString;
if (textOrHtml.implementsGoogStringTypedString) {
textAsString = textOrHtml.getTypedStringValue();
} else {
textAsString = String(textOrHtml);
}
return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
goog.string.htmlEscape(textAsString), dir);
};
/**
* Returns HTML-escaped text as a SafeHtml object, with newlines changed to
* &lt;br&gt;.
* @param {!goog.html.SafeHtml.TextOrHtml_} textOrHtml The text to escape. If
* the parameter is of type SafeHtml it is returned directly (no escaping
* is done).
* @return {!goog.html.SafeHtml} The escaped text, wrapped as a SafeHtml.
*/
goog.html.SafeHtml.htmlEscapePreservingNewlines = function(textOrHtml) {
if (textOrHtml instanceof goog.html.SafeHtml) {
return textOrHtml;
}
var html = goog.html.SafeHtml.htmlEscape(textOrHtml);
return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
goog.string.newLineToBr(goog.html.SafeHtml.unwrap(html)),
html.getDirection());
};
/**
* Returns HTML-escaped text as a SafeHtml object, with newlines changed to
* &lt;br&gt; and escaping whitespace to preserve spatial formatting. Character
* entity #160 is used to make it safer for XML.
* @param {!goog.html.SafeHtml.TextOrHtml_} textOrHtml The text to escape. If
* the parameter is of type SafeHtml it is returned directly (no escaping
* is done).
* @return {!goog.html.SafeHtml} The escaped text, wrapped as a SafeHtml.
*/
goog.html.SafeHtml.htmlEscapePreservingNewlinesAndSpaces = function(
textOrHtml) {
if (textOrHtml instanceof goog.html.SafeHtml) {
return textOrHtml;
}
var html = goog.html.SafeHtml.htmlEscape(textOrHtml);
return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
goog.string.whitespaceEscape(goog.html.SafeHtml.unwrap(html)),
html.getDirection());
};
/**
* Coerces an arbitrary object into a SafeHtml object.
*
* If {@code textOrHtml} is already of type {@code goog.html.SafeHtml}, the same
* object is returned. Otherwise, {@code textOrHtml} is coerced to string, and
* HTML-escaped. If {@code textOrHtml} is of a type that implements
* {@code goog.i18n.bidi.DirectionalString}, its directionality, if known, is
* preserved.
*
* @param {!goog.html.SafeHtml.TextOrHtml_} textOrHtml The text or SafeHtml to
* coerce.
* @return {!goog.html.SafeHtml} The resulting SafeHtml object.
* @deprecated Use goog.html.SafeHtml.htmlEscape.
*/
goog.html.SafeHtml.from = goog.html.SafeHtml.htmlEscape;
/**
* @const
* @private
*/
goog.html.SafeHtml.VALID_NAMES_IN_TAG_ = /^[a-zA-Z0-9-]+$/;
/**
* Set of attributes containing URL as defined at
* http://www.w3.org/TR/html5/index.html#attributes-1.
* @private @const {!Object<string,boolean>}
*/
goog.html.SafeHtml.URL_ATTRIBUTES_ = goog.object.createSet(
'action', 'cite', 'data', 'formaction', 'href', 'manifest', 'poster',
'src');
/**
* Tags which are unsupported via create(). They might be supported via a
* tag-specific create method. These are tags which might require a
* TrustedResourceUrl in one of their attributes or a restricted type for
* their content.
* @private @const {!Object<string,boolean>}
*/
goog.html.SafeHtml.NOT_ALLOWED_TAG_NAMES_ = goog.object.createSet(
goog.dom.TagName.APPLET, goog.dom.TagName.BASE, goog.dom.TagName.EMBED,
goog.dom.TagName.IFRAME, goog.dom.TagName.LINK, goog.dom.TagName.MATH,
goog.dom.TagName.META, goog.dom.TagName.OBJECT, goog.dom.TagName.SCRIPT,
goog.dom.TagName.STYLE, goog.dom.TagName.SVG, goog.dom.TagName.TEMPLATE);
/**
* @typedef {string|number|goog.string.TypedString|
* goog.html.SafeStyle.PropertyMap|undefined}
*/
goog.html.SafeHtml.AttributeValue;
/**
* Creates a SafeHtml content consisting of a tag with optional attributes and
* optional content.
*
* For convenience tag names and attribute names are accepted as regular
* strings, instead of goog.string.Const. Nevertheless, you should not pass
* user-controlled values to these parameters. Note that these parameters are
* syntactically validated at runtime, and invalid values will result in
* an exception.
*
* Example usage:
*
* goog.html.SafeHtml.create('br');
* goog.html.SafeHtml.create('div', {'class': 'a'});
* goog.html.SafeHtml.create('p', {}, 'a');
* goog.html.SafeHtml.create('p', {}, goog.html.SafeHtml.create('br'));
*
* goog.html.SafeHtml.create('span', {
* 'style': {'margin': '0'}
* });
*
* To guarantee SafeHtml's type contract is upheld there are restrictions on
* attribute values and tag names.
*
* - For attributes which contain script code (on*), a goog.string.Const is
* required.
* - For attributes which contain style (style), a goog.html.SafeStyle or a
* goog.html.SafeStyle.PropertyMap is required.
* - For attributes which are interpreted as URLs (e.g. src, href) a
* goog.html.SafeUrl, goog.string.Const or string is required. If a string
* is passed, it will be sanitized with SafeUrl.sanitize().
* - For tags which can load code or set security relevant page metadata,
* more specific goog.html.SafeHtml.create*() functions must be used. Tags
* which are not supported by this function are applet, base, embed, iframe,
* link, math, object, script, style, svg, and template.
*
* @param {!goog.dom.TagName|string} tagName The name of the tag. Only tag names
* consisting of [a-zA-Z0-9-] are allowed. Tag names documented above are
* disallowed.
* @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes
* Mapping from attribute names to their values. Only attribute names
* consisting of [a-zA-Z0-9-] are allowed. Value of null or undefined causes
* the attribute to be omitted.
* @param {!goog.html.SafeHtml.TextOrHtml_|
* !Array<!goog.html.SafeHtml.TextOrHtml_>=} opt_content Content to
* HTML-escape and put inside the tag. This must be empty for void tags
* like <br>. Array elements are concatenated.
* @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
* @throws {Error} If invalid tag name, attribute name, or attribute value is
* provided.
* @throws {goog.asserts.AssertionError} If content for void tag is provided.
*/
goog.html.SafeHtml.create = function(tagName, opt_attributes, opt_content) {
goog.html.SafeHtml.verifyTagName(String(tagName));
return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
String(tagName), opt_attributes, opt_content);
};
/**
* Verifies if the tag name is valid and if it doesn't change the context.
* E.g. STRONG is fine but SCRIPT throws because it changes context. See
* goog.html.SafeHtml.create for an explanation of allowed tags.
* @param {string} tagName
* @throws {Error} If invalid tag name is provided.
* @package
*/
goog.html.SafeHtml.verifyTagName = function(tagName) {
if (!goog.html.SafeHtml.VALID_NAMES_IN_TAG_.test(tagName)) {
throw Error('Invalid tag name <' + tagName + '>.');
}
if (tagName.toUpperCase() in goog.html.SafeHtml.NOT_ALLOWED_TAG_NAMES_) {
throw Error('Tag name <' + tagName + '> is not allowed for SafeHtml.');
}
};
/**
* Creates a SafeHtml representing an iframe tag.
*
* This by default restricts the iframe as much as possible by setting the
* sandbox attribute to the empty string. If the iframe requires less
* restrictions, set the sandbox attribute as tight as possible, but do not rely
* on the sandbox as a security feature because it is not supported by older
* browsers. If a sandbox is essential to security (e.g. for third-party
* frames), use createSandboxIframe which checks for browser support.
*
* @see https://developer.mozilla.org/en/docs/Web/HTML/Element/iframe#attr-sandbox
*
* @param {?goog.html.TrustedResourceUrl=} opt_src The value of the src
* attribute. If null or undefined src will not be set.
* @param {?goog.html.SafeHtml=} opt_srcdoc The value of the srcdoc attribute.
* If null or undefined srcdoc will not be set.
* @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes
* Mapping from attribute names to their values. Only attribute names
* consisting of [a-zA-Z0-9-] are allowed. Value of null or undefined causes
* the attribute to be omitted.
* @param {!goog.html.SafeHtml.TextOrHtml_|
* !Array<!goog.html.SafeHtml.TextOrHtml_>=} opt_content Content to
* HTML-escape and put inside the tag. Array elements are concatenated.
* @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
* @throws {Error} If invalid tag name, attribute name, or attribute value is
* provided. If opt_attributes contains the src or srcdoc attributes.
*/
goog.html.SafeHtml.createIframe = function(
opt_src, opt_srcdoc, opt_attributes, opt_content) {
if (opt_src) {
// Check whether this is really TrustedResourceUrl.
goog.html.TrustedResourceUrl.unwrap(opt_src);
}
var fixedAttributes = {};
fixedAttributes['src'] = opt_src || null;
fixedAttributes['srcdoc'] =
opt_srcdoc && goog.html.SafeHtml.unwrap(opt_srcdoc);
var defaultAttributes = {'sandbox': ''};
var attributes = goog.html.SafeHtml.combineAttributes(
fixedAttributes, defaultAttributes, opt_attributes);
return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
'iframe', attributes, opt_content);
};
/**
* Creates a SafeHtml representing a sandboxed iframe tag.
*
* The sandbox attribute is enforced in its most restrictive mode, an empty
* string. Consequently, the security requirements for the src and srcdoc
* attributes are relaxed compared to SafeHtml.createIframe. This function
* will throw on browsers that do not support the sandbox attribute, as
* determined by SafeHtml.canUseSandboxIframe.
*
* The SafeHtml returned by this function can trigger downloads with no
* user interaction on Chrome (though only a few, further attempts are blocked).
* Firefox and IE will block all downloads from the sandbox.
*
* @see https://developer.mozilla.org/en/docs/Web/HTML/Element/iframe#attr-sandbox
* @see https://lists.w3.org/Archives/Public/public-whatwg-archive/2013Feb/0112.html
*
* @param {string|!goog.html.SafeUrl=} opt_src The value of the src
* attribute. If null or undefined src will not be set.
* @param {string=} opt_srcdoc The value of the srcdoc attribute.
* If null or undefined srcdoc will not be set. Will not be sanitized.
* @param {!Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes
* Mapping from attribute names to their values. Only attribute names
* consisting of [a-zA-Z0-9-] are allowed. Value of null or undefined causes
* the attribute to be omitted.
* @param {!goog.html.SafeHtml.TextOrHtml_|
* !Array<!goog.html.SafeHtml.TextOrHtml_>=} opt_content Content to
* HTML-escape and put inside the tag. Array elements are concatenated.
* @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
* @throws {Error} If invalid tag name, attribute name, or attribute value is
* provided. If opt_attributes contains the src, srcdoc or sandbox
* attributes. If browser does not support the sandbox attribute on iframe.
*/
goog.html.SafeHtml.createSandboxIframe = function(
opt_src, opt_srcdoc, opt_attributes, opt_content) {
if (!goog.html.SafeHtml.canUseSandboxIframe()) {
throw new Error('The browser does not support sandboxed iframes.');
}
var fixedAttributes = {};
if (opt_src) {
// Note that sanitize is a no-op on SafeUrl.
fixedAttributes['src'] =
goog.html.SafeUrl.unwrap(goog.html.SafeUrl.sanitize(opt_src));
} else {
fixedAttributes['src'] = null;
}
fixedAttributes['srcdoc'] = opt_srcdoc || null;
fixedAttributes['sandbox'] = '';
var attributes =
goog.html.SafeHtml.combineAttributes(fixedAttributes, {}, opt_attributes);
return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
'iframe', attributes, opt_content);
};
/**
* Checks if the user agent supports sandboxed iframes.
* @return {boolean}
*/
goog.html.SafeHtml.canUseSandboxIframe = function() {
return goog.global['HTMLIFrameElement'] &&
('sandbox' in goog.global['HTMLIFrameElement'].prototype);
};
/**
* Creates a SafeHtml representing a script tag with the src attribute.
* @param {!goog.html.TrustedResourceUrl} src The value of the src
* attribute.
* @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=}
* opt_attributes
* Mapping from attribute names to their values. Only attribute names
* consisting of [a-zA-Z0-9-] are allowed. Value of null or undefined
* causes the attribute to be omitted.
* @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
* @throws {Error} If invalid attribute name or value is provided. If
* opt_attributes contains the src attribute.
*/
goog.html.SafeHtml.createScriptSrc = function(src, opt_attributes) {
// TODO(mlourenco): The charset attribute should probably be blocked. If
// its value is attacker controlled, the script contains attacker controlled
// sub-strings (even if properly escaped) and the server does not set charset
// then XSS is likely possible.
// https://html.spec.whatwg.org/multipage/scripting.html#dom-script-charset
// Check whether this is really TrustedResourceUrl.
goog.html.TrustedResourceUrl.unwrap(src);
var fixedAttributes = {'src': src};
var defaultAttributes = {};
var attributes = goog.html.SafeHtml.combineAttributes(
fixedAttributes, defaultAttributes, opt_attributes);
return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
'script', attributes);
};
/**
* Creates a SafeHtml representing a script tag. Does not allow the language,
* src, text or type attributes to be set.
* @param {!goog.html.SafeScript|!Array<!goog.html.SafeScript>}
* script Content to put inside the tag. Array elements are
* concatenated.
* @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes
* Mapping from attribute names to their values. Only attribute names
* consisting of [a-zA-Z0-9-] are allowed. Value of null or undefined causes
* the attribute to be omitted.
* @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
* @throws {Error} If invalid attribute name or attribute value is provided. If
* opt_attributes contains the language, src, text or type attribute.
*/
goog.html.SafeHtml.createScript = function(script, opt_attributes) {
for (var attr in opt_attributes) {
var attrLower = attr.toLowerCase();
if (attrLower == 'language' || attrLower == 'src' || attrLower == 'text' ||
attrLower == 'type') {
throw Error('Cannot set "' + attrLower + '" attribute');
}
}
var content = '';
script = goog.array.concat(script);
for (var i = 0; i < script.length; i++) {
content += goog.html.SafeScript.unwrap(script[i]);
}
// Convert to SafeHtml so that it's not HTML-escaped. This is safe because
// as part of its contract, SafeScript should have no dangerous '<'.
var htmlContent =
goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
content, goog.i18n.bidi.Dir.NEUTRAL);
return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
'script', opt_attributes, htmlContent);
};
/**
* Creates a SafeHtml representing a style tag. The type attribute is set
* to "text/css".
* @param {!goog.html.SafeStyleSheet|!Array<!goog.html.SafeStyleSheet>}
* styleSheet Content to put inside the tag. Array elements are
* concatenated.
* @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes
* Mapping from attribute names to their values. Only attribute names
* consisting of [a-zA-Z0-9-] are allowed. Value of null or undefined causes
* the attribute to be omitted.
* @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
* @throws {Error} If invalid attribute name or attribute value is provided. If
* opt_attributes contains the type attribute.
*/
goog.html.SafeHtml.createStyle = function(styleSheet, opt_attributes) {
var fixedAttributes = {'type': 'text/css'};
var defaultAttributes = {};
var attributes = goog.html.SafeHtml.combineAttributes(
fixedAttributes, defaultAttributes, opt_attributes);
var content = '';
styleSheet = goog.array.concat(styleSheet);
for (var i = 0; i < styleSheet.length; i++) {
content += goog.html.SafeStyleSheet.unwrap(styleSheet[i]);
}
// Convert to SafeHtml so that it's not HTML-escaped. This is safe because
// as part of its contract, SafeStyleSheet should have no dangerous '<'.
var htmlContent =
goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
content, goog.i18n.bidi.Dir.NEUTRAL);
return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
'style', attributes, htmlContent);
};
/**
* Creates a SafeHtml representing a meta refresh tag.
* @param {!goog.html.SafeUrl|string} url Where to redirect. If a string is
* passed, it will be sanitized with SafeUrl.sanitize().
* @param {number=} opt_secs Number of seconds until the page should be
* reloaded. Will be set to 0 if unspecified.
* @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
*/
goog.html.SafeHtml.createMetaRefresh = function(url, opt_secs) {
// Note that sanitize is a no-op on SafeUrl.
var unwrappedUrl = goog.html.SafeUrl.unwrap(goog.html.SafeUrl.sanitize(url));
if (goog.labs.userAgent.browser.isIE() ||
goog.labs.userAgent.browser.isEdge()) {
// IE/EDGE can't parse the content attribute if the url contains a
// semicolon. We can fix this by adding quotes around the url, but then we
// can't parse quotes in the URL correctly. Also, it seems that IE/EDGE
// did not unescape semicolons in these URLs at some point in the past. We
// take a best-effort approach.
//
// If the URL has semicolons (which may happen in some cases, see
// http://www.w3.org/TR/1999/REC-html401-19991224/appendix/notes.html#h-B.2
// for instance), wrap it in single quotes to protect the semicolons.
// If the URL has semicolons and single quotes, url-encode the single quotes
// as well.
//
// This is imperfect. Notice that both ' and ; are reserved characters in
// URIs, so this could do the wrong thing, but at least it will do the wrong
// thing in only rare cases.
if (goog.string.contains(unwrappedUrl, ';')) {
unwrappedUrl = "'" + unwrappedUrl.replace(/'/g, '%27') + "'";
}
}
var attributes = {
'http-equiv': 'refresh',
'content': (opt_secs || 0) + '; url=' + unwrappedUrl
};
// This function will handle the HTML escaping for attributes.
return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
'meta', attributes);
};
/**
* @param {string} tagName The tag name.
* @param {string} name The attribute name.
* @param {!goog.html.SafeHtml.AttributeValue} value The attribute value.
* @return {string} A "name=value" string.
* @throws {Error} If attribute value is unsafe for the given tag and attribute.
* @private
*/
goog.html.SafeHtml.getAttrNameAndValue_ = function(tagName, name, value) {
// If it's goog.string.Const, allow any valid attribute name.
if (value instanceof goog.string.Const) {
value = goog.string.Const.unwrap(value);
} else if (name.toLowerCase() == 'style') {
value = goog.html.SafeHtml.getStyleValue_(value);
} else if (/^on/i.test(name)) {
// TODO(jakubvrana): Disallow more attributes with a special meaning.
throw Error(
'Attribute "' + name + '" requires goog.string.Const value, "' + value +
'" given.');
// URL attributes handled differently according to tag.
} else if (name.toLowerCase() in goog.html.SafeHtml.URL_ATTRIBUTES_) {
if (value instanceof goog.html.TrustedResourceUrl) {
value = goog.html.TrustedResourceUrl.unwrap(value);
} else if (value instanceof goog.html.SafeUrl) {
value = goog.html.SafeUrl.unwrap(value);
} else if (goog.isString(value)) {
value = goog.html.SafeUrl.sanitize(value).getTypedStringValue();
} else {
throw Error(
'Attribute "' + name + '" on tag "' + tagName +
'" requires goog.html.SafeUrl, goog.string.Const, or string,' +
' value "' + value + '" given.');
}
}
// Accept SafeUrl, TrustedResourceUrl, etc. for attributes which only require
// HTML-escaping.
if (value.implementsGoogStringTypedString) {
// Ok to call getTypedStringValue() since there's no reliance on the type
// contract for security here.
value = value.getTypedStringValue();
}
goog.asserts.assert(
goog.isString(value) || goog.isNumber(value),
'String or number value expected, got ' + (typeof value) +
' with value: ' + value);
return name + '="' + goog.string.htmlEscape(String(value)) + '"';
};
/**
* Gets value allowed in "style" attribute.
* @param {!goog.html.SafeHtml.AttributeValue} value It could be SafeStyle or a
* map which will be passed to goog.html.SafeStyle.create.
* @return {string} Unwrapped value.
* @throws {Error} If string value is given.
* @private
*/
goog.html.SafeHtml.getStyleValue_ = function(value) {
if (!goog.isObject(value)) {
throw Error(
'The "style" attribute requires goog.html.SafeStyle or map ' +
'of style properties, ' + (typeof value) + ' given: ' + value);
}
if (!(value instanceof goog.html.SafeStyle)) {
// Process the property bag into a style object.
value = goog.html.SafeStyle.create(value);
}
return goog.html.SafeStyle.unwrap(value);
};
/**
* Creates a SafeHtml content with known directionality consisting of a tag with
* optional attributes and optional content.
* @param {!goog.i18n.bidi.Dir} dir Directionality.
* @param {string} tagName
* @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes
* @param {!goog.html.SafeHtml.TextOrHtml_|
* !Array<!goog.html.SafeHtml.TextOrHtml_>=} opt_content
* @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
*/
goog.html.SafeHtml.createWithDir = function(
dir, tagName, opt_attributes, opt_content) {
var html = goog.html.SafeHtml.create(tagName, opt_attributes, opt_content);
html.dir_ = dir;
return html;
};
/**
* Creates a new SafeHtml object by concatenating values.
* @param {...(!goog.html.SafeHtml.TextOrHtml_|
* !Array<!goog.html.SafeHtml.TextOrHtml_>)} var_args Values to concatenate.
* @return {!goog.html.SafeHtml}
*/
goog.html.SafeHtml.concat = function(var_args) {
var dir = goog.i18n.bidi.Dir.NEUTRAL;
var content = '';
/**
* @param {!goog.html.SafeHtml.TextOrHtml_|
* !Array<!goog.html.SafeHtml.TextOrHtml_>} argument
*/
var addArgument = function(argument) {
if (goog.isArray(argument)) {
goog.array.forEach(argument, addArgument);
} else {
var html = goog.html.SafeHtml.htmlEscape(argument);
content += goog.html.SafeHtml.unwrap(html);
var htmlDir = html.getDirection();
if (dir == goog.i18n.bidi.Dir.NEUTRAL) {
dir = htmlDir;
} else if (htmlDir != goog.i18n.bidi.Dir.NEUTRAL && dir != htmlDir) {
dir = null;
}
}
};
goog.array.forEach(arguments, addArgument);
return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
content, dir);
};
/**
* Creates a new SafeHtml object with known directionality by concatenating the
* values.
* @param {!goog.i18n.bidi.Dir} dir Directionality.
* @param {...(!goog.html.SafeHtml.TextOrHtml_|
* !Array<!goog.html.SafeHtml.TextOrHtml_>)} var_args Elements of array
* arguments would be processed recursively.
* @return {!goog.html.SafeHtml}
*/
goog.html.SafeHtml.concatWithDir = function(dir, var_args) {
var html = goog.html.SafeHtml.concat(goog.array.slice(arguments, 1));
html.dir_ = dir;
return html;
};
/**
* Type marker for the SafeHtml type, used to implement additional run-time
* type checking.
* @const {!Object}
* @private
*/
goog.html.SafeHtml.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};
/**
* Package-internal utility method to create SafeHtml instances.
*
* @param {string} html The string to initialize the SafeHtml object with.
* @param {?goog.i18n.bidi.Dir} dir The directionality of the SafeHtml to be
* constructed, or null if unknown.
* @return {!goog.html.SafeHtml} The initialized SafeHtml object.
* @package
*/
goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse = function(
html, dir) {
return new goog.html.SafeHtml().initSecurityPrivateDoNotAccessOrElse_(
html, dir);
};
/**
* Called from createSafeHtmlSecurityPrivateDoNotAccessOrElse(). This
* method exists only so that the compiler can dead code eliminate static
* fields (like EMPTY) when they're not accessed.
* @param {string} html
* @param {?goog.i18n.bidi.Dir} dir
* @return {!goog.html.SafeHtml}
* @private
*/
goog.html.SafeHtml.prototype.initSecurityPrivateDoNotAccessOrElse_ = function(
html, dir) {
this.privateDoNotAccessOrElseSafeHtmlWrappedValue_ = html;
this.dir_ = dir;
return this;
};
/**
* Like create() but does not restrict which tags can be constructed.
*
* @param {string} tagName Tag name. Set or validated by caller.
* @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes
* @param {(!goog.html.SafeHtml.TextOrHtml_|
* !Array<!goog.html.SafeHtml.TextOrHtml_>)=} opt_content
* @return {!goog.html.SafeHtml}
* @throws {Error} If invalid or unsafe attribute name or value is provided.
* @throws {goog.asserts.AssertionError} If content for void tag is provided.
* @package
*/
goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse = function(
tagName, opt_attributes, opt_content) {
var dir = null;
var result = '<' + tagName;
result += goog.html.SafeHtml.stringifyAttributes(tagName, opt_attributes);
var content = opt_content;
if (!goog.isDefAndNotNull(content)) {
content = [];
} else if (!goog.isArray(content)) {
content = [content];
}
if (goog.dom.tags.isVoidTag(tagName.toLowerCase())) {
goog.asserts.assert(
!content.length, 'Void tag <' + tagName + '> does not allow content.');
result += '>';
} else {
var html = goog.html.SafeHtml.concat(content);
result += '>' + goog.html.SafeHtml.unwrap(html) + '</' + tagName + '>';
dir = html.getDirection();
}
var dirAttribute = opt_attributes && opt_attributes['dir'];
if (dirAttribute) {
if (/^(ltr|rtl|auto)$/i.test(dirAttribute)) {
// If the tag has the "dir" attribute specified then its direction is
// neutral because it can be safely used in any context.
dir = goog.i18n.bidi.Dir.NEUTRAL;
} else {
dir = null;
}
}
return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
result, dir);
};
/**
* Creates a string with attributes to insert after tagName.
* @param {string} tagName
* @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes
* @return {string} Returns an empty string if there are no attributes, returns
* a string starting with a space otherwise.
* @throws {Error} If attribute value is unsafe for the given tag and attribute.
* @package
*/
goog.html.SafeHtml.stringifyAttributes = function(tagName, opt_attributes) {
var result = '';
if (opt_attributes) {
for (var name in opt_attributes) {
if (!goog.html.SafeHtml.VALID_NAMES_IN_TAG_.test(name)) {
throw Error('Invalid attribute name "' + name + '".');
}
var value = opt_attributes[name];
if (!goog.isDefAndNotNull(value)) {
continue;
}
result +=
' ' + goog.html.SafeHtml.getAttrNameAndValue_(tagName, name, value);
}
}
return result;
};
/**
* @param {!Object<string, ?goog.html.SafeHtml.AttributeValue>} fixedAttributes
* @param {!Object<string, string>} defaultAttributes
* @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes
* Optional attributes passed to create*().
* @return {!Object<string, ?goog.html.SafeHtml.AttributeValue>}
* @throws {Error} If opt_attributes contains an attribute with the same name
* as an attribute in fixedAttributes.
* @package
*/
goog.html.SafeHtml.combineAttributes = function(
fixedAttributes, defaultAttributes, opt_attributes) {
var combinedAttributes = {};
var name;
for (name in fixedAttributes) {
goog.asserts.assert(name.toLowerCase() == name, 'Must be lower case');
combinedAttributes[name] = fixedAttributes[name];
}
for (name in defaultAttributes) {
goog.asserts.assert(name.toLowerCase() == name, 'Must be lower case');
combinedAttributes[name] = defaultAttributes[name];
}
for (name in opt_attributes) {
var nameLower = name.toLowerCase();
if (nameLower in fixedAttributes) {
throw Error(
'Cannot override "' + nameLower + '" attribute, got "' + name +
'" with value "' + opt_attributes[name] + '"');
}
if (nameLower in defaultAttributes) {
delete combinedAttributes[nameLower];
}
combinedAttributes[name] = opt_attributes[name];
}
return combinedAttributes;
};
/**
* A SafeHtml instance corresponding to the HTML doctype: "<!DOCTYPE html>".
* @const {!goog.html.SafeHtml}
*/
goog.html.SafeHtml.DOCTYPE_HTML =
goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
'<!DOCTYPE html>', goog.i18n.bidi.Dir.NEUTRAL);
/**
* A SafeHtml instance corresponding to the empty string.
* @const {!goog.html.SafeHtml}
*/
goog.html.SafeHtml.EMPTY =
goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
'', goog.i18n.bidi.Dir.NEUTRAL);
/**
* A SafeHtml instance corresponding to the <br> tag.
* @const {!goog.html.SafeHtml}
*/
goog.html.SafeHtml.BR =
goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
'<br>', goog.i18n.bidi.Dir.NEUTRAL);

View File

@ -0,0 +1,234 @@
// Copyright 2014 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview The SafeScript type and its builders.
*
* TODO(xtof): Link to document stating type contract.
*/
goog.provide('goog.html.SafeScript');
goog.require('goog.asserts');
goog.require('goog.string.Const');
goog.require('goog.string.TypedString');
/**
* A string-like object which represents JavaScript code and that carries the
* security type contract that its value, as a string, will not cause execution
* of unconstrained attacker controlled code (XSS) when evaluated as JavaScript
* in a browser.
*
* Instances of this type must be created via the factory method
* {@code goog.html.SafeScript.fromConstant} and not by invoking its
* constructor. The constructor intentionally takes no parameters and the type
* is immutable; hence only a default instance corresponding to the empty string
* can be obtained via constructor invocation.
*
* A SafeScript's string representation can safely be interpolated as the
* content of a script element within HTML. The SafeScript string should not be
* escaped before interpolation.
*
* Note that the SafeScript might contain text that is attacker-controlled but
* that text should have been interpolated with appropriate escaping,
* sanitization and/or validation into the right location in the script, such
* that it is highly constrained in its effect (for example, it had to match a
* set of whitelisted words).
*
* A SafeScript can be constructed via security-reviewed unchecked
* conversions. In this case producers of SafeScript must ensure themselves that
* the SafeScript does not contain unsafe script. Note in particular that
* {@code &lt;} is dangerous, even when inside JavaScript strings, and so should
* always be forbidden or JavaScript escaped in user controlled input. For
* example, if {@code &lt;/script&gt;&lt;script&gt;evil&lt;/script&gt;"} were
* interpolated inside a JavaScript string, it would break out of the context
* of the original script element and {@code evil} would execute. Also note
* that within an HTML script (raw text) element, HTML character references,
* such as "&lt;" are not allowed. See
* http://www.w3.org/TR/html5/scripting-1.html#restrictions-for-contents-of-script-elements.
*
* @see goog.html.SafeScript#fromConstant
* @constructor
* @final
* @struct
* @implements {goog.string.TypedString}
*/
goog.html.SafeScript = function() {
/**
* The contained value of this SafeScript. The field has a purposely
* ugly name to make (non-compiled) code that attempts to directly access this
* field stand out.
* @private {string}
*/
this.privateDoNotAccessOrElseSafeScriptWrappedValue_ = '';
/**
* A type marker used to implement additional run-time type checking.
* @see goog.html.SafeScript#unwrap
* @const {!Object}
* @private
*/
this.SAFE_SCRIPT_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ =
goog.html.SafeScript.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_;
};
/**
* @override
* @const
*/
goog.html.SafeScript.prototype.implementsGoogStringTypedString = true;
/**
* Type marker for the SafeScript type, used to implement additional
* run-time type checking.
* @const {!Object}
* @private
*/
goog.html.SafeScript.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};
/**
* Creates a SafeScript object from a compile-time constant string.
*
* @param {!goog.string.Const} script A compile-time-constant string from which
* to create a SafeScript.
* @return {!goog.html.SafeScript} A SafeScript object initialized to
* {@code script}.
*/
goog.html.SafeScript.fromConstant = function(script) {
var scriptString = goog.string.Const.unwrap(script);
if (scriptString.length === 0) {
return goog.html.SafeScript.EMPTY;
}
return goog.html.SafeScript.createSafeScriptSecurityPrivateDoNotAccessOrElse(
scriptString);
};
/**
* Returns this SafeScript's value as a string.
*
* IMPORTANT: In code where it is security relevant that an object's type is
* indeed {@code SafeScript}, use {@code goog.html.SafeScript.unwrap} instead of
* this method. If in doubt, assume that it's security relevant. In particular,
* note that goog.html functions which return a goog.html type do not guarantee
* the returned instance is of the right type. For example:
*
* <pre>
* var fakeSafeHtml = new String('fake');
* fakeSafeHtml.__proto__ = goog.html.SafeHtml.prototype;
* var newSafeHtml = goog.html.SafeHtml.htmlEscape(fakeSafeHtml);
* // newSafeHtml is just an alias for fakeSafeHtml, it's passed through by
* // goog.html.SafeHtml.htmlEscape() as fakeSafeHtml
* // instanceof goog.html.SafeHtml.
* </pre>
*
* @see goog.html.SafeScript#unwrap
* @override
*/
goog.html.SafeScript.prototype.getTypedStringValue = function() {
return this.privateDoNotAccessOrElseSafeScriptWrappedValue_;
};
if (goog.DEBUG) {
/**
* Returns a debug string-representation of this value.
*
* To obtain the actual string value wrapped in a SafeScript, use
* {@code goog.html.SafeScript.unwrap}.
*
* @see goog.html.SafeScript#unwrap
* @override
*/
goog.html.SafeScript.prototype.toString = function() {
return 'SafeScript{' +
this.privateDoNotAccessOrElseSafeScriptWrappedValue_ + '}';
};
}
/**
* Performs a runtime check that the provided object is indeed a
* SafeScript object, and returns its value.
*
* @param {!goog.html.SafeScript} safeScript The object to extract from.
* @return {string} The safeScript object's contained string, unless
* the run-time type check fails. In that case, {@code unwrap} returns an
* innocuous string, or, if assertions are enabled, throws
* {@code goog.asserts.AssertionError}.
*/
goog.html.SafeScript.unwrap = function(safeScript) {
// Perform additional Run-time type-checking to ensure that
// safeScript is indeed an instance of the expected type. This
// provides some additional protection against security bugs due to
// application code that disables type checks.
// Specifically, the following checks are performed:
// 1. The object is an instance of the expected type.
// 2. The object is not an instance of a subclass.
// 3. The object carries a type marker for the expected type. "Faking" an
// object requires a reference to the type marker, which has names intended
// to stand out in code reviews.
if (safeScript instanceof goog.html.SafeScript &&
safeScript.constructor === goog.html.SafeScript &&
safeScript.SAFE_SCRIPT_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ ===
goog.html.SafeScript.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_) {
return safeScript.privateDoNotAccessOrElseSafeScriptWrappedValue_;
} else {
goog.asserts.fail('expected object of type SafeScript, got \'' +
safeScript + '\' of type ' + goog.typeOf(safeScript));
return 'type_error:SafeScript';
}
};
/**
* Package-internal utility method to create SafeScript instances.
*
* @param {string} script The string to initialize the SafeScript object with.
* @return {!goog.html.SafeScript} The initialized SafeScript object.
* @package
*/
goog.html.SafeScript.createSafeScriptSecurityPrivateDoNotAccessOrElse =
function(script) {
return new goog.html.SafeScript().initSecurityPrivateDoNotAccessOrElse_(
script);
};
/**
* Called from createSafeScriptSecurityPrivateDoNotAccessOrElse(). This
* method exists only so that the compiler can dead code eliminate static
* fields (like EMPTY) when they're not accessed.
* @param {string} script
* @return {!goog.html.SafeScript}
* @private
*/
goog.html.SafeScript.prototype.initSecurityPrivateDoNotAccessOrElse_ = function(
script) {
this.privateDoNotAccessOrElseSafeScriptWrappedValue_ = script;
return this;
};
/**
* A SafeScript instance corresponding to the empty string.
* @const {!goog.html.SafeScript}
*/
goog.html.SafeScript.EMPTY =
goog.html.SafeScript.createSafeScriptSecurityPrivateDoNotAccessOrElse('');

View File

@ -0,0 +1,560 @@
// Copyright 2014 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview The SafeStyle type and its builders.
*
* TODO(xtof): Link to document stating type contract.
*/
goog.provide('goog.html.SafeStyle');
goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.html.SafeUrl');
goog.require('goog.string');
goog.require('goog.string.Const');
goog.require('goog.string.TypedString');
/**
* A string-like object which represents a sequence of CSS declarations
* ({@code propertyName1: propertyvalue1; propertyName2: propertyValue2; ...})
* and that carries the security type contract that its value, as a string,
* will not cause untrusted script execution (XSS) when evaluated as CSS in a
* browser.
*
* Instances of this type must be created via the factory methods
* ({@code goog.html.SafeStyle.create} or
* {@code goog.html.SafeStyle.fromConstant}) and not by invoking its
* constructor. The constructor intentionally takes no parameters and the type
* is immutable; hence only a default instance corresponding to the empty string
* can be obtained via constructor invocation.
*
* SafeStyle's string representation can safely be:
* <ul>
* <li>Interpolated as the content of a *quoted* HTML style attribute.
* However, the SafeStyle string *must be HTML-attribute-escaped* before
* interpolation.
* <li>Interpolated as the content of a {}-wrapped block within a stylesheet.
* '<' characters in the SafeStyle string *must be CSS-escaped* before
* interpolation. The SafeStyle string is also guaranteed not to be able
* to introduce new properties or elide existing ones.
* <li>Interpolated as the content of a {}-wrapped block within an HTML
* <style> element. '<' characters in the SafeStyle string
* *must be CSS-escaped* before interpolation.
* <li>Assigned to the style property of a DOM node. The SafeStyle string
* should not be escaped before being assigned to the property.
* </ul>
*
* A SafeStyle may never contain literal angle brackets. Otherwise, it could
* be unsafe to place a SafeStyle into a &lt;style&gt; tag (where it can't
* be HTML escaped). For example, if the SafeStyle containing
* "{@code font: 'foo &lt;style/&gt;&lt;script&gt;evil&lt;/script&gt;'}" were
* interpolated within a &lt;style&gt; tag, this would then break out of the
* style context into HTML.
*
* A SafeStyle may contain literal single or double quotes, and as such the
* entire style string must be escaped when used in a style attribute (if
* this were not the case, the string could contain a matching quote that
* would escape from the style attribute).
*
* Values of this type must be composable, i.e. for any two values
* {@code style1} and {@code style2} of this type,
* {@code goog.html.SafeStyle.unwrap(style1) +
* goog.html.SafeStyle.unwrap(style2)} must itself be a value that satisfies
* the SafeStyle type constraint. This requirement implies that for any value
* {@code style} of this type, {@code goog.html.SafeStyle.unwrap(style)} must
* not end in a "property value" or "property name" context. For example,
* a value of {@code background:url("} or {@code font-} would not satisfy the
* SafeStyle contract. This is because concatenating such strings with a
* second value that itself does not contain unsafe CSS can result in an
* overall string that does. For example, if {@code javascript:evil())"} is
* appended to {@code background:url("}, the resulting string may result in
* the execution of a malicious script.
*
* TODO(mlourenco): Consider whether we should implement UTF-8 interchange
* validity checks and blacklisting of newlines (including Unicode ones) and
* other whitespace characters (\t, \f). Document here if so and also update
* SafeStyle.fromConstant().
*
* The following example values comply with this type's contract:
* <ul>
* <li><pre>width: 1em;</pre>
* <li><pre>height:1em;</pre>
* <li><pre>width: 1em;height: 1em;</pre>
* <li><pre>background:url('http://url');</pre>
* </ul>
* In addition, the empty string is safe for use in a CSS attribute.
*
* The following example values do NOT comply with this type's contract:
* <ul>
* <li><pre>background: red</pre> (missing a trailing semi-colon)
* <li><pre>background:</pre> (missing a value and a trailing semi-colon)
* <li><pre>1em</pre> (missing an attribute name, which provides context for
* the value)
* </ul>
*
* @see goog.html.SafeStyle#create
* @see goog.html.SafeStyle#fromConstant
* @see http://www.w3.org/TR/css3-syntax/
* @constructor
* @final
* @struct
* @implements {goog.string.TypedString}
*/
goog.html.SafeStyle = function() {
/**
* The contained value of this SafeStyle. The field has a purposely
* ugly name to make (non-compiled) code that attempts to directly access this
* field stand out.
* @private {string}
*/
this.privateDoNotAccessOrElseSafeStyleWrappedValue_ = '';
/**
* A type marker used to implement additional run-time type checking.
* @see goog.html.SafeStyle#unwrap
* @const {!Object}
* @private
*/
this.SAFE_STYLE_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ =
goog.html.SafeStyle.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_;
};
/**
* @override
* @const
*/
goog.html.SafeStyle.prototype.implementsGoogStringTypedString = true;
/**
* Type marker for the SafeStyle type, used to implement additional
* run-time type checking.
* @const {!Object}
* @private
*/
goog.html.SafeStyle.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};
/**
* Creates a SafeStyle object from a compile-time constant string.
*
* {@code style} should be in the format
* {@code name: value; [name: value; ...]} and must not have any < or >
* characters in it. This is so that SafeStyle's contract is preserved,
* allowing the SafeStyle to correctly be interpreted as a sequence of CSS
* declarations and without affecting the syntactic structure of any
* surrounding CSS and HTML.
*
* This method performs basic sanity checks on the format of {@code style}
* but does not constrain the format of {@code name} and {@code value}, except
* for disallowing tag characters.
*
* @param {!goog.string.Const} style A compile-time-constant string from which
* to create a SafeStyle.
* @return {!goog.html.SafeStyle} A SafeStyle object initialized to
* {@code style}.
*/
goog.html.SafeStyle.fromConstant = function(style) {
var styleString = goog.string.Const.unwrap(style);
if (styleString.length === 0) {
return goog.html.SafeStyle.EMPTY;
}
goog.html.SafeStyle.checkStyle_(styleString);
goog.asserts.assert(
goog.string.endsWith(styleString, ';'),
'Last character of style string is not \';\': ' + styleString);
goog.asserts.assert(
goog.string.contains(styleString, ':'),
'Style string must contain at least one \':\', to ' +
'specify a "name: value" pair: ' + styleString);
return goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse(
styleString);
};
/**
* Checks if the style definition is valid.
* @param {string} style
* @private
*/
goog.html.SafeStyle.checkStyle_ = function(style) {
goog.asserts.assert(
!/[<>]/.test(style), 'Forbidden characters in style string: ' + style);
};
/**
* Returns this SafeStyle's value as a string.
*
* IMPORTANT: In code where it is security relevant that an object's type is
* indeed {@code SafeStyle}, use {@code goog.html.SafeStyle.unwrap} instead of
* this method. If in doubt, assume that it's security relevant. In particular,
* note that goog.html functions which return a goog.html type do not guarantee
* the returned instance is of the right type. For example:
*
* <pre>
* var fakeSafeHtml = new String('fake');
* fakeSafeHtml.__proto__ = goog.html.SafeHtml.prototype;
* var newSafeHtml = goog.html.SafeHtml.htmlEscape(fakeSafeHtml);
* // newSafeHtml is just an alias for fakeSafeHtml, it's passed through by
* // goog.html.SafeHtml.htmlEscape() as fakeSafeHtml
* // instanceof goog.html.SafeHtml.
* </pre>
*
* @see goog.html.SafeStyle#unwrap
* @override
*/
goog.html.SafeStyle.prototype.getTypedStringValue = function() {
return this.privateDoNotAccessOrElseSafeStyleWrappedValue_;
};
if (goog.DEBUG) {
/**
* Returns a debug string-representation of this value.
*
* To obtain the actual string value wrapped in a SafeStyle, use
* {@code goog.html.SafeStyle.unwrap}.
*
* @see goog.html.SafeStyle#unwrap
* @override
*/
goog.html.SafeStyle.prototype.toString = function() {
return 'SafeStyle{' + this.privateDoNotAccessOrElseSafeStyleWrappedValue_ +
'}';
};
}
/**
* Performs a runtime check that the provided object is indeed a
* SafeStyle object, and returns its value.
*
* @param {!goog.html.SafeStyle} safeStyle The object to extract from.
* @return {string} The safeStyle object's contained string, unless
* the run-time type check fails. In that case, {@code unwrap} returns an
* innocuous string, or, if assertions are enabled, throws
* {@code goog.asserts.AssertionError}.
*/
goog.html.SafeStyle.unwrap = function(safeStyle) {
// Perform additional Run-time type-checking to ensure that
// safeStyle is indeed an instance of the expected type. This
// provides some additional protection against security bugs due to
// application code that disables type checks.
// Specifically, the following checks are performed:
// 1. The object is an instance of the expected type.
// 2. The object is not an instance of a subclass.
// 3. The object carries a type marker for the expected type. "Faking" an
// object requires a reference to the type marker, which has names intended
// to stand out in code reviews.
if (safeStyle instanceof goog.html.SafeStyle &&
safeStyle.constructor === goog.html.SafeStyle &&
safeStyle.SAFE_STYLE_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ ===
goog.html.SafeStyle.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_) {
return safeStyle.privateDoNotAccessOrElseSafeStyleWrappedValue_;
} else {
goog.asserts.fail('expected object of type SafeStyle, got \'' +
safeStyle + '\' of type ' + goog.typeOf(safeStyle));
return 'type_error:SafeStyle';
}
};
/**
* Package-internal utility method to create SafeStyle instances.
*
* @param {string} style The string to initialize the SafeStyle object with.
* @return {!goog.html.SafeStyle} The initialized SafeStyle object.
* @package
*/
goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse = function(
style) {
return new goog.html.SafeStyle().initSecurityPrivateDoNotAccessOrElse_(style);
};
/**
* Called from createSafeStyleSecurityPrivateDoNotAccessOrElse(). This
* method exists only so that the compiler can dead code eliminate static
* fields (like EMPTY) when they're not accessed.
* @param {string} style
* @return {!goog.html.SafeStyle}
* @private
*/
goog.html.SafeStyle.prototype.initSecurityPrivateDoNotAccessOrElse_ = function(
style) {
this.privateDoNotAccessOrElseSafeStyleWrappedValue_ = style;
return this;
};
/**
* A SafeStyle instance corresponding to the empty string.
* @const {!goog.html.SafeStyle}
*/
goog.html.SafeStyle.EMPTY =
goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse('');
/**
* The innocuous string generated by goog.html.SafeStyle.create when passed
* an unsafe value.
* @const {string}
*/
goog.html.SafeStyle.INNOCUOUS_STRING = 'zClosurez';
/**
* A single property value.
* @typedef {string|!goog.string.Const|!goog.html.SafeUrl}
*/
goog.html.SafeStyle.PropertyValue;
/**
* Mapping of property names to their values.
* We don't support numbers even though some values might be numbers (e.g.
* line-height or 0 for any length). The reason is that most numeric values need
* units (e.g. '1px') and allowing numbers could cause users forgetting about
* them.
* @typedef {!Object<string, ?goog.html.SafeStyle.PropertyValue|
* ?Array<!goog.html.SafeStyle.PropertyValue>>}
*/
goog.html.SafeStyle.PropertyMap;
/**
* Creates a new SafeStyle object from the properties specified in the map.
* @param {goog.html.SafeStyle.PropertyMap} map Mapping of property names to
* their values, for example {'margin': '1px'}. Names must consist of
* [-_a-zA-Z0-9]. Values might be strings consisting of
* [-,.'"%_!# a-zA-Z0-9], where " and ' must be properly balanced. We also
* allow simple functions like rgb() and url() which sanitizes its contents.
* Other values must be wrapped in goog.string.Const. URLs might be passed
* as goog.html.SafeUrl which will be wrapped into url(""). We also support
* array whose elements are joined with ' '. Null value causes skipping the
* property.
* @return {!goog.html.SafeStyle}
* @throws {Error} If invalid name is provided.
* @throws {goog.asserts.AssertionError} If invalid value is provided. With
* disabled assertions, invalid value is replaced by
* goog.html.SafeStyle.INNOCUOUS_STRING.
*/
goog.html.SafeStyle.create = function(map) {
var style = '';
for (var name in map) {
if (!/^[-_a-zA-Z0-9]+$/.test(name)) {
throw Error('Name allows only [-_a-zA-Z0-9], got: ' + name);
}
var value = map[name];
if (value == null) {
continue;
}
if (goog.isArray(value)) {
value = goog.array.map(value, goog.html.SafeStyle.sanitizePropertyValue_)
.join(' ');
} else {
value = goog.html.SafeStyle.sanitizePropertyValue_(value);
}
style += name + ':' + value + ';';
}
if (!style) {
return goog.html.SafeStyle.EMPTY;
}
goog.html.SafeStyle.checkStyle_(style);
return goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse(
style);
};
/**
* Checks and converts value to string.
* @param {!goog.html.SafeStyle.PropertyValue} value
* @return {string}
* @private
*/
goog.html.SafeStyle.sanitizePropertyValue_ = function(value) {
if (value instanceof goog.html.SafeUrl) {
var url = goog.html.SafeUrl.unwrap(value);
return 'url("' + url.replace(/</g, '%3c').replace(/[\\"]/g, '\\$&') + '")';
}
var result = value instanceof goog.string.Const ?
goog.string.Const.unwrap(value) :
goog.html.SafeStyle.sanitizePropertyValueString_(String(value));
// These characters can be used to change context and we don't want that even
// with const values.
goog.asserts.assert(!/[{;}]/.test(result), 'Value does not allow [{;}].');
return result;
};
/**
* Checks string value.
* @param {string} value
* @return {string}
* @private
*/
goog.html.SafeStyle.sanitizePropertyValueString_ = function(value) {
var valueWithoutFunctions =
value.replace(goog.html.SafeUrl.FUNCTIONS_RE_, '$1')
.replace(goog.html.SafeUrl.URL_RE_, 'url');
if (!goog.html.SafeStyle.VALUE_RE_.test(valueWithoutFunctions)) {
goog.asserts.fail(
'String value allows only ' + goog.html.SafeStyle.VALUE_ALLOWED_CHARS_ +
' and simple functions, got: ' + value);
return goog.html.SafeStyle.INNOCUOUS_STRING;
} else if (!goog.html.SafeStyle.hasBalancedQuotes_(value)) {
goog.asserts.fail('String value requires balanced quotes, got: ' + value);
return goog.html.SafeStyle.INNOCUOUS_STRING;
}
return goog.html.SafeStyle.sanitizeUrl_(value);
};
/**
* Checks that quotes (" and ') are properly balanced inside a string. Assumes
* that neither escape (\) nor any other character that could result in
* breaking out of a string parsing context are allowed;
* see http://www.w3.org/TR/css3-syntax/#string-token-diagram.
* @param {string} value Untrusted CSS property value.
* @return {boolean} True if property value is safe with respect to quote
* balancedness.
* @private
*/
goog.html.SafeStyle.hasBalancedQuotes_ = function(value) {
var outsideSingle = true;
var outsideDouble = true;
for (var i = 0; i < value.length; i++) {
var c = value.charAt(i);
if (c == "'" && outsideDouble) {
outsideSingle = !outsideSingle;
} else if (c == '"' && outsideSingle) {
outsideDouble = !outsideDouble;
}
}
return outsideSingle && outsideDouble;
};
/**
* Characters allowed in goog.html.SafeStyle.VALUE_RE_.
* @private {string}
*/
goog.html.SafeStyle.VALUE_ALLOWED_CHARS_ = '[-,."\'%_!# a-zA-Z0-9]';
/**
* Regular expression for safe values.
*
* Quotes (" and ') are allowed, but a check must be done elsewhere to ensure
* they're balanced.
*
* ',' allows multiple values to be assigned to the same property
* (e.g. background-attachment or font-family) and hence could allow
* multiple values to get injected, but that should pose no risk of XSS.
*
* The expression checks only for XSS safety, not for CSS validity.
* @const {!RegExp}
* @private
*/
goog.html.SafeStyle.VALUE_RE_ =
new RegExp('^' + goog.html.SafeStyle.VALUE_ALLOWED_CHARS_ + '+$');
/**
* Regular expression for url(). We support URLs allowed by
* https://www.w3.org/TR/css-syntax-3/#url-token-diagram without using escape
* sequences. Use percent-encoding if you need to use special characters like
* backslash.
* @private @const {!RegExp}
*/
goog.html.SafeUrl.URL_RE_ = new RegExp(
'\\b(url\\([ \t\n]*)(' +
'\'[ -&(-\\[\\]-~]*\'' + // Printable characters except ' and \.
'|"[ !#-\\[\\]-~]*"' + // Printable characters except " and \.
'|[!#-&*-\\[\\]-~]*' + // Printable characters except [ "'()\\].
')([ \t\n]*\\))',
'g');
/**
* Regular expression for simple functions.
* @private @const {!RegExp}
*/
goog.html.SafeUrl.FUNCTIONS_RE_ = new RegExp(
'\\b(hsl|hsla|rgb|rgba|(rotate|scale|translate)(X|Y|Z|3d)?)' +
'\\([-0-9a-z.%, ]+\\)',
'g');
/**
* Sanitize URLs inside url().
*
* NOTE: We could also consider using CSS.escape once that's available in the
* browsers. However, loosely matching URL e.g. with url\(.*\) and then escaping
* the contents would result in a slightly different language than CSS leading
* to confusion of users. E.g. url(")") is valid in CSS but it would be invalid
* as seen by our parser. On the other hand, url(\) is invalid in CSS but our
* parser would be fine with it.
*
* @param {string} value Untrusted CSS property value.
* @return {string}
* @private
*/
goog.html.SafeStyle.sanitizeUrl_ = function(value) {
return value.replace(
goog.html.SafeUrl.URL_RE_, function(match, before, url, after) {
var quote = '';
url = url.replace(/^(['"])(.*)\1$/, function(match, start, inside) {
quote = start;
return inside;
});
var sanitized = goog.html.SafeUrl.sanitize(url).getTypedStringValue();
return before + quote + sanitized + quote + after;
});
};
/**
* Creates a new SafeStyle object by concatenating the values.
* @param {...(!goog.html.SafeStyle|!Array<!goog.html.SafeStyle>)} var_args
* SafeStyles to concatenate.
* @return {!goog.html.SafeStyle}
*/
goog.html.SafeStyle.concat = function(var_args) {
var style = '';
/**
* @param {!goog.html.SafeStyle|!Array<!goog.html.SafeStyle>} argument
*/
var addArgument = function(argument) {
if (goog.isArray(argument)) {
goog.array.forEach(argument, addArgument);
} else {
style += goog.html.SafeStyle.unwrap(argument);
}
};
goog.array.forEach(arguments, addArgument);
if (!style) {
return goog.html.SafeStyle.EMPTY;
}
return goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse(
style);
};

View File

@ -0,0 +1,344 @@
// Copyright 2014 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview The SafeStyleSheet type and its builders.
*
* TODO(xtof): Link to document stating type contract.
*/
goog.provide('goog.html.SafeStyleSheet');
goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.html.SafeStyle');
goog.require('goog.object');
goog.require('goog.string');
goog.require('goog.string.Const');
goog.require('goog.string.TypedString');
/**
* A string-like object which represents a CSS style sheet and that carries the
* security type contract that its value, as a string, will not cause untrusted
* script execution (XSS) when evaluated as CSS in a browser.
*
* Instances of this type must be created via the factory method
* {@code goog.html.SafeStyleSheet.fromConstant} and not by invoking its
* constructor. The constructor intentionally takes no parameters and the type
* is immutable; hence only a default instance corresponding to the empty string
* can be obtained via constructor invocation.
*
* A SafeStyleSheet's string representation can safely be interpolated as the
* content of a style element within HTML. The SafeStyleSheet string should
* not be escaped before interpolation.
*
* Values of this type must be composable, i.e. for any two values
* {@code styleSheet1} and {@code styleSheet2} of this type,
* {@code goog.html.SafeStyleSheet.unwrap(styleSheet1) +
* goog.html.SafeStyleSheet.unwrap(styleSheet2)} must itself be a value that
* satisfies the SafeStyleSheet type constraint. This requirement implies that
* for any value {@code styleSheet} of this type,
* {@code goog.html.SafeStyleSheet.unwrap(styleSheet1)} must end in
* "beginning of rule" context.
* A SafeStyleSheet can be constructed via security-reviewed unchecked
* conversions. In this case producers of SafeStyleSheet must ensure themselves
* that the SafeStyleSheet does not contain unsafe script. Note in particular
* that {@code &lt;} is dangerous, even when inside CSS strings, and so should
* always be forbidden or CSS-escaped in user controlled input. For example, if
* {@code &lt;/style&gt;&lt;script&gt;evil&lt;/script&gt;"} were interpolated
* inside a CSS string, it would break out of the context of the original
* style element and {@code evil} would execute. Also note that within an HTML
* style (raw text) element, HTML character references, such as
* {@code &amp;lt;}, are not allowed. See
*
http://www.w3.org/TR/html5/scripting-1.html#restrictions-for-contents-of-script-elements
* (similar considerations apply to the style element).
*
* @see goog.html.SafeStyleSheet#fromConstant
* @constructor
* @final
* @struct
* @implements {goog.string.TypedString}
*/
goog.html.SafeStyleSheet = function() {
/**
* The contained value of this SafeStyleSheet. The field has a purposely
* ugly name to make (non-compiled) code that attempts to directly access this
* field stand out.
* @private {string}
*/
this.privateDoNotAccessOrElseSafeStyleSheetWrappedValue_ = '';
/**
* A type marker used to implement additional run-time type checking.
* @see goog.html.SafeStyleSheet#unwrap
* @const {!Object}
* @private
*/
this.SAFE_STYLE_SHEET_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ =
goog.html.SafeStyleSheet.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_;
};
/**
* @override
* @const
*/
goog.html.SafeStyleSheet.prototype.implementsGoogStringTypedString = true;
/**
* Type marker for the SafeStyleSheet type, used to implement additional
* run-time type checking.
* @const {!Object}
* @private
*/
goog.html.SafeStyleSheet.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};
/**
* Creates a style sheet consisting of one selector and one style definition.
* Use {@link goog.html.SafeStyleSheet.concat} to create longer style sheets.
* This function doesn't support @import, @media and similar constructs.
* @param {string} selector CSS selector, e.g. '#id' or 'tag .class, #id'. We
* support CSS3 selectors: https://w3.org/TR/css3-selectors/#selectors.
* @param {!goog.html.SafeStyle.PropertyMap|!goog.html.SafeStyle} style Style
* definition associated with the selector.
* @return {!goog.html.SafeStyleSheet}
* @throws {Error} If invalid selector is provided.
*/
goog.html.SafeStyleSheet.createRule = function(selector, style) {
if (goog.string.contains(selector, '<')) {
throw Error('Selector does not allow \'<\', got: ' + selector);
}
// Remove strings.
var selectorToCheck =
selector.replace(/('|")((?!\1)[^\r\n\f\\]|\\[\s\S])*\1/g, '');
// Check characters allowed in CSS3 selectors.
if (!/^[-_a-zA-Z0-9#.:* ,>+~[\]()=^$|]+$/.test(selectorToCheck)) {
throw Error(
'Selector allows only [-_a-zA-Z0-9#.:* ,>+~[\\]()=^$|] and ' +
'strings, got: ' + selector);
}
// Check balanced () and [].
if (!goog.html.SafeStyleSheet.hasBalancedBrackets_(selectorToCheck)) {
throw Error('() and [] in selector must be balanced, got: ' + selector);
}
if (!(style instanceof goog.html.SafeStyle)) {
style = goog.html.SafeStyle.create(style);
}
var styleSheet = selector + '{' + goog.html.SafeStyle.unwrap(style) + '}';
return goog.html.SafeStyleSheet
.createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(styleSheet);
};
/**
* Checks if a string has balanced () and [] brackets.
* @param {string} s String to check.
* @return {boolean}
* @private
*/
goog.html.SafeStyleSheet.hasBalancedBrackets_ = function(s) {
var brackets = {'(': ')', '[': ']'};
var expectedBrackets = [];
for (var i = 0; i < s.length; i++) {
var ch = s[i];
if (brackets[ch]) {
expectedBrackets.push(brackets[ch]);
} else if (goog.object.contains(brackets, ch)) {
if (expectedBrackets.pop() != ch) {
return false;
}
}
}
return expectedBrackets.length == 0;
};
/**
* Creates a new SafeStyleSheet object by concatenating values.
* @param {...(!goog.html.SafeStyleSheet|!Array<!goog.html.SafeStyleSheet>)}
* var_args Values to concatenate.
* @return {!goog.html.SafeStyleSheet}
*/
goog.html.SafeStyleSheet.concat = function(var_args) {
var result = '';
/**
* @param {!goog.html.SafeStyleSheet|!Array<!goog.html.SafeStyleSheet>}
* argument
*/
var addArgument = function(argument) {
if (goog.isArray(argument)) {
goog.array.forEach(argument, addArgument);
} else {
result += goog.html.SafeStyleSheet.unwrap(argument);
}
};
goog.array.forEach(arguments, addArgument);
return goog.html.SafeStyleSheet
.createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(result);
};
/**
* Creates a SafeStyleSheet object from a compile-time constant string.
*
* {@code styleSheet} must not have any &lt; characters in it, so that
* the syntactic structure of the surrounding HTML is not affected.
*
* @param {!goog.string.Const} styleSheet A compile-time-constant string from
* which to create a SafeStyleSheet.
* @return {!goog.html.SafeStyleSheet} A SafeStyleSheet object initialized to
* {@code styleSheet}.
*/
goog.html.SafeStyleSheet.fromConstant = function(styleSheet) {
var styleSheetString = goog.string.Const.unwrap(styleSheet);
if (styleSheetString.length === 0) {
return goog.html.SafeStyleSheet.EMPTY;
}
// > is a valid character in CSS selectors and there's no strict need to
// block it if we already block <.
goog.asserts.assert(
!goog.string.contains(styleSheetString, '<'),
"Forbidden '<' character in style sheet string: " + styleSheetString);
return goog.html.SafeStyleSheet
.createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(styleSheetString);
};
/**
* Returns this SafeStyleSheet's value as a string.
*
* IMPORTANT: In code where it is security relevant that an object's type is
* indeed {@code SafeStyleSheet}, use {@code goog.html.SafeStyleSheet.unwrap}
* instead of this method. If in doubt, assume that it's security relevant. In
* particular, note that goog.html functions which return a goog.html type do
* not guarantee the returned instance is of the right type. For example:
*
* <pre>
* var fakeSafeHtml = new String('fake');
* fakeSafeHtml.__proto__ = goog.html.SafeHtml.prototype;
* var newSafeHtml = goog.html.SafeHtml.htmlEscape(fakeSafeHtml);
* // newSafeHtml is just an alias for fakeSafeHtml, it's passed through by
* // goog.html.SafeHtml.htmlEscape() as fakeSafeHtml
* // instanceof goog.html.SafeHtml.
* </pre>
*
* @see goog.html.SafeStyleSheet#unwrap
* @override
*/
goog.html.SafeStyleSheet.prototype.getTypedStringValue = function() {
return this.privateDoNotAccessOrElseSafeStyleSheetWrappedValue_;
};
if (goog.DEBUG) {
/**
* Returns a debug string-representation of this value.
*
* To obtain the actual string value wrapped in a SafeStyleSheet, use
* {@code goog.html.SafeStyleSheet.unwrap}.
*
* @see goog.html.SafeStyleSheet#unwrap
* @override
*/
goog.html.SafeStyleSheet.prototype.toString = function() {
return 'SafeStyleSheet{' +
this.privateDoNotAccessOrElseSafeStyleSheetWrappedValue_ + '}';
};
}
/**
* Performs a runtime check that the provided object is indeed a
* SafeStyleSheet object, and returns its value.
*
* @param {!goog.html.SafeStyleSheet} safeStyleSheet The object to extract from.
* @return {string} The safeStyleSheet object's contained string, unless
* the run-time type check fails. In that case, {@code unwrap} returns an
* innocuous string, or, if assertions are enabled, throws
* {@code goog.asserts.AssertionError}.
*/
goog.html.SafeStyleSheet.unwrap = function(safeStyleSheet) {
// Perform additional Run-time type-checking to ensure that
// safeStyleSheet is indeed an instance of the expected type. This
// provides some additional protection against security bugs due to
// application code that disables type checks.
// Specifically, the following checks are performed:
// 1. The object is an instance of the expected type.
// 2. The object is not an instance of a subclass.
// 3. The object carries a type marker for the expected type. "Faking" an
// object requires a reference to the type marker, which has names intended
// to stand out in code reviews.
if (safeStyleSheet instanceof goog.html.SafeStyleSheet &&
safeStyleSheet.constructor === goog.html.SafeStyleSheet &&
safeStyleSheet
.SAFE_STYLE_SHEET_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ ===
goog.html.SafeStyleSheet.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_) {
return safeStyleSheet.privateDoNotAccessOrElseSafeStyleSheetWrappedValue_;
} else {
goog.asserts.fail('expected object of type SafeStyleSheet, got \'' +
safeStyleSheet + '\' of type ' + goog.typeOf(safeStyleSheet));
return 'type_error:SafeStyleSheet';
}
};
/**
* Package-internal utility method to create SafeStyleSheet instances.
*
* @param {string} styleSheet The string to initialize the SafeStyleSheet
* object with.
* @return {!goog.html.SafeStyleSheet} The initialized SafeStyleSheet object.
* @package
*/
goog.html.SafeStyleSheet.createSafeStyleSheetSecurityPrivateDoNotAccessOrElse =
function(styleSheet) {
return new goog.html.SafeStyleSheet().initSecurityPrivateDoNotAccessOrElse_(
styleSheet);
};
/**
* Called from createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(). This
* method exists only so that the compiler can dead code eliminate static
* fields (like EMPTY) when they're not accessed.
* @param {string} styleSheet
* @return {!goog.html.SafeStyleSheet}
* @private
*/
goog.html.SafeStyleSheet.prototype.initSecurityPrivateDoNotAccessOrElse_ =
function(styleSheet) {
this.privateDoNotAccessOrElseSafeStyleSheetWrappedValue_ = styleSheet;
return this;
};
/**
* A SafeStyleSheet instance corresponding to the empty string.
* @const {!goog.html.SafeStyleSheet}
*/
goog.html.SafeStyleSheet.EMPTY =
goog.html.SafeStyleSheet
.createSafeStyleSheetSecurityPrivateDoNotAccessOrElse('');

View File

@ -0,0 +1,454 @@
// Copyright 2013 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview The SafeUrl type and its builders.
*
* TODO(xtof): Link to document stating type contract.
*/
goog.provide('goog.html.SafeUrl');
goog.require('goog.asserts');
goog.require('goog.fs.url');
goog.require('goog.html.TrustedResourceUrl');
goog.require('goog.i18n.bidi.Dir');
goog.require('goog.i18n.bidi.DirectionalString');
goog.require('goog.string');
goog.require('goog.string.Const');
goog.require('goog.string.TypedString');
/**
* A string that is safe to use in URL context in DOM APIs and HTML documents.
*
* A SafeUrl is a string-like object that carries the security type contract
* that its value as a string will not cause untrusted script execution
* when evaluated as a hyperlink URL in a browser.
*
* Values of this type are guaranteed to be safe to use in URL/hyperlink
* contexts, such as assignment to URL-valued DOM properties, in the sense that
* the use will not result in a Cross-Site-Scripting vulnerability. Similarly,
* SafeUrls can be interpolated into the URL context of an HTML template (e.g.,
* inside a href attribute). However, appropriate HTML-escaping must still be
* applied.
*
* Note that, as documented in {@code goog.html.SafeUrl.unwrap}, this type's
* contract does not guarantee that instances are safe to interpolate into HTML
* without appropriate escaping.
*
* Note also that this type's contract does not imply any guarantees regarding
* the resource the URL refers to. In particular, SafeUrls are <b>not</b>
* safe to use in a context where the referred-to resource is interpreted as
* trusted code, e.g., as the src of a script tag.
*
* Instances of this type must be created via the factory methods
* ({@code goog.html.SafeUrl.fromConstant}, {@code goog.html.SafeUrl.sanitize}),
* etc and not by invoking its constructor. The constructor intentionally
* takes no parameters and the type is immutable; hence only a default instance
* corresponding to the empty string can be obtained via constructor invocation.
*
* @see goog.html.SafeUrl#fromConstant
* @see goog.html.SafeUrl#from
* @see goog.html.SafeUrl#sanitize
* @constructor
* @final
* @struct
* @implements {goog.i18n.bidi.DirectionalString}
* @implements {goog.string.TypedString}
*/
goog.html.SafeUrl = function() {
/**
* The contained value of this SafeUrl. The field has a purposely ugly
* name to make (non-compiled) code that attempts to directly access this
* field stand out.
* @private {string}
*/
this.privateDoNotAccessOrElseSafeHtmlWrappedValue_ = '';
/**
* A type marker used to implement additional run-time type checking.
* @see goog.html.SafeUrl#unwrap
* @const {!Object}
* @private
*/
this.SAFE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ =
goog.html.SafeUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_;
};
/**
* The innocuous string generated by goog.html.SafeUrl.sanitize when passed
* an unsafe URL.
*
* about:invalid is registered in
* http://www.w3.org/TR/css3-values/#about-invalid.
* http://tools.ietf.org/html/rfc6694#section-2.2.1 permits about URLs to
* contain a fragment, which is not to be considered when determining if an
* about URL is well-known.
*
* Using about:invalid seems preferable to using a fixed data URL, since
* browsers might choose to not report CSP violations on it, as legitimate
* CSS function calls to attr() can result in this URL being produced. It is
* also a standard URL which matches exactly the semantics we need:
* "The about:invalid URI references a non-existent document with a generic
* error condition. It can be used when a URI is necessary, but the default
* value shouldn't be resolveable as any type of document".
*
* @const {string}
*/
goog.html.SafeUrl.INNOCUOUS_STRING = 'about:invalid#zClosurez';
/**
* @override
* @const
*/
goog.html.SafeUrl.prototype.implementsGoogStringTypedString = true;
/**
* Returns this SafeUrl's value a string.
*
* IMPORTANT: In code where it is security relevant that an object's type is
* indeed {@code SafeUrl}, use {@code goog.html.SafeUrl.unwrap} instead of this
* method. If in doubt, assume that it's security relevant. In particular, note
* that goog.html functions which return a goog.html type do not guarantee that
* the returned instance is of the right type. For example:
*
* <pre>
* var fakeSafeHtml = new String('fake');
* fakeSafeHtml.__proto__ = goog.html.SafeHtml.prototype;
* var newSafeHtml = goog.html.SafeHtml.htmlEscape(fakeSafeHtml);
* // newSafeHtml is just an alias for fakeSafeHtml, it's passed through by
* // goog.html.SafeHtml.htmlEscape() as fakeSafeHtml instanceof
* // goog.html.SafeHtml.
* </pre>
*
* IMPORTANT: The guarantees of the SafeUrl type contract only extend to the
* behavior of browsers when interpreting URLs. Values of SafeUrl objects MUST
* be appropriately escaped before embedding in a HTML document. Note that the
* required escaping is context-sensitive (e.g. a different escaping is
* required for embedding a URL in a style property within a style
* attribute, as opposed to embedding in a href attribute).
*
* @see goog.html.SafeUrl#unwrap
* @override
*/
goog.html.SafeUrl.prototype.getTypedStringValue = function() {
return this.privateDoNotAccessOrElseSafeHtmlWrappedValue_;
};
/**
* @override
* @const
*/
goog.html.SafeUrl.prototype.implementsGoogI18nBidiDirectionalString = true;
/**
* Returns this URLs directionality, which is always {@code LTR}.
* @override
*/
goog.html.SafeUrl.prototype.getDirection = function() {
return goog.i18n.bidi.Dir.LTR;
};
if (goog.DEBUG) {
/**
* Returns a debug string-representation of this value.
*
* To obtain the actual string value wrapped in a SafeUrl, use
* {@code goog.html.SafeUrl.unwrap}.
*
* @see goog.html.SafeUrl#unwrap
* @override
*/
goog.html.SafeUrl.prototype.toString = function() {
return 'SafeUrl{' + this.privateDoNotAccessOrElseSafeHtmlWrappedValue_ +
'}';
};
}
/**
* Performs a runtime check that the provided object is indeed a SafeUrl
* object, and returns its value.
*
* IMPORTANT: The guarantees of the SafeUrl type contract only extend to the
* behavior of browsers when interpreting URLs. Values of SafeUrl objects MUST
* be appropriately escaped before embedding in a HTML document. Note that the
* required escaping is context-sensitive (e.g. a different escaping is
* required for embedding a URL in a style property within a style
* attribute, as opposed to embedding in a href attribute).
*
* @param {!goog.html.SafeUrl} safeUrl The object to extract from.
* @return {string} The SafeUrl object's contained string, unless the run-time
* type check fails. In that case, {@code unwrap} returns an innocuous
* string, or, if assertions are enabled, throws
* {@code goog.asserts.AssertionError}.
*/
goog.html.SafeUrl.unwrap = function(safeUrl) {
// Perform additional Run-time type-checking to ensure that safeUrl is indeed
// an instance of the expected type. This provides some additional protection
// against security bugs due to application code that disables type checks.
// Specifically, the following checks are performed:
// 1. The object is an instance of the expected type.
// 2. The object is not an instance of a subclass.
// 3. The object carries a type marker for the expected type. "Faking" an
// object requires a reference to the type marker, which has names intended
// to stand out in code reviews.
if (safeUrl instanceof goog.html.SafeUrl &&
safeUrl.constructor === goog.html.SafeUrl &&
safeUrl.SAFE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ ===
goog.html.SafeUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_) {
return safeUrl.privateDoNotAccessOrElseSafeHtmlWrappedValue_;
} else {
goog.asserts.fail('expected object of type SafeUrl, got \'' +
safeUrl + '\' of type ' + goog.typeOf(safeUrl));
return 'type_error:SafeUrl';
}
};
/**
* Creates a SafeUrl object from a compile-time constant string.
*
* Compile-time constant strings are inherently program-controlled and hence
* trusted.
*
* @param {!goog.string.Const} url A compile-time-constant string from which to
* create a SafeUrl.
* @return {!goog.html.SafeUrl} A SafeUrl object initialized to {@code url}.
*/
goog.html.SafeUrl.fromConstant = function(url) {
return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(
goog.string.Const.unwrap(url));
};
/**
* A pattern that matches Blob or data types that can have SafeUrls created
* from URL.createObjectURL(blob) or via a data: URI.
* @const
* @private
*/
goog.html.SAFE_MIME_TYPE_PATTERN_ = new RegExp(
'^(?:audio/(?:3gpp|3gpp2|aac|midi|mp4|mpeg|ogg|x-m4a|x-wav|webm)|' +
'image/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|' +
'text/csv|' +
'video/(?:mpeg|mp4|ogg|webm))$',
'i');
/**
* Creates a SafeUrl wrapping a blob URL for the given {@code blob}.
*
* The blob URL is created with {@code URL.createObjectURL}. If the MIME type
* for {@code blob} is not of a known safe audio, image or video MIME type,
* then the SafeUrl will wrap {@link #INNOCUOUS_STRING}.
*
* @see http://www.w3.org/TR/FileAPI/#url
* @param {!Blob} blob
* @return {!goog.html.SafeUrl} The blob URL, or an innocuous string wrapped
* as a SafeUrl.
*/
goog.html.SafeUrl.fromBlob = function(blob) {
var url = goog.html.SAFE_MIME_TYPE_PATTERN_.test(blob.type) ?
goog.fs.url.createObjectUrl(blob) :
goog.html.SafeUrl.INNOCUOUS_STRING;
return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);
};
/**
* Matches a base-64 data URL, with the first match group being the MIME type.
* @const
* @private
*/
goog.html.DATA_URL_PATTERN_ = /^data:([^;,]*);base64,[a-z0-9+\/]+=*$/i;
/**
* Creates a SafeUrl wrapping a data: URL, after validating it matches a
* known-safe audio, image or video MIME type.
*
* @param {string} dataUrl A valid base64 data URL with one of the whitelisted
* audio, image or video MIME types.
* @return {!goog.html.SafeUrl} A matching safe URL, or {@link INNOCUOUS_STRING}
* wrapped as a SafeUrl if it does not pass.
*/
goog.html.SafeUrl.fromDataUrl = function(dataUrl) {
// There's a slight risk here that a browser sniffs the content type if it
// doesn't know the MIME type and executes HTML within the data: URL. For this
// to cause XSS it would also have to execute the HTML in the same origin
// of the page with the link. It seems unlikely that both of these will
// happen, particularly in not really old IEs.
var match = dataUrl.match(goog.html.DATA_URL_PATTERN_);
var valid = match && goog.html.SAFE_MIME_TYPE_PATTERN_.test(match[1]);
return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(
valid ? dataUrl : goog.html.SafeUrl.INNOCUOUS_STRING);
};
/**
* Creates a SafeUrl wrapping a tel: URL.
*
* @param {string} telUrl A tel URL.
* @return {!goog.html.SafeUrl} A matching safe URL, or {@link INNOCUOUS_STRING}
* wrapped as a SafeUrl if it does not pass.
*/
goog.html.SafeUrl.fromTelUrl = function(telUrl) {
// There's a risk that a tel: URL could immediately place a call once
// clicked, without requiring user confirmation. For that reason it is
// handled in this separate function.
if (!goog.string.caseInsensitiveStartsWith(telUrl, 'tel:')) {
telUrl = goog.html.SafeUrl.INNOCUOUS_STRING;
}
return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(
telUrl);
};
/**
* Creates a SafeUrl from TrustedResourceUrl. This is safe because
* TrustedResourceUrl is more tightly restricted than SafeUrl.
*
* @param {!goog.html.TrustedResourceUrl} trustedResourceUrl
* @return {!goog.html.SafeUrl}
*/
goog.html.SafeUrl.fromTrustedResourceUrl = function(trustedResourceUrl) {
return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(
goog.html.TrustedResourceUrl.unwrap(trustedResourceUrl));
};
/**
* A pattern that recognizes a commonly useful subset of URLs that satisfy
* the SafeUrl contract.
*
* This regular expression matches a subset of URLs that will not cause script
* execution if used in URL context within a HTML document. Specifically, this
* regular expression matches if (comment from here on and regex copied from
* Soy's EscapingConventions):
* (1) Either a protocol in a whitelist (http, https, mailto or ftp).
* (2) or no protocol. A protocol must be followed by a colon. The below
* allows that by allowing colons only after one of the characters [/?#].
* A colon after a hash (#) must be in the fragment.
* Otherwise, a colon after a (?) must be in a query.
* Otherwise, a colon after a single solidus (/) must be in a path.
* Otherwise, a colon after a double solidus (//) must be in the authority
* (before port).
*
* @private
* @const {!RegExp}
*/
goog.html.SAFE_URL_PATTERN_ =
/^(?:(?:https?|mailto|ftp):|[^:/?#]*(?:[/?#]|$))/i;
/**
* Creates a SafeUrl object from {@code url}. If {@code url} is a
* goog.html.SafeUrl then it is simply returned. Otherwise the input string is
* validated to match a pattern of commonly used safe URLs.
*
* {@code url} may be a URL with the http, https, mailto or ftp scheme,
* or a relative URL (i.e., a URL without a scheme; specifically, a
* scheme-relative, absolute-path-relative, or path-relative URL).
*
* @see http://url.spec.whatwg.org/#concept-relative-url
* @param {string|!goog.string.TypedString} url The URL to validate.
* @return {!goog.html.SafeUrl} The validated URL, wrapped as a SafeUrl.
*/
goog.html.SafeUrl.sanitize = function(url) {
if (url instanceof goog.html.SafeUrl) {
return url;
} else if (url.implementsGoogStringTypedString) {
url = url.getTypedStringValue();
} else {
url = String(url);
}
if (!goog.html.SAFE_URL_PATTERN_.test(url)) {
url = goog.html.SafeUrl.INNOCUOUS_STRING;
}
return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);
};
/**
* Creates a SafeUrl object from {@code url}. If {@code url} is a
* goog.html.SafeUrl then it is simply returned. Otherwise the input string is
* validated to match a pattern of commonly used safe URLs.
*
* {@code url} may be a URL with the http, https, mailto or ftp scheme,
* or a relative URL (i.e., a URL without a scheme; specifically, a
* scheme-relative, absolute-path-relative, or path-relative URL).
*
* This function asserts (using goog.asserts) that the URL matches this pattern.
* If it does not, in addition to failing the assert, an innocous URL will be
* returned.
*
* @see http://url.spec.whatwg.org/#concept-relative-url
* @param {string|!goog.string.TypedString} url The URL to validate.
* @return {!goog.html.SafeUrl} The validated URL, wrapped as a SafeUrl.
*/
goog.html.SafeUrl.sanitizeAssertUnchanged = function(url) {
if (url instanceof goog.html.SafeUrl) {
return url;
} else if (url.implementsGoogStringTypedString) {
url = url.getTypedStringValue();
} else {
url = String(url);
}
if (!goog.asserts.assert(goog.html.SAFE_URL_PATTERN_.test(url))) {
url = goog.html.SafeUrl.INNOCUOUS_STRING;
}
return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);
};
/**
* Type marker for the SafeUrl type, used to implement additional run-time
* type checking.
* @const {!Object}
* @private
*/
goog.html.SafeUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};
/**
* Package-internal utility method to create SafeUrl instances.
*
* @param {string} url The string to initialize the SafeUrl object with.
* @return {!goog.html.SafeUrl} The initialized SafeUrl object.
* @package
*/
goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse = function(
url) {
var safeUrl = new goog.html.SafeUrl();
safeUrl.privateDoNotAccessOrElseSafeHtmlWrappedValue_ = url;
return safeUrl;
};
/**
* A SafeUrl corresponding to the special about:blank url.
* @const {!goog.html.SafeUrl}
*/
goog.html.SafeUrl.ABOUT_BLANK =
goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(
'about:blank');

View File

@ -0,0 +1,408 @@
// Copyright 2013 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview The TrustedResourceUrl type and its builders.
*
* TODO(xtof): Link to document stating type contract.
*/
goog.provide('goog.html.TrustedResourceUrl');
goog.require('goog.asserts');
goog.require('goog.i18n.bidi.Dir');
goog.require('goog.i18n.bidi.DirectionalString');
goog.require('goog.string.Const');
goog.require('goog.string.TypedString');
/**
* A URL which is under application control and from which script, CSS, and
* other resources that represent executable code, can be fetched.
*
* Given that the URL can only be constructed from strings under application
* control and is used to load resources, bugs resulting in a malformed URL
* should not have a security impact and are likely to be easily detectable
* during testing. Given the wide number of non-RFC compliant URLs in use,
* stricter validation could prevent some applications from being able to use
* this type.
*
* Instances of this type must be created via the factory method,
* ({@code fromConstant}, {@code fromConstants}, {@code format} or {@code
* formatWithParams}), and not by invoking its constructor. The constructor
* intentionally takes no parameters and the type is immutable; hence only a
* default instance corresponding to the empty string can be obtained via
* constructor invocation.
*
* @see goog.html.TrustedResourceUrl#fromConstant
* @constructor
* @final
* @struct
* @implements {goog.i18n.bidi.DirectionalString}
* @implements {goog.string.TypedString}
*/
goog.html.TrustedResourceUrl = function() {
/**
* The contained value of this TrustedResourceUrl. The field has a purposely
* ugly name to make (non-compiled) code that attempts to directly access this
* field stand out.
* @private {string}
*/
this.privateDoNotAccessOrElseTrustedResourceUrlWrappedValue_ = '';
/**
* A type marker used to implement additional run-time type checking.
* @see goog.html.TrustedResourceUrl#unwrap
* @const {!Object}
* @private
*/
this.TRUSTED_RESOURCE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ =
goog.html.TrustedResourceUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_;
};
/**
* @override
* @const
*/
goog.html.TrustedResourceUrl.prototype.implementsGoogStringTypedString = true;
/**
* Returns this TrustedResourceUrl's value as a string.
*
* IMPORTANT: In code where it is security relevant that an object's type is
* indeed {@code TrustedResourceUrl}, use
* {@code goog.html.TrustedResourceUrl.unwrap} instead of this method. If in
* doubt, assume that it's security relevant. In particular, note that
* goog.html functions which return a goog.html type do not guarantee that
* the returned instance is of the right type. For example:
*
* <pre>
* var fakeSafeHtml = new String('fake');
* fakeSafeHtml.__proto__ = goog.html.SafeHtml.prototype;
* var newSafeHtml = goog.html.SafeHtml.htmlEscape(fakeSafeHtml);
* // newSafeHtml is just an alias for fakeSafeHtml, it's passed through by
* // goog.html.SafeHtml.htmlEscape() as fakeSafeHtml instanceof
* // goog.html.SafeHtml.
* </pre>
*
* @see goog.html.TrustedResourceUrl#unwrap
* @override
*/
goog.html.TrustedResourceUrl.prototype.getTypedStringValue = function() {
return this.privateDoNotAccessOrElseTrustedResourceUrlWrappedValue_;
};
/**
* @override
* @const
*/
goog.html.TrustedResourceUrl.prototype.implementsGoogI18nBidiDirectionalString =
true;
/**
* Returns this URLs directionality, which is always {@code LTR}.
* @override
*/
goog.html.TrustedResourceUrl.prototype.getDirection = function() {
return goog.i18n.bidi.Dir.LTR;
};
if (goog.DEBUG) {
/**
* Returns a debug string-representation of this value.
*
* To obtain the actual string value wrapped in a TrustedResourceUrl, use
* {@code goog.html.TrustedResourceUrl.unwrap}.
*
* @see goog.html.TrustedResourceUrl#unwrap
* @override
*/
goog.html.TrustedResourceUrl.prototype.toString = function() {
return 'TrustedResourceUrl{' +
this.privateDoNotAccessOrElseTrustedResourceUrlWrappedValue_ + '}';
};
}
/**
* Performs a runtime check that the provided object is indeed a
* TrustedResourceUrl object, and returns its value.
*
* @param {!goog.html.TrustedResourceUrl} trustedResourceUrl The object to
* extract from.
* @return {string} The trustedResourceUrl object's contained string, unless
* the run-time type check fails. In that case, {@code unwrap} returns an
* innocuous string, or, if assertions are enabled, throws
* {@code goog.asserts.AssertionError}.
*/
goog.html.TrustedResourceUrl.unwrap = function(trustedResourceUrl) {
// Perform additional Run-time type-checking to ensure that
// trustedResourceUrl is indeed an instance of the expected type. This
// provides some additional protection against security bugs due to
// application code that disables type checks.
// Specifically, the following checks are performed:
// 1. The object is an instance of the expected type.
// 2. The object is not an instance of a subclass.
// 3. The object carries a type marker for the expected type. "Faking" an
// object requires a reference to the type marker, which has names intended
// to stand out in code reviews.
if (trustedResourceUrl instanceof goog.html.TrustedResourceUrl &&
trustedResourceUrl.constructor === goog.html.TrustedResourceUrl &&
trustedResourceUrl
.TRUSTED_RESOURCE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ ===
goog.html.TrustedResourceUrl
.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_) {
return trustedResourceUrl
.privateDoNotAccessOrElseTrustedResourceUrlWrappedValue_;
} else {
goog.asserts.fail('expected object of type TrustedResourceUrl, got \'' +
trustedResourceUrl + '\' of type ' + goog.typeOf(trustedResourceUrl));
return 'type_error:TrustedResourceUrl';
}
};
/**
* Creates a TrustedResourceUrl from a format string and arguments.
*
* The arguments for interpolation into the format string map labels to values.
* Values of type `goog.string.Const` are interpolated without modifcation.
* Values of other types are cast to string and encoded with
* encodeURIComponent.
*
* `%{<label>}` markers are used in the format string to indicate locations
* to be interpolated with the valued mapped to the given label. `<label>`
* must contain only alphanumeric and `_` characters.
*
* The format string must start with one of the following:
* - `https://<origin>/`
* - `//<origin>/`
* - `/<pathStart>`
* - `about:blank`
*
* `<origin>` must contain only alphanumeric or any of the following: `-.:[]`.
* `<pathStart>` is any character except `/` and `\`.
*
* Example usage:
*
* var url = goog.html.TrustedResourceUrl.format(goog.string.Const.from(
* 'https://www.google.com/search?q=%{query}), {'query': searchTerm});
*
* var url = goog.html.TrustedResourceUrl.format(goog.string.Const.from(
* '//www.youtube.com/v/%{videoId}?hl=en&fs=1%{autoplay}'), {
* 'videoId': videoId,
* 'autoplay': opt_autoplay ?
* goog.string.Const.EMPTY : goog.string.Const.from('&autoplay=1')
* });
*
* While this function can be used to create a TrustedResourceUrl from only
* constants, fromConstant() and fromConstants() are generally preferable for
* that purpose.
*
* @param {!goog.string.Const} format The format string.
* @param {!Object<string, (string|number|!goog.string.Const)>} args Mapping
* of labels to values to be interpolated into the format string.
* goog.string.Const values are interpolated without encoding.
* @return {!goog.html.TrustedResourceUrl}
* @throws {!Error} On an invalid format string or if a label used in the
* the format string is not present in args.
*/
goog.html.TrustedResourceUrl.format = function(format, args) {
var result = goog.html.TrustedResourceUrl.format_(format, args);
return goog.html.TrustedResourceUrl
.createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(result);
};
/**
* String version of TrustedResourceUrl.format.
* @param {!goog.string.Const} format
* @param {!Object<string, (string|number|!goog.string.Const)>} args
* @return {string}
* @throws {!Error}
* @private
*/
goog.html.TrustedResourceUrl.format_ = function(format, args) {
var formatStr = goog.string.Const.unwrap(format);
if (!goog.html.TrustedResourceUrl.BASE_URL_.test(formatStr)) {
throw new Error('Invalid TrustedResourceUrl format: ' + formatStr);
}
return formatStr.replace(
goog.html.TrustedResourceUrl.FORMAT_MARKER_, function(match, id) {
if (!Object.prototype.hasOwnProperty.call(args, id)) {
throw new Error(
'Found marker, "' + id + '", in format string, "' + formatStr +
'", but no valid label mapping found ' +
'in args: ' + JSON.stringify(args));
}
var arg = args[id];
if (arg instanceof goog.string.Const) {
return goog.string.Const.unwrap(arg);
} else {
return encodeURIComponent(String(arg));
}
});
};
/**
* @private @const {!RegExp}
*/
goog.html.TrustedResourceUrl.FORMAT_MARKER_ = /%{(\w+)}/g;
/**
* The URL must be absolute, scheme-relative or path-absolute. So it must
* start with:
* - https:// followed by allowed origin characters.
* - // followed by allowed origin characters.
* - / not followed by / or \. There will only be an absolute path.
*
* Based on
* https://url.spec.whatwg.org/commit-snapshots/56b74ce7cca8883eab62e9a12666e2fac665d03d/#url-parsing
* an initial / which is not followed by another / or \ will end up in the "path
* state" and from there it can only go to "fragment state" and "query state".
*
* We don't enforce a well-formed domain name. So '.' or '1.2' are valid.
* That's ok because the origin comes from a compile-time constant.
*
* A regular expression is used instead of goog.uri for several reasons:
* - Strictness. E.g. we don't want any userinfo component and we don't
* want '/./, nor \' in the first path component.
* - Small trusted base. goog.uri is generic and might need to change,
* reasoning about all the ways it can parse a URL now and in the future
* is error-prone.
* - Code size. We expect many calls to .format(), many of which might
* not be using goog.uri.
* - Simplicity. Using goog.uri would likely not result in simpler nor shorter
* code.
* @private @const {!RegExp}
*/
goog.html.TrustedResourceUrl.BASE_URL_ =
/^(?:https:)?\/\/[0-9a-z.:[\]-]+\/|^\/[^\/\\]|^about:blank(#|$)/i;
/**
* Formats the URL same as TrustedResourceUrl.format and then adds extra URL
* parameters.
*
* Example usage:
*
* // Creates '//www.youtube.com/v/abc?autoplay=1' for videoId='abc' and
* // opt_autoplay=1. Creates '//www.youtube.com/v/abc' for videoId='abc'
* // and opt_autoplay=undefined.
* var url = goog.html.TrustedResourceUrl.formatWithParams(
* goog.string.Const.from('//www.youtube.com/v/%{videoId}'),
* {'videoId': videoId},
* {'autoplay': opt_autoplay});
*
* @param {!goog.string.Const} format The format string.
* @param {!Object<string, (string|number|!goog.string.Const)>} args Mapping
* of labels to values to be interpolated into the format string.
* goog.string.Const values are interpolated without encoding.
* @param {!Object<string, *>} params Parameters to add to URL. Parameters with
* value {@code null} or {@code undefined} are skipped. Both keys and values
* are encoded. Note that JavaScript doesn't guarantee the order of values
* in an object which might result in non-deterministic order of the
* parameters. However, browsers currently preserve the order.
* @return {!goog.html.TrustedResourceUrl}
* @throws {!Error} On an invalid format string or if a label used in the
* the format string is not present in args.
*/
goog.html.TrustedResourceUrl.formatWithParams = function(format, args, params) {
var url = goog.html.TrustedResourceUrl.format_(format, args);
var separator = /\?/.test(url) ? '&' : '?';
for (var key in params) {
if (params[key] == null) {
continue;
}
url += separator + encodeURIComponent(key) + '=' +
encodeURIComponent(String(params[key]));
separator = '&';
}
return goog.html.TrustedResourceUrl
.createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(url);
};
/**
* Creates a TrustedResourceUrl object from a compile-time constant string.
*
* Compile-time constant strings are inherently program-controlled and hence
* trusted.
*
* @param {!goog.string.Const} url A compile-time-constant string from which to
* create a TrustedResourceUrl.
* @return {!goog.html.TrustedResourceUrl} A TrustedResourceUrl object
* initialized to {@code url}.
*/
goog.html.TrustedResourceUrl.fromConstant = function(url) {
return goog.html.TrustedResourceUrl
.createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(
goog.string.Const.unwrap(url));
};
/**
* Creates a TrustedResourceUrl object from a compile-time constant strings.
*
* Compile-time constant strings are inherently program-controlled and hence
* trusted.
*
* @param {!Array<!goog.string.Const>} parts Compile-time-constant strings from
* which to create a TrustedResourceUrl.
* @return {!goog.html.TrustedResourceUrl} A TrustedResourceUrl object
* initialized to concatenation of {@code parts}.
*/
goog.html.TrustedResourceUrl.fromConstants = function(parts) {
var unwrapped = '';
for (var i = 0; i < parts.length; i++) {
unwrapped += goog.string.Const.unwrap(parts[i]);
}
return goog.html.TrustedResourceUrl
.createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(unwrapped);
};
/**
* Type marker for the TrustedResourceUrl type, used to implement additional
* run-time type checking.
* @const {!Object}
* @private
*/
goog.html.TrustedResourceUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};
/**
* Package-internal utility method to create TrustedResourceUrl instances.
*
* @param {string} url The string to initialize the TrustedResourceUrl object
* with.
* @return {!goog.html.TrustedResourceUrl} The initialized TrustedResourceUrl
* object.
* @package
*/
goog.html.TrustedResourceUrl
.createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse = function(url) {
var trustedResourceUrl = new goog.html.TrustedResourceUrl();
trustedResourceUrl.privateDoNotAccessOrElseTrustedResourceUrlWrappedValue_ =
url;
return trustedResourceUrl;
};

View File

@ -0,0 +1,228 @@
// Copyright 2013 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Unchecked conversions to create values of goog.html types from
* plain strings. Use of these functions could potentially result in instances
* of goog.html types that violate their type contracts, and hence result in
* security vulnerabilties.
*
* Therefore, all uses of the methods herein must be carefully security
* reviewed. Avoid use of the methods in this file whenever possible; instead
* prefer to create instances of goog.html types using inherently safe builders
* or template systems.
*
*
*
* @visibility {//closure/goog/html:approved_for_unchecked_conversion}
* @visibility {//closure/goog/bin/sizetests:__pkg__}
*/
goog.provide('goog.html.uncheckedconversions');
goog.require('goog.asserts');
goog.require('goog.html.SafeHtml');
goog.require('goog.html.SafeScript');
goog.require('goog.html.SafeStyle');
goog.require('goog.html.SafeStyleSheet');
goog.require('goog.html.SafeUrl');
goog.require('goog.html.TrustedResourceUrl');
goog.require('goog.string');
goog.require('goog.string.Const');
/**
* Performs an "unchecked conversion" to SafeHtml from a plain string that is
* known to satisfy the SafeHtml type contract.
*
* IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
* that the value of {@code html} satisfies the SafeHtml type contract in all
* possible program states.
*
*
* @param {!goog.string.Const} justification A constant string explaining why
* this use of this method is safe. May include a security review ticket
* number.
* @param {string} html A string that is claimed to adhere to the SafeHtml
* contract.
* @param {?goog.i18n.bidi.Dir=} opt_dir The optional directionality of the
* SafeHtml to be constructed. A null or undefined value signifies an
* unknown directionality.
* @return {!goog.html.SafeHtml} The value of html, wrapped in a SafeHtml
* object.
*/
goog.html.uncheckedconversions.safeHtmlFromStringKnownToSatisfyTypeContract =
function(justification, html, opt_dir) {
// unwrap() called inside an assert so that justification can be optimized
// away in production code.
goog.asserts.assertString(
goog.string.Const.unwrap(justification), 'must provide justification');
goog.asserts.assert(
!goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)),
'must provide non-empty justification');
return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
html, opt_dir || null);
};
/**
* Performs an "unchecked conversion" to SafeScript from a plain string that is
* known to satisfy the SafeScript type contract.
*
* IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
* that the value of {@code script} satisfies the SafeScript type contract in
* all possible program states.
*
*
* @param {!goog.string.Const} justification A constant string explaining why
* this use of this method is safe. May include a security review ticket
* number.
* @param {string} script The string to wrap as a SafeScript.
* @return {!goog.html.SafeScript} The value of {@code script}, wrapped in a
* SafeScript object.
*/
goog.html.uncheckedconversions.safeScriptFromStringKnownToSatisfyTypeContract =
function(justification, script) {
// unwrap() called inside an assert so that justification can be optimized
// away in production code.
goog.asserts.assertString(
goog.string.Const.unwrap(justification), 'must provide justification');
goog.asserts.assert(
!goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)),
'must provide non-empty justification');
return goog.html.SafeScript.createSafeScriptSecurityPrivateDoNotAccessOrElse(
script);
};
/**
* Performs an "unchecked conversion" to SafeStyle from a plain string that is
* known to satisfy the SafeStyle type contract.
*
* IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
* that the value of {@code style} satisfies the SafeStyle type contract in all
* possible program states.
*
*
* @param {!goog.string.Const} justification A constant string explaining why
* this use of this method is safe. May include a security review ticket
* number.
* @param {string} style The string to wrap as a SafeStyle.
* @return {!goog.html.SafeStyle} The value of {@code style}, wrapped in a
* SafeStyle object.
*/
goog.html.uncheckedconversions.safeStyleFromStringKnownToSatisfyTypeContract =
function(justification, style) {
// unwrap() called inside an assert so that justification can be optimized
// away in production code.
goog.asserts.assertString(
goog.string.Const.unwrap(justification), 'must provide justification');
goog.asserts.assert(
!goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)),
'must provide non-empty justification');
return goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse(
style);
};
/**
* Performs an "unchecked conversion" to SafeStyleSheet from a plain string
* that is known to satisfy the SafeStyleSheet type contract.
*
* IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
* that the value of {@code styleSheet} satisfies the SafeStyleSheet type
* contract in all possible program states.
*
*
* @param {!goog.string.Const} justification A constant string explaining why
* this use of this method is safe. May include a security review ticket
* number.
* @param {string} styleSheet The string to wrap as a SafeStyleSheet.
* @return {!goog.html.SafeStyleSheet} The value of {@code styleSheet}, wrapped
* in a SafeStyleSheet object.
*/
goog.html.uncheckedconversions
.safeStyleSheetFromStringKnownToSatisfyTypeContract = function(
justification, styleSheet) {
// unwrap() called inside an assert so that justification can be optimized
// away in production code.
goog.asserts.assertString(
goog.string.Const.unwrap(justification), 'must provide justification');
goog.asserts.assert(
!goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)),
'must provide non-empty justification');
return goog.html.SafeStyleSheet
.createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(styleSheet);
};
/**
* Performs an "unchecked conversion" to SafeUrl from a plain string that is
* known to satisfy the SafeUrl type contract.
*
* IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
* that the value of {@code url} satisfies the SafeUrl type contract in all
* possible program states.
*
*
* @param {!goog.string.Const} justification A constant string explaining why
* this use of this method is safe. May include a security review ticket
* number.
* @param {string} url The string to wrap as a SafeUrl.
* @return {!goog.html.SafeUrl} The value of {@code url}, wrapped in a SafeUrl
* object.
*/
goog.html.uncheckedconversions.safeUrlFromStringKnownToSatisfyTypeContract =
function(justification, url) {
// unwrap() called inside an assert so that justification can be optimized
// away in production code.
goog.asserts.assertString(
goog.string.Const.unwrap(justification), 'must provide justification');
goog.asserts.assert(
!goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)),
'must provide non-empty justification');
return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);
};
/**
* Performs an "unchecked conversion" to TrustedResourceUrl from a plain string
* that is known to satisfy the TrustedResourceUrl type contract.
*
* IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
* that the value of {@code url} satisfies the TrustedResourceUrl type contract
* in all possible program states.
*
*
* @param {!goog.string.Const} justification A constant string explaining why
* this use of this method is safe. May include a security review ticket
* number.
* @param {string} url The string to wrap as a TrustedResourceUrl.
* @return {!goog.html.TrustedResourceUrl} The value of {@code url}, wrapped in
* a TrustedResourceUrl object.
*/
goog.html.uncheckedconversions
.trustedResourceUrlFromStringKnownToSatisfyTypeContract = function(
justification, url) {
// unwrap() called inside an assert so that justification can be optimized
// away in production code.
goog.asserts.assertString(
goog.string.Const.unwrap(justification), 'must provide justification');
goog.asserts.assert(
!goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)),
'must provide non-empty justification');
return goog.html.TrustedResourceUrl
.createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(url);
};

View File

@ -0,0 +1,876 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Utility functions for supporting Bidi issues.
*/
/**
* Namespace for bidi supporting functions.
*/
goog.provide('goog.i18n.bidi');
goog.provide('goog.i18n.bidi.Dir');
goog.provide('goog.i18n.bidi.DirectionalString');
goog.provide('goog.i18n.bidi.Format');
/**
* @define {boolean} FORCE_RTL forces the {@link goog.i18n.bidi.IS_RTL} constant
* to say that the current locale is a RTL locale. This should only be used
* if you want to override the default behavior for deciding whether the
* current locale is RTL or not.
*
* {@see goog.i18n.bidi.IS_RTL}
*/
goog.define('goog.i18n.bidi.FORCE_RTL', false);
/**
* Constant that defines whether or not the current locale is a RTL locale.
* If {@link goog.i18n.bidi.FORCE_RTL} is not true, this constant will default
* to check that {@link goog.LOCALE} is one of a few major RTL locales.
*
* <p>This is designed to be a maximally efficient compile-time constant. For
* example, for the default goog.LOCALE, compiling
* "if (goog.i18n.bidi.IS_RTL) alert('rtl') else {}" should produce no code. It
* is this design consideration that limits the implementation to only
* supporting a few major RTL locales, as opposed to the broader repertoire of
* something like goog.i18n.bidi.isRtlLanguage.
*
* <p>Since this constant refers to the directionality of the locale, it is up
* to the caller to determine if this constant should also be used for the
* direction of the UI.
*
* {@see goog.LOCALE}
*
* @type {boolean}
*
* TODO(user): write a test that checks that this is a compile-time constant.
*/
goog.i18n.bidi.IS_RTL = goog.i18n.bidi.FORCE_RTL ||
((goog.LOCALE.substring(0, 2).toLowerCase() == 'ar' ||
goog.LOCALE.substring(0, 2).toLowerCase() == 'fa' ||
goog.LOCALE.substring(0, 2).toLowerCase() == 'he' ||
goog.LOCALE.substring(0, 2).toLowerCase() == 'iw' ||
goog.LOCALE.substring(0, 2).toLowerCase() == 'ps' ||
goog.LOCALE.substring(0, 2).toLowerCase() == 'sd' ||
goog.LOCALE.substring(0, 2).toLowerCase() == 'ug' ||
goog.LOCALE.substring(0, 2).toLowerCase() == 'ur' ||
goog.LOCALE.substring(0, 2).toLowerCase() == 'yi') &&
(goog.LOCALE.length == 2 || goog.LOCALE.substring(2, 3) == '-' ||
goog.LOCALE.substring(2, 3) == '_')) ||
(goog.LOCALE.length >= 3 &&
goog.LOCALE.substring(0, 3).toLowerCase() == 'ckb' &&
(goog.LOCALE.length == 3 || goog.LOCALE.substring(3, 4) == '-' ||
goog.LOCALE.substring(3, 4) == '_'));
/**
* Unicode formatting characters and directionality string constants.
* @enum {string}
*/
goog.i18n.bidi.Format = {
/** Unicode "Left-To-Right Embedding" (LRE) character. */
LRE: '\u202A',
/** Unicode "Right-To-Left Embedding" (RLE) character. */
RLE: '\u202B',
/** Unicode "Pop Directional Formatting" (PDF) character. */
PDF: '\u202C',
/** Unicode "Left-To-Right Mark" (LRM) character. */
LRM: '\u200E',
/** Unicode "Right-To-Left Mark" (RLM) character. */
RLM: '\u200F'
};
/**
* Directionality enum.
* @enum {number}
*/
goog.i18n.bidi.Dir = {
/**
* Left-to-right.
*/
LTR: 1,
/**
* Right-to-left.
*/
RTL: -1,
/**
* Neither left-to-right nor right-to-left.
*/
NEUTRAL: 0
};
/**
* 'right' string constant.
* @type {string}
*/
goog.i18n.bidi.RIGHT = 'right';
/**
* 'left' string constant.
* @type {string}
*/
goog.i18n.bidi.LEFT = 'left';
/**
* 'left' if locale is RTL, 'right' if not.
* @type {string}
*/
goog.i18n.bidi.I18N_RIGHT =
goog.i18n.bidi.IS_RTL ? goog.i18n.bidi.LEFT : goog.i18n.bidi.RIGHT;
/**
* 'right' if locale is RTL, 'left' if not.
* @type {string}
*/
goog.i18n.bidi.I18N_LEFT =
goog.i18n.bidi.IS_RTL ? goog.i18n.bidi.RIGHT : goog.i18n.bidi.LEFT;
/**
* Convert a directionality given in various formats to a goog.i18n.bidi.Dir
* constant. Useful for interaction with different standards of directionality
* representation.
*
* @param {goog.i18n.bidi.Dir|number|boolean|null} givenDir Directionality given
* in one of the following formats:
* 1. A goog.i18n.bidi.Dir constant.
* 2. A number (positive = LTR, negative = RTL, 0 = neutral).
* 3. A boolean (true = RTL, false = LTR).
* 4. A null for unknown directionality.
* @param {boolean=} opt_noNeutral Whether a givenDir of zero or
* goog.i18n.bidi.Dir.NEUTRAL should be treated as null, i.e. unknown, in
* order to preserve legacy behavior.
* @return {?goog.i18n.bidi.Dir} A goog.i18n.bidi.Dir constant matching the
* given directionality. If given null, returns null (i.e. unknown).
*/
goog.i18n.bidi.toDir = function(givenDir, opt_noNeutral) {
if (typeof givenDir == 'number') {
// This includes the non-null goog.i18n.bidi.Dir case.
return givenDir > 0 ? goog.i18n.bidi.Dir.LTR : givenDir < 0 ?
goog.i18n.bidi.Dir.RTL :
opt_noNeutral ? null : goog.i18n.bidi.Dir.NEUTRAL;
} else if (givenDir == null) {
return null;
} else {
// Must be typeof givenDir == 'boolean'.
return givenDir ? goog.i18n.bidi.Dir.RTL : goog.i18n.bidi.Dir.LTR;
}
};
/**
* A practical pattern to identify strong LTR characters. This pattern is not
* theoretically correct according to the Unicode standard. It is simplified for
* performance and small code size.
* @type {string}
* @private
*/
goog.i18n.bidi.ltrChars_ =
'A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02B8\u0300-\u0590\u0800-\u1FFF' +
'\u200E\u2C00-\uFB1C\uFE00-\uFE6F\uFEFD-\uFFFF';
/**
* A practical pattern to identify strong RTL character. This pattern is not
* theoretically correct according to the Unicode standard. It is simplified
* for performance and small code size.
* @type {string}
* @private
*/
goog.i18n.bidi.rtlChars_ =
'\u0591-\u06EF\u06FA-\u07FF\u200F\uFB1D-\uFDFF\uFE70-\uFEFC';
/**
* Simplified regular expression for an HTML tag (opening or closing) or an HTML
* escape. We might want to skip over such expressions when estimating the text
* directionality.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.htmlSkipReg_ = /<[^>]*>|&[^;]+;/g;
/**
* Returns the input text with spaces instead of HTML tags or HTML escapes, if
* opt_isStripNeeded is true. Else returns the input as is.
* Useful for text directionality estimation.
* Note: the function should not be used in other contexts; it is not 100%
* correct, but rather a good-enough implementation for directionality
* estimation purposes.
* @param {string} str The given string.
* @param {boolean=} opt_isStripNeeded Whether to perform the stripping.
* Default: false (to retain consistency with calling functions).
* @return {string} The given string cleaned of HTML tags / escapes.
* @private
*/
goog.i18n.bidi.stripHtmlIfNeeded_ = function(str, opt_isStripNeeded) {
return opt_isStripNeeded ? str.replace(goog.i18n.bidi.htmlSkipReg_, '') : str;
};
/**
* Regular expression to check for RTL characters.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.rtlCharReg_ = new RegExp('[' + goog.i18n.bidi.rtlChars_ + ']');
/**
* Regular expression to check for LTR characters.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.ltrCharReg_ = new RegExp('[' + goog.i18n.bidi.ltrChars_ + ']');
/**
* Test whether the given string has any RTL characters in it.
* @param {string} str The given string that need to be tested.
* @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
* Default: false.
* @return {boolean} Whether the string contains RTL characters.
*/
goog.i18n.bidi.hasAnyRtl = function(str, opt_isHtml) {
return goog.i18n.bidi.rtlCharReg_.test(
goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml));
};
/**
* Test whether the given string has any RTL characters in it.
* @param {string} str The given string that need to be tested.
* @return {boolean} Whether the string contains RTL characters.
* @deprecated Use hasAnyRtl.
*/
goog.i18n.bidi.hasRtlChar = goog.i18n.bidi.hasAnyRtl;
/**
* Test whether the given string has any LTR characters in it.
* @param {string} str The given string that need to be tested.
* @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
* Default: false.
* @return {boolean} Whether the string contains LTR characters.
*/
goog.i18n.bidi.hasAnyLtr = function(str, opt_isHtml) {
return goog.i18n.bidi.ltrCharReg_.test(
goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml));
};
/**
* Regular expression pattern to check if the first character in the string
* is LTR.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.ltrRe_ = new RegExp('^[' + goog.i18n.bidi.ltrChars_ + ']');
/**
* Regular expression pattern to check if the first character in the string
* is RTL.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.rtlRe_ = new RegExp('^[' + goog.i18n.bidi.rtlChars_ + ']');
/**
* Check if the first character in the string is RTL or not.
* @param {string} str The given string that need to be tested.
* @return {boolean} Whether the first character in str is an RTL char.
*/
goog.i18n.bidi.isRtlChar = function(str) {
return goog.i18n.bidi.rtlRe_.test(str);
};
/**
* Check if the first character in the string is LTR or not.
* @param {string} str The given string that need to be tested.
* @return {boolean} Whether the first character in str is an LTR char.
*/
goog.i18n.bidi.isLtrChar = function(str) {
return goog.i18n.bidi.ltrRe_.test(str);
};
/**
* Check if the first character in the string is neutral or not.
* @param {string} str The given string that need to be tested.
* @return {boolean} Whether the first character in str is a neutral char.
*/
goog.i18n.bidi.isNeutralChar = function(str) {
return !goog.i18n.bidi.isLtrChar(str) && !goog.i18n.bidi.isRtlChar(str);
};
/**
* Regular expressions to check if a piece of text is of LTR directionality
* on first character with strong directionality.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.ltrDirCheckRe_ = new RegExp(
'^[^' + goog.i18n.bidi.rtlChars_ + ']*[' + goog.i18n.bidi.ltrChars_ + ']');
/**
* Regular expressions to check if a piece of text is of RTL directionality
* on first character with strong directionality.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.rtlDirCheckRe_ = new RegExp(
'^[^' + goog.i18n.bidi.ltrChars_ + ']*[' + goog.i18n.bidi.rtlChars_ + ']');
/**
* Check whether the first strongly directional character (if any) is RTL.
* @param {string} str String being checked.
* @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
* Default: false.
* @return {boolean} Whether RTL directionality is detected using the first
* strongly-directional character method.
*/
goog.i18n.bidi.startsWithRtl = function(str, opt_isHtml) {
return goog.i18n.bidi.rtlDirCheckRe_.test(
goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml));
};
/**
* Check whether the first strongly directional character (if any) is RTL.
* @param {string} str String being checked.
* @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
* Default: false.
* @return {boolean} Whether RTL directionality is detected using the first
* strongly-directional character method.
* @deprecated Use startsWithRtl.
*/
goog.i18n.bidi.isRtlText = goog.i18n.bidi.startsWithRtl;
/**
* Check whether the first strongly directional character (if any) is LTR.
* @param {string} str String being checked.
* @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
* Default: false.
* @return {boolean} Whether LTR directionality is detected using the first
* strongly-directional character method.
*/
goog.i18n.bidi.startsWithLtr = function(str, opt_isHtml) {
return goog.i18n.bidi.ltrDirCheckRe_.test(
goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml));
};
/**
* Check whether the first strongly directional character (if any) is LTR.
* @param {string} str String being checked.
* @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
* Default: false.
* @return {boolean} Whether LTR directionality is detected using the first
* strongly-directional character method.
* @deprecated Use startsWithLtr.
*/
goog.i18n.bidi.isLtrText = goog.i18n.bidi.startsWithLtr;
/**
* Regular expression to check if a string looks like something that must
* always be LTR even in RTL text, e.g. a URL. When estimating the
* directionality of text containing these, we treat these as weakly LTR,
* like numbers.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.isRequiredLtrRe_ = /^http:\/\/.*/;
/**
* Check whether the input string either contains no strongly directional
* characters or looks like a url.
* @param {string} str String being checked.
* @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
* Default: false.
* @return {boolean} Whether neutral directionality is detected.
*/
goog.i18n.bidi.isNeutralText = function(str, opt_isHtml) {
str = goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml);
return goog.i18n.bidi.isRequiredLtrRe_.test(str) ||
!goog.i18n.bidi.hasAnyLtr(str) && !goog.i18n.bidi.hasAnyRtl(str);
};
/**
* Regular expressions to check if the last strongly-directional character in a
* piece of text is LTR.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.ltrExitDirCheckRe_ = new RegExp(
'[' + goog.i18n.bidi.ltrChars_ + '][^' + goog.i18n.bidi.rtlChars_ + ']*$');
/**
* Regular expressions to check if the last strongly-directional character in a
* piece of text is RTL.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.rtlExitDirCheckRe_ = new RegExp(
'[' + goog.i18n.bidi.rtlChars_ + '][^' + goog.i18n.bidi.ltrChars_ + ']*$');
/**
* Check if the exit directionality a piece of text is LTR, i.e. if the last
* strongly-directional character in the string is LTR.
* @param {string} str String being checked.
* @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
* Default: false.
* @return {boolean} Whether LTR exit directionality was detected.
*/
goog.i18n.bidi.endsWithLtr = function(str, opt_isHtml) {
return goog.i18n.bidi.ltrExitDirCheckRe_.test(
goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml));
};
/**
* Check if the exit directionality a piece of text is LTR, i.e. if the last
* strongly-directional character in the string is LTR.
* @param {string} str String being checked.
* @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
* Default: false.
* @return {boolean} Whether LTR exit directionality was detected.
* @deprecated Use endsWithLtr.
*/
goog.i18n.bidi.isLtrExitText = goog.i18n.bidi.endsWithLtr;
/**
* Check if the exit directionality a piece of text is RTL, i.e. if the last
* strongly-directional character in the string is RTL.
* @param {string} str String being checked.
* @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
* Default: false.
* @return {boolean} Whether RTL exit directionality was detected.
*/
goog.i18n.bidi.endsWithRtl = function(str, opt_isHtml) {
return goog.i18n.bidi.rtlExitDirCheckRe_.test(
goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml));
};
/**
* Check if the exit directionality a piece of text is RTL, i.e. if the last
* strongly-directional character in the string is RTL.
* @param {string} str String being checked.
* @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
* Default: false.
* @return {boolean} Whether RTL exit directionality was detected.
* @deprecated Use endsWithRtl.
*/
goog.i18n.bidi.isRtlExitText = goog.i18n.bidi.endsWithRtl;
/**
* A regular expression for matching right-to-left language codes.
* See {@link #isRtlLanguage} for the design.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.rtlLocalesRe_ = new RegExp(
'^(ar|ckb|dv|he|iw|fa|nqo|ps|sd|ug|ur|yi|' +
'.*[-_](Arab|Hebr|Thaa|Nkoo|Tfng))' +
'(?!.*[-_](Latn|Cyrl)($|-|_))($|-|_)',
'i');
/**
* Check if a BCP 47 / III language code indicates an RTL language, i.e. either:
* - a language code explicitly specifying one of the right-to-left scripts,
* e.g. "az-Arab", or<p>
* - a language code specifying one of the languages normally written in a
* right-to-left script, e.g. "fa" (Farsi), except ones explicitly specifying
* Latin or Cyrillic script (which are the usual LTR alternatives).<p>
* The list of right-to-left scripts appears in the 100-199 range in
* http://www.unicode.org/iso15924/iso15924-num.html, of which Arabic and
* Hebrew are by far the most widely used. We also recognize Thaana, N'Ko, and
* Tifinagh, which also have significant modern usage. The rest (Syriac,
* Samaritan, Mandaic, etc.) seem to have extremely limited or no modern usage
* and are not recognized to save on code size.
* The languages usually written in a right-to-left script are taken as those
* with Suppress-Script: Hebr|Arab|Thaa|Nkoo|Tfng in
* http://www.iana.org/assignments/language-subtag-registry,
* as well as Central (or Sorani) Kurdish (ckb), Sindhi (sd) and Uyghur (ug).
* Other subtags of the language code, e.g. regions like EG (Egypt), are
* ignored.
* @param {string} lang BCP 47 (a.k.a III) language code.
* @return {boolean} Whether the language code is an RTL language.
*/
goog.i18n.bidi.isRtlLanguage = function(lang) {
return goog.i18n.bidi.rtlLocalesRe_.test(lang);
};
/**
* Regular expression for bracket guard replacement in text.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.bracketGuardTextRe_ =
/(\(.*?\)+)|(\[.*?\]+)|(\{.*?\}+)|(<.*?>+)/g;
/**
* Apply bracket guard using LRM and RLM. This is to address the problem of
* messy bracket display frequently happens in RTL layout.
* This function works for plain text, not for HTML. In HTML, the opening
* bracket might be in a different context than the closing bracket (such as
* an attribute value).
* @param {string} s The string that need to be processed.
* @param {boolean=} opt_isRtlContext specifies default direction (usually
* direction of the UI).
* @return {string} The processed string, with all bracket guarded.
*/
goog.i18n.bidi.guardBracketInText = function(s, opt_isRtlContext) {
var useRtl = opt_isRtlContext === undefined ? goog.i18n.bidi.hasAnyRtl(s) :
opt_isRtlContext;
var mark = useRtl ? goog.i18n.bidi.Format.RLM : goog.i18n.bidi.Format.LRM;
return s.replace(goog.i18n.bidi.bracketGuardTextRe_, mark + '$&' + mark);
};
/**
* Enforce the html snippet in RTL directionality regardless overall context.
* If the html piece was enclosed by tag, dir will be applied to existing
* tag, otherwise a span tag will be added as wrapper. For this reason, if
* html snippet start with with tag, this tag must enclose the whole piece. If
* the tag already has a dir specified, this new one will override existing
* one in behavior (tested on FF and IE).
* @param {string} html The string that need to be processed.
* @return {string} The processed string, with directionality enforced to RTL.
*/
goog.i18n.bidi.enforceRtlInHtml = function(html) {
if (html.charAt(0) == '<') {
return html.replace(/<\w+/, '$& dir=rtl');
}
// '\n' is important for FF so that it won't incorrectly merge span groups
return '\n<span dir=rtl>' + html + '</span>';
};
/**
* Enforce RTL on both end of the given text piece using unicode BiDi formatting
* characters RLE and PDF.
* @param {string} text The piece of text that need to be wrapped.
* @return {string} The wrapped string after process.
*/
goog.i18n.bidi.enforceRtlInText = function(text) {
return goog.i18n.bidi.Format.RLE + text + goog.i18n.bidi.Format.PDF;
};
/**
* Enforce the html snippet in RTL directionality regardless overall context.
* If the html piece was enclosed by tag, dir will be applied to existing
* tag, otherwise a span tag will be added as wrapper. For this reason, if
* html snippet start with with tag, this tag must enclose the whole piece. If
* the tag already has a dir specified, this new one will override existing
* one in behavior (tested on FF and IE).
* @param {string} html The string that need to be processed.
* @return {string} The processed string, with directionality enforced to RTL.
*/
goog.i18n.bidi.enforceLtrInHtml = function(html) {
if (html.charAt(0) == '<') {
return html.replace(/<\w+/, '$& dir=ltr');
}
// '\n' is important for FF so that it won't incorrectly merge span groups
return '\n<span dir=ltr>' + html + '</span>';
};
/**
* Enforce LTR on both end of the given text piece using unicode BiDi formatting
* characters LRE and PDF.
* @param {string} text The piece of text that need to be wrapped.
* @return {string} The wrapped string after process.
*/
goog.i18n.bidi.enforceLtrInText = function(text) {
return goog.i18n.bidi.Format.LRE + text + goog.i18n.bidi.Format.PDF;
};
/**
* Regular expression to find dimensions such as "padding: .3 0.4ex 5px 6;"
* @type {RegExp}
* @private
*/
goog.i18n.bidi.dimensionsRe_ =
/:\s*([.\d][.\w]*)\s+([.\d][.\w]*)\s+([.\d][.\w]*)\s+([.\d][.\w]*)/g;
/**
* Regular expression for left.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.leftRe_ = /left/gi;
/**
* Regular expression for right.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.rightRe_ = /right/gi;
/**
* Placeholder regular expression for swapping.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.tempRe_ = /%%%%/g;
/**
* Swap location parameters and 'left'/'right' in CSS specification. The
* processed string will be suited for RTL layout. Though this function can
* cover most cases, there are always exceptions. It is suggested to put
* those exceptions in separate group of CSS string.
* @param {string} cssStr CSS spefication string.
* @return {string} Processed CSS specification string.
*/
goog.i18n.bidi.mirrorCSS = function(cssStr) {
return cssStr
.
// reverse dimensions
replace(goog.i18n.bidi.dimensionsRe_, ':$1 $4 $3 $2')
.replace(goog.i18n.bidi.leftRe_, '%%%%')
. // swap left and right
replace(goog.i18n.bidi.rightRe_, goog.i18n.bidi.LEFT)
.replace(goog.i18n.bidi.tempRe_, goog.i18n.bidi.RIGHT);
};
/**
* Regular expression for hebrew double quote substitution, finding quote
* directly after hebrew characters.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.doubleQuoteSubstituteRe_ = /([\u0591-\u05f2])"/g;
/**
* Regular expression for hebrew single quote substitution, finding quote
* directly after hebrew characters.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.singleQuoteSubstituteRe_ = /([\u0591-\u05f2])'/g;
/**
* Replace the double and single quote directly after a Hebrew character with
* GERESH and GERSHAYIM. In such case, most likely that's user intention.
* @param {string} str String that need to be processed.
* @return {string} Processed string with double/single quote replaced.
*/
goog.i18n.bidi.normalizeHebrewQuote = function(str) {
return str.replace(goog.i18n.bidi.doubleQuoteSubstituteRe_, '$1\u05f4')
.replace(goog.i18n.bidi.singleQuoteSubstituteRe_, '$1\u05f3');
};
/**
* Regular expression to split a string into "words" for directionality
* estimation based on relative word counts.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.wordSeparatorRe_ = /\s+/;
/**
* Regular expression to check if a string contains any numerals. Used to
* differentiate between completely neutral strings and those containing
* numbers, which are weakly LTR.
*
* Native Arabic digits (\u0660 - \u0669) are not included because although they
* do flow left-to-right inside a number, this is the case even if the overall
* directionality is RTL, and a mathematical expression using these digits is
* supposed to flow right-to-left overall, including unary plus and minus
* appearing to the right of a number, and this does depend on the overall
* directionality being RTL. The digits used in Farsi (\u06F0 - \u06F9), on the
* other hand, are included, since Farsi math (including unary plus and minus)
* does flow left-to-right.
*
* @type {RegExp}
* @private
*/
goog.i18n.bidi.hasNumeralsRe_ = /[\d\u06f0-\u06f9]/;
/**
* This constant controls threshold of RTL directionality.
* @type {number}
* @private
*/
goog.i18n.bidi.rtlDetectionThreshold_ = 0.40;
/**
* Estimates the directionality of a string based on relative word counts.
* If the number of RTL words is above a certain percentage of the total number
* of strongly directional words, returns RTL.
* Otherwise, if any words are strongly or weakly LTR, returns LTR.
* Otherwise, returns UNKNOWN, which is used to mean "neutral".
* Numbers are counted as weakly LTR.
* @param {string} str The string to be checked.
* @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
* Default: false.
* @return {goog.i18n.bidi.Dir} Estimated overall directionality of {@code str}.
*/
goog.i18n.bidi.estimateDirection = function(str, opt_isHtml) {
var rtlCount = 0;
var totalCount = 0;
var hasWeaklyLtr = false;
var tokens = goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml)
.split(goog.i18n.bidi.wordSeparatorRe_);
for (var i = 0; i < tokens.length; i++) {
var token = tokens[i];
if (goog.i18n.bidi.startsWithRtl(token)) {
rtlCount++;
totalCount++;
} else if (goog.i18n.bidi.isRequiredLtrRe_.test(token)) {
hasWeaklyLtr = true;
} else if (goog.i18n.bidi.hasAnyLtr(token)) {
totalCount++;
} else if (goog.i18n.bidi.hasNumeralsRe_.test(token)) {
hasWeaklyLtr = true;
}
}
return totalCount == 0 ?
(hasWeaklyLtr ? goog.i18n.bidi.Dir.LTR : goog.i18n.bidi.Dir.NEUTRAL) :
(rtlCount / totalCount > goog.i18n.bidi.rtlDetectionThreshold_ ?
goog.i18n.bidi.Dir.RTL :
goog.i18n.bidi.Dir.LTR);
};
/**
* Check the directionality of a piece of text, return true if the piece of
* text should be laid out in RTL direction.
* @param {string} str The piece of text that need to be detected.
* @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
* Default: false.
* @return {boolean} Whether this piece of text should be laid out in RTL.
*/
goog.i18n.bidi.detectRtlDirectionality = function(str, opt_isHtml) {
return goog.i18n.bidi.estimateDirection(str, opt_isHtml) ==
goog.i18n.bidi.Dir.RTL;
};
/**
* Sets text input element's directionality and text alignment based on a
* given directionality. Does nothing if the given directionality is unknown or
* neutral.
* @param {Element} element Input field element to set directionality to.
* @param {goog.i18n.bidi.Dir|number|boolean|null} dir Desired directionality,
* given in one of the following formats:
* 1. A goog.i18n.bidi.Dir constant.
* 2. A number (positive = LRT, negative = RTL, 0 = neutral).
* 3. A boolean (true = RTL, false = LTR).
* 4. A null for unknown directionality.
*/
goog.i18n.bidi.setElementDirAndAlign = function(element, dir) {
if (element) {
dir = goog.i18n.bidi.toDir(dir);
if (dir) {
element.style.textAlign = dir == goog.i18n.bidi.Dir.RTL ?
goog.i18n.bidi.RIGHT :
goog.i18n.bidi.LEFT;
element.dir = dir == goog.i18n.bidi.Dir.RTL ? 'rtl' : 'ltr';
}
}
};
/**
* Sets element dir based on estimated directionality of the given text.
* @param {!Element} element
* @param {string} text
*/
goog.i18n.bidi.setElementDirByTextDirectionality = function(element, text) {
switch (goog.i18n.bidi.estimateDirection(text)) {
case (goog.i18n.bidi.Dir.LTR):
element.dir = 'ltr';
break;
case (goog.i18n.bidi.Dir.RTL):
element.dir = 'rtl';
break;
default:
// Default for no direction, inherit from document.
element.removeAttribute('dir');
}
};
/**
* Strings that have an (optional) known direction.
*
* Implementations of this interface are string-like objects that carry an
* attached direction, if known.
* @interface
*/
goog.i18n.bidi.DirectionalString = function() {};
/**
* Interface marker of the DirectionalString interface.
*
* This property can be used to determine at runtime whether or not an object
* implements this interface. All implementations of this interface set this
* property to {@code true}.
* @type {boolean}
*/
goog.i18n.bidi.DirectionalString.prototype
.implementsGoogI18nBidiDirectionalString;
/**
* Retrieves this object's known direction (if any).
* @return {?goog.i18n.bidi.Dir} The known direction. Null if unknown.
*/
goog.i18n.bidi.DirectionalString.prototype.getDirection;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,338 @@
// Copyright 2013 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Closure user agent detection (Browser).
* @see <a href="http://www.useragentstring.com/">User agent strings</a>
* For more information on rendering engine, platform, or device see the other
* sub-namespaces in goog.labs.userAgent, goog.labs.userAgent.platform,
* goog.labs.userAgent.device respectively.)
*
* @author martone@google.com (Andy Martone)
*/
goog.provide('goog.labs.userAgent.browser');
goog.require('goog.array');
goog.require('goog.labs.userAgent.util');
goog.require('goog.object');
goog.require('goog.string');
// TODO(nnaze): Refactor to remove excessive exclusion logic in matching
// functions.
/**
* @return {boolean} Whether the user's browser is Opera. Note: Chromium
* based Opera (Opera 15+) is detected as Chrome to avoid unnecessary
* special casing.
* @private
*/
goog.labs.userAgent.browser.matchOpera_ = function() {
return goog.labs.userAgent.util.matchUserAgent('Opera');
};
/**
* @return {boolean} Whether the user's browser is IE.
* @private
*/
goog.labs.userAgent.browser.matchIE_ = function() {
return goog.labs.userAgent.util.matchUserAgent('Trident') ||
goog.labs.userAgent.util.matchUserAgent('MSIE');
};
/**
* @return {boolean} Whether the user's browser is Edge.
* @private
*/
goog.labs.userAgent.browser.matchEdge_ = function() {
return goog.labs.userAgent.util.matchUserAgent('Edge');
};
/**
* @return {boolean} Whether the user's browser is Firefox.
* @private
*/
goog.labs.userAgent.browser.matchFirefox_ = function() {
return goog.labs.userAgent.util.matchUserAgent('Firefox');
};
/**
* @return {boolean} Whether the user's browser is Safari.
* @private
*/
goog.labs.userAgent.browser.matchSafari_ = function() {
return goog.labs.userAgent.util.matchUserAgent('Safari') &&
!(goog.labs.userAgent.browser.matchChrome_() ||
goog.labs.userAgent.browser.matchCoast_() ||
goog.labs.userAgent.browser.matchOpera_() ||
goog.labs.userAgent.browser.matchEdge_() ||
goog.labs.userAgent.browser.isSilk() ||
goog.labs.userAgent.util.matchUserAgent('Android'));
};
/**
* @return {boolean} Whether the user's browser is Coast (Opera's Webkit-based
* iOS browser).
* @private
*/
goog.labs.userAgent.browser.matchCoast_ = function() {
return goog.labs.userAgent.util.matchUserAgent('Coast');
};
/**
* @return {boolean} Whether the user's browser is iOS Webview.
* @private
*/
goog.labs.userAgent.browser.matchIosWebview_ = function() {
// iOS Webview does not show up as Chrome or Safari. Also check for Opera's
// WebKit-based iOS browser, Coast.
return (goog.labs.userAgent.util.matchUserAgent('iPad') ||
goog.labs.userAgent.util.matchUserAgent('iPhone')) &&
!goog.labs.userAgent.browser.matchSafari_() &&
!goog.labs.userAgent.browser.matchChrome_() &&
!goog.labs.userAgent.browser.matchCoast_() &&
goog.labs.userAgent.util.matchUserAgent('AppleWebKit');
};
/**
* @return {boolean} Whether the user's browser is Chrome.
* @private
*/
goog.labs.userAgent.browser.matchChrome_ = function() {
return (goog.labs.userAgent.util.matchUserAgent('Chrome') ||
goog.labs.userAgent.util.matchUserAgent('CriOS')) &&
!goog.labs.userAgent.browser.matchEdge_();
};
/**
* @return {boolean} Whether the user's browser is the Android browser.
* @private
*/
goog.labs.userAgent.browser.matchAndroidBrowser_ = function() {
// Android can appear in the user agent string for Chrome on Android.
// This is not the Android standalone browser if it does.
return goog.labs.userAgent.util.matchUserAgent('Android') &&
!(goog.labs.userAgent.browser.isChrome() ||
goog.labs.userAgent.browser.isFirefox() ||
goog.labs.userAgent.browser.isOpera() ||
goog.labs.userAgent.browser.isSilk());
};
/**
* @return {boolean} Whether the user's browser is Opera.
*/
goog.labs.userAgent.browser.isOpera = goog.labs.userAgent.browser.matchOpera_;
/**
* @return {boolean} Whether the user's browser is IE.
*/
goog.labs.userAgent.browser.isIE = goog.labs.userAgent.browser.matchIE_;
/**
* @return {boolean} Whether the user's browser is Edge.
*/
goog.labs.userAgent.browser.isEdge = goog.labs.userAgent.browser.matchEdge_;
/**
* @return {boolean} Whether the user's browser is Firefox.
*/
goog.labs.userAgent.browser.isFirefox =
goog.labs.userAgent.browser.matchFirefox_;
/**
* @return {boolean} Whether the user's browser is Safari.
*/
goog.labs.userAgent.browser.isSafari = goog.labs.userAgent.browser.matchSafari_;
/**
* @return {boolean} Whether the user's browser is Coast (Opera's Webkit-based
* iOS browser).
*/
goog.labs.userAgent.browser.isCoast = goog.labs.userAgent.browser.matchCoast_;
/**
* @return {boolean} Whether the user's browser is iOS Webview.
*/
goog.labs.userAgent.browser.isIosWebview =
goog.labs.userAgent.browser.matchIosWebview_;
/**
* @return {boolean} Whether the user's browser is Chrome.
*/
goog.labs.userAgent.browser.isChrome = goog.labs.userAgent.browser.matchChrome_;
/**
* @return {boolean} Whether the user's browser is the Android browser.
*/
goog.labs.userAgent.browser.isAndroidBrowser =
goog.labs.userAgent.browser.matchAndroidBrowser_;
/**
* For more information, see:
* http://docs.aws.amazon.com/silk/latest/developerguide/user-agent.html
* @return {boolean} Whether the user's browser is Silk.
*/
goog.labs.userAgent.browser.isSilk = function() {
return goog.labs.userAgent.util.matchUserAgent('Silk');
};
/**
* @return {string} The browser version or empty string if version cannot be
* determined. Note that for Internet Explorer, this returns the version of
* the browser, not the version of the rendering engine. (IE 8 in
* compatibility mode will return 8.0 rather than 7.0. To determine the
* rendering engine version, look at document.documentMode instead. See
* http://msdn.microsoft.com/en-us/library/cc196988(v=vs.85).aspx for more
* details.)
*/
goog.labs.userAgent.browser.getVersion = function() {
var userAgentString = goog.labs.userAgent.util.getUserAgent();
// Special case IE since IE's version is inside the parenthesis and
// without the '/'.
if (goog.labs.userAgent.browser.isIE()) {
return goog.labs.userAgent.browser.getIEVersion_(userAgentString);
}
var versionTuples =
goog.labs.userAgent.util.extractVersionTuples(userAgentString);
// Construct a map for easy lookup.
var versionMap = {};
goog.array.forEach(versionTuples, function(tuple) {
// Note that the tuple is of length three, but we only care about the
// first two.
var key = tuple[0];
var value = tuple[1];
versionMap[key] = value;
});
var versionMapHasKey = goog.partial(goog.object.containsKey, versionMap);
// Gives the value with the first key it finds, otherwise empty string.
function lookUpValueWithKeys(keys) {
var key = goog.array.find(keys, versionMapHasKey);
return versionMap[key] || '';
}
// Check Opera before Chrome since Opera 15+ has "Chrome" in the string.
// See
// http://my.opera.com/ODIN/blog/2013/07/15/opera-user-agent-strings-opera-15-and-beyond
if (goog.labs.userAgent.browser.isOpera()) {
// Opera 10 has Version/10.0 but Opera/9.8, so look for "Version" first.
// Opera uses 'OPR' for more recent UAs.
return lookUpValueWithKeys(['Version', 'Opera']);
}
// Check Edge before Chrome since it has Chrome in the string.
if (goog.labs.userAgent.browser.isEdge()) {
return lookUpValueWithKeys(['Edge']);
}
if (goog.labs.userAgent.browser.isChrome()) {
return lookUpValueWithKeys(['Chrome', 'CriOS']);
}
// Usually products browser versions are in the third tuple after "Mozilla"
// and the engine.
var tuple = versionTuples[2];
return tuple && tuple[1] || '';
};
/**
* @param {string|number} version The version to check.
* @return {boolean} Whether the browser version is higher or the same as the
* given version.
*/
goog.labs.userAgent.browser.isVersionOrHigher = function(version) {
return goog.string.compareVersions(
goog.labs.userAgent.browser.getVersion(), version) >= 0;
};
/**
* Determines IE version. More information:
* http://msdn.microsoft.com/en-us/library/ie/bg182625(v=vs.85).aspx#uaString
* http://msdn.microsoft.com/en-us/library/hh869301(v=vs.85).aspx
* http://blogs.msdn.com/b/ie/archive/2010/03/23/introducing-ie9-s-user-agent-string.aspx
* http://blogs.msdn.com/b/ie/archive/2009/01/09/the-internet-explorer-8-user-agent-string-updated-edition.aspx
*
* @param {string} userAgent the User-Agent.
* @return {string}
* @private
*/
goog.labs.userAgent.browser.getIEVersion_ = function(userAgent) {
// IE11 may identify itself as MSIE 9.0 or MSIE 10.0 due to an IE 11 upgrade
// bug. Example UA:
// Mozilla/5.0 (MSIE 9.0; Windows NT 6.1; WOW64; Trident/7.0; rv:11.0)
// like Gecko.
// See http://www.whatismybrowser.com/developers/unknown-user-agent-fragments.
var rv = /rv: *([\d\.]*)/.exec(userAgent);
if (rv && rv[1]) {
return rv[1];
}
var version = '';
var msie = /MSIE +([\d\.]+)/.exec(userAgent);
if (msie && msie[1]) {
// IE in compatibility mode usually identifies itself as MSIE 7.0; in this
// case, use the Trident version to determine the version of IE. For more
// details, see the links above.
var tridentVersion = /Trident\/(\d.\d)/.exec(userAgent);
if (msie[1] == '7.0') {
if (tridentVersion && tridentVersion[1]) {
switch (tridentVersion[1]) {
case '4.0':
version = '8.0';
break;
case '5.0':
version = '9.0';
break;
case '6.0':
version = '10.0';
break;
case '7.0':
version = '11.0';
break;
}
} else {
version = '7.0';
}
} else {
version = msie[1];
}
}
return version;
};

View File

@ -0,0 +1,156 @@
// Copyright 2013 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Closure user agent detection.
* @see http://en.wikipedia.org/wiki/User_agent
* For more information on browser brand, platform, or device see the other
* sub-namespaces in goog.labs.userAgent (browser, platform, and device).
*
*/
goog.provide('goog.labs.userAgent.engine');
goog.require('goog.array');
goog.require('goog.labs.userAgent.util');
goog.require('goog.string');
/**
* @return {boolean} Whether the rendering engine is Presto.
*/
goog.labs.userAgent.engine.isPresto = function() {
return goog.labs.userAgent.util.matchUserAgent('Presto');
};
/**
* @return {boolean} Whether the rendering engine is Trident.
*/
goog.labs.userAgent.engine.isTrident = function() {
// IE only started including the Trident token in IE8.
return goog.labs.userAgent.util.matchUserAgent('Trident') ||
goog.labs.userAgent.util.matchUserAgent('MSIE');
};
/**
* @return {boolean} Whether the rendering engine is Edge.
*/
goog.labs.userAgent.engine.isEdge = function() {
return goog.labs.userAgent.util.matchUserAgent('Edge');
};
/**
* @return {boolean} Whether the rendering engine is WebKit.
*/
goog.labs.userAgent.engine.isWebKit = function() {
return goog.labs.userAgent.util.matchUserAgentIgnoreCase('WebKit') &&
!goog.labs.userAgent.engine.isEdge();
};
/**
* @return {boolean} Whether the rendering engine is Gecko.
*/
goog.labs.userAgent.engine.isGecko = function() {
return goog.labs.userAgent.util.matchUserAgent('Gecko') &&
!goog.labs.userAgent.engine.isWebKit() &&
!goog.labs.userAgent.engine.isTrident() &&
!goog.labs.userAgent.engine.isEdge();
};
/**
* @return {string} The rendering engine's version or empty string if version
* can't be determined.
*/
goog.labs.userAgent.engine.getVersion = function() {
var userAgentString = goog.labs.userAgent.util.getUserAgent();
if (userAgentString) {
var tuples = goog.labs.userAgent.util.extractVersionTuples(userAgentString);
var engineTuple = goog.labs.userAgent.engine.getEngineTuple_(tuples);
if (engineTuple) {
// In Gecko, the version string is either in the browser info or the
// Firefox version. See Gecko user agent string reference:
// http://goo.gl/mULqa
if (engineTuple[0] == 'Gecko') {
return goog.labs.userAgent.engine.getVersionForKey_(tuples, 'Firefox');
}
return engineTuple[1];
}
// MSIE has only one version identifier, and the Trident version is
// specified in the parenthetical. IE Edge is covered in the engine tuple
// detection.
var browserTuple = tuples[0];
var info;
if (browserTuple && (info = browserTuple[2])) {
var match = /Trident\/([^\s;]+)/.exec(info);
if (match) {
return match[1];
}
}
}
return '';
};
/**
* @param {!Array<!Array<string>>} tuples Extracted version tuples.
* @return {!Array<string>|undefined} The engine tuple or undefined if not
* found.
* @private
*/
goog.labs.userAgent.engine.getEngineTuple_ = function(tuples) {
if (!goog.labs.userAgent.engine.isEdge()) {
return tuples[1];
}
for (var i = 0; i < tuples.length; i++) {
var tuple = tuples[i];
if (tuple[0] == 'Edge') {
return tuple;
}
}
};
/**
* @param {string|number} version The version to check.
* @return {boolean} Whether the rendering engine version is higher or the same
* as the given version.
*/
goog.labs.userAgent.engine.isVersionOrHigher = function(version) {
return goog.string.compareVersions(
goog.labs.userAgent.engine.getVersion(), version) >= 0;
};
/**
* @param {!Array<!Array<string>>} tuples Version tuples.
* @param {string} key The key to look for.
* @return {string} The version string of the given key, if present.
* Otherwise, the empty string.
* @private
*/
goog.labs.userAgent.engine.getVersionForKey_ = function(tuples, key) {
// TODO(nnaze): Move to util if useful elsewhere.
var pair = goog.array.find(tuples, function(pair) { return key == pair[0]; });
return pair && pair[1] || '';
};

View File

@ -0,0 +1,160 @@
// Copyright 2013 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Closure user agent platform detection.
* @see <a href="http://www.useragentstring.com/">User agent strings</a>
* For more information on browser brand, rendering engine, or device see the
* other sub-namespaces in goog.labs.userAgent (browser, engine, and device
* respectively).
*
*/
goog.provide('goog.labs.userAgent.platform');
goog.require('goog.labs.userAgent.util');
goog.require('goog.string');
/**
* @return {boolean} Whether the platform is Android.
*/
goog.labs.userAgent.platform.isAndroid = function() {
return goog.labs.userAgent.util.matchUserAgent('Android');
};
/**
* @return {boolean} Whether the platform is iPod.
*/
goog.labs.userAgent.platform.isIpod = function() {
return goog.labs.userAgent.util.matchUserAgent('iPod');
};
/**
* @return {boolean} Whether the platform is iPhone.
*/
goog.labs.userAgent.platform.isIphone = function() {
return goog.labs.userAgent.util.matchUserAgent('iPhone') &&
!goog.labs.userAgent.util.matchUserAgent('iPod') &&
!goog.labs.userAgent.util.matchUserAgent('iPad');
};
/**
* @return {boolean} Whether the platform is iPad.
*/
goog.labs.userAgent.platform.isIpad = function() {
return goog.labs.userAgent.util.matchUserAgent('iPad');
};
/**
* @return {boolean} Whether the platform is iOS.
*/
goog.labs.userAgent.platform.isIos = function() {
return goog.labs.userAgent.platform.isIphone() ||
goog.labs.userAgent.platform.isIpad() ||
goog.labs.userAgent.platform.isIpod();
};
/**
* @return {boolean} Whether the platform is Mac.
*/
goog.labs.userAgent.platform.isMacintosh = function() {
return goog.labs.userAgent.util.matchUserAgent('Macintosh');
};
/**
* Note: ChromeOS is not considered to be Linux as it does not report itself
* as Linux in the user agent string.
* @return {boolean} Whether the platform is Linux.
*/
goog.labs.userAgent.platform.isLinux = function() {
return goog.labs.userAgent.util.matchUserAgent('Linux');
};
/**
* @return {boolean} Whether the platform is Windows.
*/
goog.labs.userAgent.platform.isWindows = function() {
return goog.labs.userAgent.util.matchUserAgent('Windows');
};
/**
* @return {boolean} Whether the platform is ChromeOS.
*/
goog.labs.userAgent.platform.isChromeOS = function() {
return goog.labs.userAgent.util.matchUserAgent('CrOS');
};
/**
* The version of the platform. We only determine the version for Windows,
* Mac, and Chrome OS. It doesn't make much sense on Linux. For Windows, we only
* look at the NT version. Non-NT-based versions (e.g. 95, 98, etc.) are given
* version 0.0.
*
* @return {string} The platform version or empty string if version cannot be
* determined.
*/
goog.labs.userAgent.platform.getVersion = function() {
var userAgentString = goog.labs.userAgent.util.getUserAgent();
var version = '', re;
if (goog.labs.userAgent.platform.isWindows()) {
re = /Windows (?:NT|Phone) ([0-9.]+)/;
var match = re.exec(userAgentString);
if (match) {
version = match[1];
} else {
version = '0.0';
}
} else if (goog.labs.userAgent.platform.isIos()) {
re = /(?:iPhone|iPod|iPad|CPU)\s+OS\s+(\S+)/;
var match = re.exec(userAgentString);
// Report the version as x.y.z and not x_y_z
version = match && match[1].replace(/_/g, '.');
} else if (goog.labs.userAgent.platform.isMacintosh()) {
re = /Mac OS X ([0-9_.]+)/;
var match = re.exec(userAgentString);
// Note: some old versions of Camino do not report an OSX version.
// Default to 10.
version = match ? match[1].replace(/_/g, '.') : '10';
} else if (goog.labs.userAgent.platform.isAndroid()) {
re = /Android\s+([^\);]+)(\)|;)/;
var match = re.exec(userAgentString);
version = match && match[1];
} else if (goog.labs.userAgent.platform.isChromeOS()) {
re = /(?:CrOS\s+(?:i686|x86_64)\s+([0-9.]+))/;
var match = re.exec(userAgentString);
version = match && match[1];
}
return version || '';
};
/**
* @param {string|number} version The version to check.
* @return {boolean} Whether the browser version is higher or the same as the
* given version.
*/
goog.labs.userAgent.platform.isVersionOrHigher = function(version) {
return goog.string.compareVersions(
goog.labs.userAgent.platform.getVersion(), version) >= 0;
};

View File

@ -0,0 +1,147 @@
// Copyright 2013 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Utilities used by goog.labs.userAgent tools. These functions
* should not be used outside of goog.labs.userAgent.*.
*
*
* @author nnaze@google.com (Nathan Naze)
*/
goog.provide('goog.labs.userAgent.util');
goog.require('goog.string');
/**
* Gets the native userAgent string from navigator if it exists.
* If navigator or navigator.userAgent string is missing, returns an empty
* string.
* @return {string}
* @private
*/
goog.labs.userAgent.util.getNativeUserAgentString_ = function() {
var navigator = goog.labs.userAgent.util.getNavigator_();
if (navigator) {
var userAgent = navigator.userAgent;
if (userAgent) {
return userAgent;
}
}
return '';
};
/**
* Getter for the native navigator.
* This is a separate function so it can be stubbed out in testing.
* @return {Navigator}
* @private
*/
goog.labs.userAgent.util.getNavigator_ = function() {
return goog.global.navigator;
};
/**
* A possible override for applications which wish to not check
* navigator.userAgent but use a specified value for detection instead.
* @private {string}
*/
goog.labs.userAgent.util.userAgent_ =
goog.labs.userAgent.util.getNativeUserAgentString_();
/**
* Applications may override browser detection on the built in
* navigator.userAgent object by setting this string. Set to null to use the
* browser object instead.
* @param {?string=} opt_userAgent The User-Agent override.
*/
goog.labs.userAgent.util.setUserAgent = function(opt_userAgent) {
goog.labs.userAgent.util.userAgent_ =
opt_userAgent || goog.labs.userAgent.util.getNativeUserAgentString_();
};
/**
* @return {string} The user agent string.
*/
goog.labs.userAgent.util.getUserAgent = function() {
return goog.labs.userAgent.util.userAgent_;
};
/**
* @param {string} str
* @return {boolean} Whether the user agent contains the given string.
*/
goog.labs.userAgent.util.matchUserAgent = function(str) {
var userAgent = goog.labs.userAgent.util.getUserAgent();
return goog.string.contains(userAgent, str);
};
/**
* @param {string} str
* @return {boolean} Whether the user agent contains the given string, ignoring
* case.
*/
goog.labs.userAgent.util.matchUserAgentIgnoreCase = function(str) {
var userAgent = goog.labs.userAgent.util.getUserAgent();
return goog.string.caseInsensitiveContains(userAgent, str);
};
/**
* Parses the user agent into tuples for each section.
* @param {string} userAgent
* @return {!Array<!Array<string>>} Tuples of key, version, and the contents
* of the parenthetical.
*/
goog.labs.userAgent.util.extractVersionTuples = function(userAgent) {
// Matches each section of a user agent string.
// Example UA:
// Mozilla/5.0 (iPad; U; CPU OS 3_2_1 like Mac OS X; en-us)
// AppleWebKit/531.21.10 (KHTML, like Gecko) Mobile/7B405
// This has three version tuples: Mozilla, AppleWebKit, and Mobile.
var versionRegExp = new RegExp(
// Key. Note that a key may have a space.
// (i.e. 'Mobile Safari' in 'Mobile Safari/5.0')
'(\\w[\\w ]+)' +
'/' + // slash
'([^\\s]+)' + // version (i.e. '5.0b')
'\\s*' + // whitespace
'(?:\\((.*?)\\))?', // parenthetical info. parentheses not matched.
'g');
var data = [];
var match;
// Iterate and collect the version tuples. Each iteration will be the
// next regex match.
while (match = versionRegExp.exec(userAgent)) {
data.push([
match[1], // key
match[2], // value
// || undefined as this is not undefined in IE7 and IE8
match[3] || undefined // info
]);
}
return data;
};

View File

@ -0,0 +1,402 @@
// Copyright 2006 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A utility class for representing a numeric box.
*/
goog.provide('goog.math.Box');
goog.require('goog.asserts');
goog.require('goog.math.Coordinate');
/**
* Class for representing a box. A box is specified as a top, right, bottom,
* and left. A box is useful for representing margins and padding.
*
* This class assumes 'screen coordinates': larger Y coordinates are further
* from the top of the screen.
*
* @param {number} top Top.
* @param {number} right Right.
* @param {number} bottom Bottom.
* @param {number} left Left.
* @struct
* @constructor
*/
goog.math.Box = function(top, right, bottom, left) {
/**
* Top
* @type {number}
*/
this.top = top;
/**
* Right
* @type {number}
*/
this.right = right;
/**
* Bottom
* @type {number}
*/
this.bottom = bottom;
/**
* Left
* @type {number}
*/
this.left = left;
};
/**
* Creates a Box by bounding a collection of goog.math.Coordinate objects
* @param {...goog.math.Coordinate} var_args Coordinates to be included inside
* the box.
* @return {!goog.math.Box} A Box containing all the specified Coordinates.
*/
goog.math.Box.boundingBox = function(var_args) {
var box = new goog.math.Box(
arguments[0].y, arguments[0].x, arguments[0].y, arguments[0].x);
for (var i = 1; i < arguments.length; i++) {
box.expandToIncludeCoordinate(arguments[i]);
}
return box;
};
/**
* @return {number} width The width of this Box.
*/
goog.math.Box.prototype.getWidth = function() {
return this.right - this.left;
};
/**
* @return {number} height The height of this Box.
*/
goog.math.Box.prototype.getHeight = function() {
return this.bottom - this.top;
};
/**
* Creates a copy of the box with the same dimensions.
* @return {!goog.math.Box} A clone of this Box.
*/
goog.math.Box.prototype.clone = function() {
return new goog.math.Box(this.top, this.right, this.bottom, this.left);
};
if (goog.DEBUG) {
/**
* Returns a nice string representing the box.
* @return {string} In the form (50t, 73r, 24b, 13l).
* @override
*/
goog.math.Box.prototype.toString = function() {
return '(' + this.top + 't, ' + this.right + 'r, ' + this.bottom + 'b, ' +
this.left + 'l)';
};
}
/**
* Returns whether the box contains a coordinate or another box.
*
* @param {goog.math.Coordinate|goog.math.Box} other A Coordinate or a Box.
* @return {boolean} Whether the box contains the coordinate or other box.
*/
goog.math.Box.prototype.contains = function(other) {
return goog.math.Box.contains(this, other);
};
/**
* Expands box with the given margins.
*
* @param {number|goog.math.Box} top Top margin or box with all margins.
* @param {number=} opt_right Right margin.
* @param {number=} opt_bottom Bottom margin.
* @param {number=} opt_left Left margin.
* @return {!goog.math.Box} A reference to this Box.
*/
goog.math.Box.prototype.expand = function(
top, opt_right, opt_bottom, opt_left) {
if (goog.isObject(top)) {
this.top -= top.top;
this.right += top.right;
this.bottom += top.bottom;
this.left -= top.left;
} else {
this.top -= /** @type {number} */ (top);
this.right += Number(opt_right);
this.bottom += Number(opt_bottom);
this.left -= Number(opt_left);
}
return this;
};
/**
* Expand this box to include another box.
* NOTE(user): This is used in code that needs to be very fast, please don't
* add functionality to this function at the expense of speed (variable
* arguments, accepting multiple argument types, etc).
* @param {goog.math.Box} box The box to include in this one.
*/
goog.math.Box.prototype.expandToInclude = function(box) {
this.left = Math.min(this.left, box.left);
this.top = Math.min(this.top, box.top);
this.right = Math.max(this.right, box.right);
this.bottom = Math.max(this.bottom, box.bottom);
};
/**
* Expand this box to include the coordinate.
* @param {!goog.math.Coordinate} coord The coordinate to be included
* inside the box.
*/
goog.math.Box.prototype.expandToIncludeCoordinate = function(coord) {
this.top = Math.min(this.top, coord.y);
this.right = Math.max(this.right, coord.x);
this.bottom = Math.max(this.bottom, coord.y);
this.left = Math.min(this.left, coord.x);
};
/**
* Compares boxes for equality.
* @param {goog.math.Box} a A Box.
* @param {goog.math.Box} b A Box.
* @return {boolean} True iff the boxes are equal, or if both are null.
*/
goog.math.Box.equals = function(a, b) {
if (a == b) {
return true;
}
if (!a || !b) {
return false;
}
return a.top == b.top && a.right == b.right && a.bottom == b.bottom &&
a.left == b.left;
};
/**
* Returns whether a box contains a coordinate or another box.
*
* @param {goog.math.Box} box A Box.
* @param {goog.math.Coordinate|goog.math.Box} other A Coordinate or a Box.
* @return {boolean} Whether the box contains the coordinate or other box.
*/
goog.math.Box.contains = function(box, other) {
if (!box || !other) {
return false;
}
if (other instanceof goog.math.Box) {
return other.left >= box.left && other.right <= box.right &&
other.top >= box.top && other.bottom <= box.bottom;
}
// other is a Coordinate.
return other.x >= box.left && other.x <= box.right && other.y >= box.top &&
other.y <= box.bottom;
};
/**
* Returns the relative x position of a coordinate compared to a box. Returns
* zero if the coordinate is inside the box.
*
* @param {goog.math.Box} box A Box.
* @param {goog.math.Coordinate} coord A Coordinate.
* @return {number} The x position of {@code coord} relative to the nearest
* side of {@code box}, or zero if {@code coord} is inside {@code box}.
*/
goog.math.Box.relativePositionX = function(box, coord) {
if (coord.x < box.left) {
return coord.x - box.left;
} else if (coord.x > box.right) {
return coord.x - box.right;
}
return 0;
};
/**
* Returns the relative y position of a coordinate compared to a box. Returns
* zero if the coordinate is inside the box.
*
* @param {goog.math.Box} box A Box.
* @param {goog.math.Coordinate} coord A Coordinate.
* @return {number} The y position of {@code coord} relative to the nearest
* side of {@code box}, or zero if {@code coord} is inside {@code box}.
*/
goog.math.Box.relativePositionY = function(box, coord) {
if (coord.y < box.top) {
return coord.y - box.top;
} else if (coord.y > box.bottom) {
return coord.y - box.bottom;
}
return 0;
};
/**
* Returns the distance between a coordinate and the nearest corner/side of a
* box. Returns zero if the coordinate is inside the box.
*
* @param {goog.math.Box} box A Box.
* @param {goog.math.Coordinate} coord A Coordinate.
* @return {number} The distance between {@code coord} and the nearest
* corner/side of {@code box}, or zero if {@code coord} is inside
* {@code box}.
*/
goog.math.Box.distance = function(box, coord) {
var x = goog.math.Box.relativePositionX(box, coord);
var y = goog.math.Box.relativePositionY(box, coord);
return Math.sqrt(x * x + y * y);
};
/**
* Returns whether two boxes intersect.
*
* @param {goog.math.Box} a A Box.
* @param {goog.math.Box} b A second Box.
* @return {boolean} Whether the boxes intersect.
*/
goog.math.Box.intersects = function(a, b) {
return (
a.left <= b.right && b.left <= a.right && a.top <= b.bottom &&
b.top <= a.bottom);
};
/**
* Returns whether two boxes would intersect with additional padding.
*
* @param {goog.math.Box} a A Box.
* @param {goog.math.Box} b A second Box.
* @param {number} padding The additional padding.
* @return {boolean} Whether the boxes intersect.
*/
goog.math.Box.intersectsWithPadding = function(a, b, padding) {
return (
a.left <= b.right + padding && b.left <= a.right + padding &&
a.top <= b.bottom + padding && b.top <= a.bottom + padding);
};
/**
* Rounds the fields to the next larger integer values.
*
* @return {!goog.math.Box} This box with ceil'd fields.
*/
goog.math.Box.prototype.ceil = function() {
this.top = Math.ceil(this.top);
this.right = Math.ceil(this.right);
this.bottom = Math.ceil(this.bottom);
this.left = Math.ceil(this.left);
return this;
};
/**
* Rounds the fields to the next smaller integer values.
*
* @return {!goog.math.Box} This box with floored fields.
*/
goog.math.Box.prototype.floor = function() {
this.top = Math.floor(this.top);
this.right = Math.floor(this.right);
this.bottom = Math.floor(this.bottom);
this.left = Math.floor(this.left);
return this;
};
/**
* Rounds the fields to nearest integer values.
*
* @return {!goog.math.Box} This box with rounded fields.
*/
goog.math.Box.prototype.round = function() {
this.top = Math.round(this.top);
this.right = Math.round(this.right);
this.bottom = Math.round(this.bottom);
this.left = Math.round(this.left);
return this;
};
/**
* Translates this box by the given offsets. If a {@code goog.math.Coordinate}
* is given, then the left and right values are translated by the coordinate's
* x value and the top and bottom values are translated by the coordinate's y
* value. Otherwise, {@code tx} and {@code opt_ty} are used to translate the x
* and y dimension values.
*
* @param {number|goog.math.Coordinate} tx The value to translate the x
* dimension values by or the the coordinate to translate this box by.
* @param {number=} opt_ty The value to translate y dimension values by.
* @return {!goog.math.Box} This box after translating.
*/
goog.math.Box.prototype.translate = function(tx, opt_ty) {
if (tx instanceof goog.math.Coordinate) {
this.left += tx.x;
this.right += tx.x;
this.top += tx.y;
this.bottom += tx.y;
} else {
goog.asserts.assertNumber(tx);
this.left += tx;
this.right += tx;
if (goog.isNumber(opt_ty)) {
this.top += opt_ty;
this.bottom += opt_ty;
}
}
return this;
};
/**
* Scales this coordinate by the given scale factors. The x and y dimension
* values are scaled by {@code sx} and {@code opt_sy} respectively.
* If {@code opt_sy} is not given, then {@code sx} is used for both x and y.
*
* @param {number} sx The scale factor to use for the x dimension.
* @param {number=} opt_sy The scale factor to use for the y dimension.
* @return {!goog.math.Box} This box after scaling.
*/
goog.math.Box.prototype.scale = function(sx, opt_sy) {
var sy = goog.isNumber(opt_sy) ? opt_sy : sx;
this.left *= sx;
this.right *= sx;
this.top *= sy;
this.bottom *= sy;
return this;
};

View File

@ -0,0 +1,279 @@
// Copyright 2006 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A utility class for representing two-dimensional positions.
*/
goog.provide('goog.math.Coordinate');
goog.require('goog.math');
/**
* Class for representing coordinates and positions.
* @param {number=} opt_x Left, defaults to 0.
* @param {number=} opt_y Top, defaults to 0.
* @struct
* @constructor
*/
goog.math.Coordinate = function(opt_x, opt_y) {
/**
* X-value
* @type {number}
*/
this.x = goog.isDef(opt_x) ? opt_x : 0;
/**
* Y-value
* @type {number}
*/
this.y = goog.isDef(opt_y) ? opt_y : 0;
};
/**
* Returns a new copy of the coordinate.
* @return {!goog.math.Coordinate} A clone of this coordinate.
*/
goog.math.Coordinate.prototype.clone = function() {
return new goog.math.Coordinate(this.x, this.y);
};
if (goog.DEBUG) {
/**
* Returns a nice string representing the coordinate.
* @return {string} In the form (50, 73).
* @override
*/
goog.math.Coordinate.prototype.toString = function() {
return '(' + this.x + ', ' + this.y + ')';
};
}
/**
* Returns whether the specified value is equal to this coordinate.
* @param {*} other Some other value.
* @return {boolean} Whether the specified value is equal to this coordinate.
*/
goog.math.Coordinate.prototype.equals = function(other) {
return other instanceof goog.math.Coordinate &&
goog.math.Coordinate.equals(this, other);
};
/**
* Compares coordinates for equality.
* @param {goog.math.Coordinate} a A Coordinate.
* @param {goog.math.Coordinate} b A Coordinate.
* @return {boolean} True iff the coordinates are equal, or if both are null.
*/
goog.math.Coordinate.equals = function(a, b) {
if (a == b) {
return true;
}
if (!a || !b) {
return false;
}
return a.x == b.x && a.y == b.y;
};
/**
* Returns the distance between two coordinates.
* @param {!goog.math.Coordinate} a A Coordinate.
* @param {!goog.math.Coordinate} b A Coordinate.
* @return {number} The distance between {@code a} and {@code b}.
*/
goog.math.Coordinate.distance = function(a, b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
return Math.sqrt(dx * dx + dy * dy);
};
/**
* Returns the magnitude of a coordinate.
* @param {!goog.math.Coordinate} a A Coordinate.
* @return {number} The distance between the origin and {@code a}.
*/
goog.math.Coordinate.magnitude = function(a) {
return Math.sqrt(a.x * a.x + a.y * a.y);
};
/**
* Returns the angle from the origin to a coordinate.
* @param {!goog.math.Coordinate} a A Coordinate.
* @return {number} The angle, in degrees, clockwise from the positive X
* axis to {@code a}.
*/
goog.math.Coordinate.azimuth = function(a) {
return goog.math.angle(0, 0, a.x, a.y);
};
/**
* Returns the squared distance between two coordinates. Squared distances can
* be used for comparisons when the actual value is not required.
*
* Performance note: eliminating the square root is an optimization often used
* in lower-level languages, but the speed difference is not nearly as
* pronounced in JavaScript (only a few percent.)
*
* @param {!goog.math.Coordinate} a A Coordinate.
* @param {!goog.math.Coordinate} b A Coordinate.
* @return {number} The squared distance between {@code a} and {@code b}.
*/
goog.math.Coordinate.squaredDistance = function(a, b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
return dx * dx + dy * dy;
};
/**
* Returns the difference between two coordinates as a new
* goog.math.Coordinate.
* @param {!goog.math.Coordinate} a A Coordinate.
* @param {!goog.math.Coordinate} b A Coordinate.
* @return {!goog.math.Coordinate} A Coordinate representing the difference
* between {@code a} and {@code b}.
*/
goog.math.Coordinate.difference = function(a, b) {
return new goog.math.Coordinate(a.x - b.x, a.y - b.y);
};
/**
* Returns the sum of two coordinates as a new goog.math.Coordinate.
* @param {!goog.math.Coordinate} a A Coordinate.
* @param {!goog.math.Coordinate} b A Coordinate.
* @return {!goog.math.Coordinate} A Coordinate representing the sum of the two
* coordinates.
*/
goog.math.Coordinate.sum = function(a, b) {
return new goog.math.Coordinate(a.x + b.x, a.y + b.y);
};
/**
* Rounds the x and y fields to the next larger integer values.
* @return {!goog.math.Coordinate} This coordinate with ceil'd fields.
*/
goog.math.Coordinate.prototype.ceil = function() {
this.x = Math.ceil(this.x);
this.y = Math.ceil(this.y);
return this;
};
/**
* Rounds the x and y fields to the next smaller integer values.
* @return {!goog.math.Coordinate} This coordinate with floored fields.
*/
goog.math.Coordinate.prototype.floor = function() {
this.x = Math.floor(this.x);
this.y = Math.floor(this.y);
return this;
};
/**
* Rounds the x and y fields to the nearest integer values.
* @return {!goog.math.Coordinate} This coordinate with rounded fields.
*/
goog.math.Coordinate.prototype.round = function() {
this.x = Math.round(this.x);
this.y = Math.round(this.y);
return this;
};
/**
* Translates this box by the given offsets. If a {@code goog.math.Coordinate}
* is given, then the x and y values are translated by the coordinate's x and y.
* Otherwise, x and y are translated by {@code tx} and {@code opt_ty}
* respectively.
* @param {number|goog.math.Coordinate} tx The value to translate x by or the
* the coordinate to translate this coordinate by.
* @param {number=} opt_ty The value to translate y by.
* @return {!goog.math.Coordinate} This coordinate after translating.
*/
goog.math.Coordinate.prototype.translate = function(tx, opt_ty) {
if (tx instanceof goog.math.Coordinate) {
this.x += tx.x;
this.y += tx.y;
} else {
this.x += Number(tx);
if (goog.isNumber(opt_ty)) {
this.y += opt_ty;
}
}
return this;
};
/**
* Scales this coordinate by the given scale factors. The x and y values are
* scaled by {@code sx} and {@code opt_sy} respectively. If {@code opt_sy}
* is not given, then {@code sx} is used for both x and y.
* @param {number} sx The scale factor to use for the x dimension.
* @param {number=} opt_sy The scale factor to use for the y dimension.
* @return {!goog.math.Coordinate} This coordinate after scaling.
*/
goog.math.Coordinate.prototype.scale = function(sx, opt_sy) {
var sy = goog.isNumber(opt_sy) ? opt_sy : sx;
this.x *= sx;
this.y *= sy;
return this;
};
/**
* Rotates this coordinate clockwise about the origin (or, optionally, the given
* center) by the given angle, in radians.
* @param {number} radians The angle by which to rotate this coordinate
* clockwise about the given center, in radians.
* @param {!goog.math.Coordinate=} opt_center The center of rotation. Defaults
* to (0, 0) if not given.
*/
goog.math.Coordinate.prototype.rotateRadians = function(radians, opt_center) {
var center = opt_center || new goog.math.Coordinate(0, 0);
var x = this.x;
var y = this.y;
var cos = Math.cos(radians);
var sin = Math.sin(radians);
this.x = (x - center.x) * cos - (y - center.y) * sin + center.x;
this.y = (x - center.x) * sin + (y - center.y) * cos + center.y;
};
/**
* Rotates this coordinate clockwise about the origin (or, optionally, the given
* center) by the given angle, in degrees.
* @param {number} degrees The angle by which to rotate this coordinate
* clockwise about the given center, in degrees.
* @param {!goog.math.Coordinate=} opt_center The center of rotation. Defaults
* to (0, 0) if not given.
*/
goog.math.Coordinate.prototype.rotateDegrees = function(degrees, opt_center) {
this.rotateRadians(goog.math.toRadians(degrees), opt_center);
};

View File

@ -0,0 +1,808 @@
// Copyright 2009 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Defines an Integer class for representing (potentially)
* infinite length two's-complement integer values.
*
* For the specific case of 64-bit integers, use goog.math.Long, which is more
* efficient.
*
*/
goog.provide('goog.math.Integer');
/**
* Constructs a two's-complement integer an array containing bits of the
* integer in 32-bit (signed) pieces, given in little-endian order (i.e.,
* lowest-order bits in the first piece), and the sign of -1 or 0.
*
* See the from* functions below for other convenient ways of constructing
* Integers.
*
* The internal representation of an integer is an array of 32-bit signed
* pieces, along with a sign (0 or -1) that indicates the contents of all the
* other 32-bit pieces out to infinity. We use 32-bit pieces because these are
* the size of integers on which Javascript performs bit-operations. For
* operations like addition and multiplication, we split each number into 16-bit
* pieces, which can easily be multiplied within Javascript's floating-point
* representation without overflow or change in sign.
*
* @struct
* @constructor
* @param {Array<number>} bits Array containing the bits of the number.
* @param {number} sign The sign of the number: -1 for negative and 0 positive.
* @final
*/
goog.math.Integer = function(bits, sign) {
/**
* @type {!Array<number>}
* @private
*/
this.bits_ = [];
/**
* @type {number}
* @private
*/
this.sign_ = sign;
// Copy the 32-bit signed integer values passed in. We prune out those at the
// top that equal the sign since they are redundant.
var top = true;
for (var i = bits.length - 1; i >= 0; i--) {
var val = bits[i] | 0;
if (!top || val != sign) {
this.bits_[i] = val;
top = false;
}
}
};
// NOTE: Common constant values ZERO, ONE, NEG_ONE, etc. are defined below the
// from* methods on which they depend.
/**
* A cache of the Integer representations of small integer values.
* @type {!Object}
* @private
*/
goog.math.Integer.IntCache_ = {};
/**
* Returns an Integer representing the given (32-bit) integer value.
* @param {number} value A 32-bit integer value.
* @return {!goog.math.Integer} The corresponding Integer value.
*/
goog.math.Integer.fromInt = function(value) {
if (-128 <= value && value < 128) {
var cachedObj = goog.math.Integer.IntCache_[value];
if (cachedObj) {
return cachedObj;
}
}
var obj = new goog.math.Integer([value | 0], value < 0 ? -1 : 0);
if (-128 <= value && value < 128) {
goog.math.Integer.IntCache_[value] = obj;
}
return obj;
};
/**
* Returns an Integer representing the given value, provided that it is a finite
* number. Otherwise, zero is returned.
* @param {number} value The value in question.
* @return {!goog.math.Integer} The corresponding Integer value.
*/
goog.math.Integer.fromNumber = function(value) {
if (isNaN(value) || !isFinite(value)) {
return goog.math.Integer.ZERO;
} else if (value < 0) {
return goog.math.Integer.fromNumber(-value).negate();
} else {
var bits = [];
var pow = 1;
for (var i = 0; value >= pow; i++) {
bits[i] = (value / pow) | 0;
pow *= goog.math.Integer.TWO_PWR_32_DBL_;
}
return new goog.math.Integer(bits, 0);
}
};
/**
* Returns a Integer representing the value that comes by concatenating the
* given entries, each is assumed to be 32 signed bits, given in little-endian
* order (lowest order bits in the lowest index), and sign-extending the highest
* order 32-bit value.
* @param {Array<number>} bits The bits of the number, in 32-bit signed pieces,
* in little-endian order.
* @return {!goog.math.Integer} The corresponding Integer value.
*/
goog.math.Integer.fromBits = function(bits) {
var high = bits[bits.length - 1];
return new goog.math.Integer(bits, high & (1 << 31) ? -1 : 0);
};
/**
* Returns an Integer representation of the given string, written using the
* given radix.
* @param {string} str The textual representation of the Integer.
* @param {number=} opt_radix The radix in which the text is written.
* @return {!goog.math.Integer} The corresponding Integer value.
*/
goog.math.Integer.fromString = function(str, opt_radix) {
if (str.length == 0) {
throw Error('number format error: empty string');
}
var radix = opt_radix || 10;
if (radix < 2 || 36 < radix) {
throw Error('radix out of range: ' + radix);
}
if (str.charAt(0) == '-') {
return goog.math.Integer.fromString(str.substring(1), radix).negate();
} else if (str.indexOf('-') >= 0) {
throw Error('number format error: interior "-" character');
}
// Do several (8) digits each time through the loop, so as to
// minimize the calls to the very expensive emulated div.
var radixToPower = goog.math.Integer.fromNumber(Math.pow(radix, 8));
var result = goog.math.Integer.ZERO;
for (var i = 0; i < str.length; i += 8) {
var size = Math.min(8, str.length - i);
var value = parseInt(str.substring(i, i + size), radix);
if (size < 8) {
var power = goog.math.Integer.fromNumber(Math.pow(radix, size));
result = result.multiply(power).add(goog.math.Integer.fromNumber(value));
} else {
result = result.multiply(radixToPower);
result = result.add(goog.math.Integer.fromNumber(value));
}
}
return result;
};
/**
* A number used repeatedly in calculations. This must appear before the first
* call to the from* functions below.
* @type {number}
* @private
*/
goog.math.Integer.TWO_PWR_32_DBL_ = (1 << 16) * (1 << 16);
/** @type {!goog.math.Integer} */
goog.math.Integer.ZERO = goog.math.Integer.fromInt(0);
/** @type {!goog.math.Integer} */
goog.math.Integer.ONE = goog.math.Integer.fromInt(1);
/**
* @type {!goog.math.Integer}
* @private
*/
goog.math.Integer.TWO_PWR_24_ = goog.math.Integer.fromInt(1 << 24);
/**
* Returns the value, assuming it is a 32-bit integer.
* @return {number} The corresponding int value.
*/
goog.math.Integer.prototype.toInt = function() {
return this.bits_.length > 0 ? this.bits_[0] : this.sign_;
};
/** @return {number} The closest floating-point representation to this value. */
goog.math.Integer.prototype.toNumber = function() {
if (this.isNegative()) {
return -this.negate().toNumber();
} else {
var val = 0;
var pow = 1;
for (var i = 0; i < this.bits_.length; i++) {
val += this.getBitsUnsigned(i) * pow;
pow *= goog.math.Integer.TWO_PWR_32_DBL_;
}
return val;
}
};
/**
* @param {number=} opt_radix The radix in which the text should be written.
* @return {string} The textual representation of this value.
* @override
*/
goog.math.Integer.prototype.toString = function(opt_radix) {
var radix = opt_radix || 10;
if (radix < 2 || 36 < radix) {
throw Error('radix out of range: ' + radix);
}
if (this.isZero()) {
return '0';
} else if (this.isNegative()) {
return '-' + this.negate().toString(radix);
}
// Do several (6) digits each time through the loop, so as to
// minimize the calls to the very expensive emulated div.
var radixToPower = goog.math.Integer.fromNumber(Math.pow(radix, 6));
var rem = this;
var result = '';
while (true) {
var remDiv = rem.divide(radixToPower);
// The right shifting fixes negative values in the case when
// intval >= 2^31; for more details see
// https://github.com/google/closure-library/pull/498
var intval = rem.subtract(remDiv.multiply(radixToPower)).toInt() >>> 0;
var digits = intval.toString(radix);
rem = remDiv;
if (rem.isZero()) {
return digits + result;
} else {
while (digits.length < 6) {
digits = '0' + digits;
}
result = '' + digits + result;
}
}
};
/**
* Returns the index-th 32-bit (signed) piece of the Integer according to
* little-endian order (i.e., index 0 contains the smallest bits).
* @param {number} index The index in question.
* @return {number} The requested 32-bits as a signed number.
*/
goog.math.Integer.prototype.getBits = function(index) {
if (index < 0) {
return 0; // Allowing this simplifies bit shifting operations below...
} else if (index < this.bits_.length) {
return this.bits_[index];
} else {
return this.sign_;
}
};
/**
* Returns the index-th 32-bit piece as an unsigned number.
* @param {number} index The index in question.
* @return {number} The requested 32-bits as an unsigned number.
*/
goog.math.Integer.prototype.getBitsUnsigned = function(index) {
var val = this.getBits(index);
return val >= 0 ? val : goog.math.Integer.TWO_PWR_32_DBL_ + val;
};
/** @return {number} The sign bit of this number, -1 or 0. */
goog.math.Integer.prototype.getSign = function() {
return this.sign_;
};
/** @return {boolean} Whether this value is zero. */
goog.math.Integer.prototype.isZero = function() {
if (this.sign_ != 0) {
return false;
}
for (var i = 0; i < this.bits_.length; i++) {
if (this.bits_[i] != 0) {
return false;
}
}
return true;
};
/** @return {boolean} Whether this value is negative. */
goog.math.Integer.prototype.isNegative = function() {
return this.sign_ == -1;
};
/** @return {boolean} Whether this value is odd. */
goog.math.Integer.prototype.isOdd = function() {
return (this.bits_.length == 0) && (this.sign_ == -1) ||
(this.bits_.length > 0) && ((this.bits_[0] & 1) != 0);
};
/**
* @param {goog.math.Integer} other Integer to compare against.
* @return {boolean} Whether this Integer equals the other.
*/
goog.math.Integer.prototype.equals = function(other) {
if (this.sign_ != other.sign_) {
return false;
}
var len = Math.max(this.bits_.length, other.bits_.length);
for (var i = 0; i < len; i++) {
if (this.getBits(i) != other.getBits(i)) {
return false;
}
}
return true;
};
/**
* @param {goog.math.Integer} other Integer to compare against.
* @return {boolean} Whether this Integer does not equal the other.
*/
goog.math.Integer.prototype.notEquals = function(other) {
return !this.equals(other);
};
/**
* @param {goog.math.Integer} other Integer to compare against.
* @return {boolean} Whether this Integer is greater than the other.
*/
goog.math.Integer.prototype.greaterThan = function(other) {
return this.compare(other) > 0;
};
/**
* @param {goog.math.Integer} other Integer to compare against.
* @return {boolean} Whether this Integer is greater than or equal to the other.
*/
goog.math.Integer.prototype.greaterThanOrEqual = function(other) {
return this.compare(other) >= 0;
};
/**
* @param {goog.math.Integer} other Integer to compare against.
* @return {boolean} Whether this Integer is less than the other.
*/
goog.math.Integer.prototype.lessThan = function(other) {
return this.compare(other) < 0;
};
/**
* @param {goog.math.Integer} other Integer to compare against.
* @return {boolean} Whether this Integer is less than or equal to the other.
*/
goog.math.Integer.prototype.lessThanOrEqual = function(other) {
return this.compare(other) <= 0;
};
/**
* Compares this Integer with the given one.
* @param {goog.math.Integer} other Integer to compare against.
* @return {number} 0 if they are the same, 1 if the this is greater, and -1
* if the given one is greater.
*/
goog.math.Integer.prototype.compare = function(other) {
var diff = this.subtract(other);
if (diff.isNegative()) {
return -1;
} else if (diff.isZero()) {
return 0;
} else {
return +1;
}
};
/**
* Returns an integer with only the first numBits bits of this value, sign
* extended from the final bit.
* @param {number} numBits The number of bits by which to shift.
* @return {!goog.math.Integer} The shorted integer value.
*/
goog.math.Integer.prototype.shorten = function(numBits) {
var arr_index = (numBits - 1) >> 5;
var bit_index = (numBits - 1) % 32;
var bits = [];
for (var i = 0; i < arr_index; i++) {
bits[i] = this.getBits(i);
}
var sigBits = bit_index == 31 ? 0xFFFFFFFF : (1 << (bit_index + 1)) - 1;
var val = this.getBits(arr_index) & sigBits;
if (val & (1 << bit_index)) {
val |= 0xFFFFFFFF - sigBits;
bits[arr_index] = val;
return new goog.math.Integer(bits, -1);
} else {
bits[arr_index] = val;
return new goog.math.Integer(bits, 0);
}
};
/** @return {!goog.math.Integer} The negation of this value. */
goog.math.Integer.prototype.negate = function() {
return this.not().add(goog.math.Integer.ONE);
};
/**
* Returns the sum of this and the given Integer.
* @param {goog.math.Integer} other The Integer to add to this.
* @return {!goog.math.Integer} The Integer result.
*/
goog.math.Integer.prototype.add = function(other) {
var len = Math.max(this.bits_.length, other.bits_.length);
var arr = [];
var carry = 0;
for (var i = 0; i <= len; i++) {
var a1 = this.getBits(i) >>> 16;
var a0 = this.getBits(i) & 0xFFFF;
var b1 = other.getBits(i) >>> 16;
var b0 = other.getBits(i) & 0xFFFF;
var c0 = carry + a0 + b0;
var c1 = (c0 >>> 16) + a1 + b1;
carry = c1 >>> 16;
c0 &= 0xFFFF;
c1 &= 0xFFFF;
arr[i] = (c1 << 16) | c0;
}
return goog.math.Integer.fromBits(arr);
};
/**
* Returns the difference of this and the given Integer.
* @param {goog.math.Integer} other The Integer to subtract from this.
* @return {!goog.math.Integer} The Integer result.
*/
goog.math.Integer.prototype.subtract = function(other) {
return this.add(other.negate());
};
/**
* Returns the product of this and the given Integer.
* @param {goog.math.Integer} other The Integer to multiply against this.
* @return {!goog.math.Integer} The product of this and the other.
*/
goog.math.Integer.prototype.multiply = function(other) {
if (this.isZero()) {
return goog.math.Integer.ZERO;
} else if (other.isZero()) {
return goog.math.Integer.ZERO;
}
if (this.isNegative()) {
if (other.isNegative()) {
return this.negate().multiply(other.negate());
} else {
return this.negate().multiply(other).negate();
}
} else if (other.isNegative()) {
return this.multiply(other.negate()).negate();
}
// If both numbers are small, use float multiplication
if (this.lessThan(goog.math.Integer.TWO_PWR_24_) &&
other.lessThan(goog.math.Integer.TWO_PWR_24_)) {
return goog.math.Integer.fromNumber(this.toNumber() * other.toNumber());
}
// Fill in an array of 16-bit products.
var len = this.bits_.length + other.bits_.length;
var arr = [];
for (var i = 0; i < 2 * len; i++) {
arr[i] = 0;
}
for (var i = 0; i < this.bits_.length; i++) {
for (var j = 0; j < other.bits_.length; j++) {
var a1 = this.getBits(i) >>> 16;
var a0 = this.getBits(i) & 0xFFFF;
var b1 = other.getBits(j) >>> 16;
var b0 = other.getBits(j) & 0xFFFF;
arr[2 * i + 2 * j] += a0 * b0;
goog.math.Integer.carry16_(arr, 2 * i + 2 * j);
arr[2 * i + 2 * j + 1] += a1 * b0;
goog.math.Integer.carry16_(arr, 2 * i + 2 * j + 1);
arr[2 * i + 2 * j + 1] += a0 * b1;
goog.math.Integer.carry16_(arr, 2 * i + 2 * j + 1);
arr[2 * i + 2 * j + 2] += a1 * b1;
goog.math.Integer.carry16_(arr, 2 * i + 2 * j + 2);
}
}
// Combine the 16-bit values into 32-bit values.
for (var i = 0; i < len; i++) {
arr[i] = (arr[2 * i + 1] << 16) | arr[2 * i];
}
for (var i = len; i < 2 * len; i++) {
arr[i] = 0;
}
return new goog.math.Integer(arr, 0);
};
/**
* Carries any overflow from the given index into later entries.
* @param {Array<number>} bits Array of 16-bit values in little-endian order.
* @param {number} index The index in question.
* @private
*/
goog.math.Integer.carry16_ = function(bits, index) {
while ((bits[index] & 0xFFFF) != bits[index]) {
bits[index + 1] += bits[index] >>> 16;
bits[index] &= 0xFFFF;
index++;
}
};
/**
* Returns "this" Integer divided by the given one. Both "this" and the given
* Integer MUST be positive.
*
* This method is only needed for very large numbers (>10^308),
* for which the original division algorithm gets into an infinite
* loop (see https://github.com/google/closure-library/issues/500).
*
* The algorithm has some possible performance enhancements (or
* could be rewritten entirely), it's just an initial solution for
* the issue linked above.
*
* @param {!goog.math.Integer} other The Integer to divide "this" by.
* @return {!goog.math.Integer} "this" value divided by the given one.
* @private
*/
goog.math.Integer.prototype.slowDivide_ = function(other) {
if (this.isNegative() || other.isNegative()) {
throw Error('slowDivide_ only works with positive integers.');
}
var twoPower = goog.math.Integer.ONE;
var multiple = other;
// First we have to figure out what the highest bit of the result
// is, so we increase "twoPower" and "multiple" until "multiple"
// exceeds "this".
while (multiple.lessThanOrEqual(this)) {
twoPower = twoPower.shiftLeft(1);
multiple = multiple.shiftLeft(1);
}
// Rewind by one power of two, giving us the highest bit of the
// result.
var res = twoPower.shiftRight(1);
var total = multiple.shiftRight(1);
// Now we starting decreasing "multiple" and "twoPower" to find the
// rest of the bits of the result.
var total2;
multiple = multiple.shiftRight(2);
twoPower = twoPower.shiftRight(2);
while (!multiple.isZero()) {
// whenever we can add "multiple" to the total and not exceed
// "this", that means we've found a 1 bit. Else we've found a 0
// and don't need to add to the result.
total2 = total.add(multiple);
if (total2.lessThanOrEqual(this)) {
res = res.add(twoPower);
total = total2;
}
multiple = multiple.shiftRight(1);
twoPower = twoPower.shiftRight(1);
}
return res;
};
/**
* Returns this Integer divided by the given one.
* @param {!goog.math.Integer} other The Integer to divide this by.
* @return {!goog.math.Integer} This value divided by the given one.
*/
goog.math.Integer.prototype.divide = function(other) {
if (other.isZero()) {
throw Error('division by zero');
} else if (this.isZero()) {
return goog.math.Integer.ZERO;
}
if (this.isNegative()) {
if (other.isNegative()) {
return this.negate().divide(other.negate());
} else {
return this.negate().divide(other).negate();
}
} else if (other.isNegative()) {
return this.divide(other.negate()).negate();
}
// Have to degrade to slowDivide for Very Large Numbers, because
// they're out of range for the floating-point approximation
// technique used below.
if (this.bits_.length > 30) {
return this.slowDivide_(other);
}
// Repeat the following until the remainder is less than other: find a
// floating-point that approximates remainder / other *from below*, add this
// into the result, and subtract it from the remainder. It is critical that
// the approximate value is less than or equal to the real value so that the
// remainder never becomes negative.
var res = goog.math.Integer.ZERO;
var rem = this;
while (rem.greaterThanOrEqual(other)) {
// Approximate the result of division. This may be a little greater or
// smaller than the actual value.
var approx = Math.max(1, Math.floor(rem.toNumber() / other.toNumber()));
// We will tweak the approximate result by changing it in the 48-th digit or
// the smallest non-fractional digit, whichever is larger.
var log2 = Math.ceil(Math.log(approx) / Math.LN2);
var delta = (log2 <= 48) ? 1 : Math.pow(2, log2 - 48);
// Decrease the approximation until it is smaller than the remainder. Note
// that if it is too large, the product overflows and is negative.
var approxRes = goog.math.Integer.fromNumber(approx);
var approxRem = approxRes.multiply(other);
while (approxRem.isNegative() || approxRem.greaterThan(rem)) {
approx -= delta;
approxRes = goog.math.Integer.fromNumber(approx);
approxRem = approxRes.multiply(other);
}
// We know the answer can't be zero... and actually, zero would cause
// infinite recursion since we would make no progress.
if (approxRes.isZero()) {
approxRes = goog.math.Integer.ONE;
}
res = res.add(approxRes);
rem = rem.subtract(approxRem);
}
return res;
};
/**
* Returns this Integer modulo the given one.
* @param {!goog.math.Integer} other The Integer by which to mod.
* @return {!goog.math.Integer} This value modulo the given one.
*/
goog.math.Integer.prototype.modulo = function(other) {
return this.subtract(this.divide(other).multiply(other));
};
/** @return {!goog.math.Integer} The bitwise-NOT of this value. */
goog.math.Integer.prototype.not = function() {
var len = this.bits_.length;
var arr = [];
for (var i = 0; i < len; i++) {
arr[i] = ~this.bits_[i];
}
return new goog.math.Integer(arr, ~this.sign_);
};
/**
* Returns the bitwise-AND of this Integer and the given one.
* @param {goog.math.Integer} other The Integer to AND with this.
* @return {!goog.math.Integer} The bitwise-AND of this and the other.
*/
goog.math.Integer.prototype.and = function(other) {
var len = Math.max(this.bits_.length, other.bits_.length);
var arr = [];
for (var i = 0; i < len; i++) {
arr[i] = this.getBits(i) & other.getBits(i);
}
return new goog.math.Integer(arr, this.sign_ & other.sign_);
};
/**
* Returns the bitwise-OR of this Integer and the given one.
* @param {goog.math.Integer} other The Integer to OR with this.
* @return {!goog.math.Integer} The bitwise-OR of this and the other.
*/
goog.math.Integer.prototype.or = function(other) {
var len = Math.max(this.bits_.length, other.bits_.length);
var arr = [];
for (var i = 0; i < len; i++) {
arr[i] = this.getBits(i) | other.getBits(i);
}
return new goog.math.Integer(arr, this.sign_ | other.sign_);
};
/**
* Returns the bitwise-XOR of this Integer and the given one.
* @param {goog.math.Integer} other The Integer to XOR with this.
* @return {!goog.math.Integer} The bitwise-XOR of this and the other.
*/
goog.math.Integer.prototype.xor = function(other) {
var len = Math.max(this.bits_.length, other.bits_.length);
var arr = [];
for (var i = 0; i < len; i++) {
arr[i] = this.getBits(i) ^ other.getBits(i);
}
return new goog.math.Integer(arr, this.sign_ ^ other.sign_);
};
/**
* Returns this value with bits shifted to the left by the given amount.
* @param {number} numBits The number of bits by which to shift.
* @return {!goog.math.Integer} This shifted to the left by the given amount.
*/
goog.math.Integer.prototype.shiftLeft = function(numBits) {
var arr_delta = numBits >> 5;
var bit_delta = numBits % 32;
var len = this.bits_.length + arr_delta + (bit_delta > 0 ? 1 : 0);
var arr = [];
for (var i = 0; i < len; i++) {
if (bit_delta > 0) {
arr[i] = (this.getBits(i - arr_delta) << bit_delta) |
(this.getBits(i - arr_delta - 1) >>> (32 - bit_delta));
} else {
arr[i] = this.getBits(i - arr_delta);
}
}
return new goog.math.Integer(arr, this.sign_);
};
/**
* Returns this value with bits shifted to the right by the given amount.
* @param {number} numBits The number of bits by which to shift.
* @return {!goog.math.Integer} This shifted to the right by the given amount.
*/
goog.math.Integer.prototype.shiftRight = function(numBits) {
var arr_delta = numBits >> 5;
var bit_delta = numBits % 32;
var len = this.bits_.length - arr_delta;
var arr = [];
for (var i = 0; i < len; i++) {
if (bit_delta > 0) {
arr[i] = (this.getBits(i + arr_delta) >>> bit_delta) |
(this.getBits(i + arr_delta + 1) << (32 - bit_delta));
} else {
arr[i] = this.getBits(i + arr_delta);
}
}
return new goog.math.Integer(arr, this.sign_);
};

View File

@ -0,0 +1,45 @@
// Copyright 2016 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A record declaration to allow ClientRect and other rectangle
* like objects to be used with goog.math.Rect.
*/
goog.provide('goog.math.IRect');
/**
* Record for representing rectangular regions, allows compatibility between
* things like ClientRect and goog.math.Rect.
*
* @record
*/
goog.math.IRect = function() {};
/** @type {number} */
goog.math.IRect.prototype.left;
/** @type {number} */
goog.math.IRect.prototype.top;
/** @type {number} */
goog.math.IRect.prototype.width;
/** @type {number} */
goog.math.IRect.prototype.height;

View File

@ -0,0 +1,965 @@
// Copyright 2009 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Defines a Long class for representing a 64-bit two's-complement
* integer value, which faithfully simulates the behavior of a Java "long". This
* implementation is derived from LongLib in GWT.
*
*/
goog.provide('goog.math.Long');
goog.require('goog.asserts');
goog.require('goog.reflect');
/**
* Constructs a 64-bit two's-complement integer, given its low and high 32-bit
* values as *signed* integers. See the from* functions below for more
* convenient ways of constructing Longs.
*
* The internal representation of a long is the two given signed, 32-bit values.
* We use 32-bit pieces because these are the size of integers on which
* Javascript performs bit-operations. For operations like addition and
* multiplication, we split each number into 16-bit pieces, which can easily be
* multiplied within Javascript's floating-point representation without overflow
* or change in sign.
*
* In the algorithms below, we frequently reduce the negative case to the
* positive case by negating the input(s) and then post-processing the result.
* Note that we must ALWAYS check specially whether those values are MIN_VALUE
* (-2^63) because -MIN_VALUE == MIN_VALUE (since 2^63 cannot be represented as
* a positive number, it overflows back into a negative). Not handling this
* case would often result in infinite recursion.
*
* @param {number} low The low (signed) 32 bits of the long.
* @param {number} high The high (signed) 32 bits of the long.
* @struct
* @constructor
* @final
*/
goog.math.Long = function(low, high) {
/**
* @type {number}
* @private
*/
this.low_ = low | 0; // force into 32 signed bits.
/**
* @type {number}
* @private
*/
this.high_ = high | 0; // force into 32 signed bits.
};
// NOTE: Common constant values ZERO, ONE, NEG_ONE, etc. are defined below the
// from* methods on which they depend.
/**
* A cache of the Long representations of small integer values.
* @type {!Object<number, !goog.math.Long>}
* @private
*/
goog.math.Long.IntCache_ = {};
/**
* A cache of the Long representations of common values.
* @type {!Object<goog.math.Long.ValueCacheId_, !goog.math.Long>}
* @private
*/
goog.math.Long.valueCache_ = {};
/**
* Returns a cached long number representing the given (32-bit) integer value.
* @param {number} value The 32-bit integer in question.
* @return {!goog.math.Long} The corresponding Long value.
* @private
*/
goog.math.Long.getCachedIntValue_ = function(value) {
return goog.reflect.cache(goog.math.Long.IntCache_, value, function(val) {
return new goog.math.Long(val, val < 0 ? -1 : 0);
});
};
/**
* The array of maximum values of a Long in string representation for a given
* radix between 2 and 36, inclusive.
* @private @const {!Array<string>}
*/
goog.math.Long.MAX_VALUE_FOR_RADIX_ = [
'', '', // unused
'111111111111111111111111111111111111111111111111111111111111111',
// base 2
'2021110011022210012102010021220101220221', // base 3
'13333333333333333333333333333333', // base 4
'1104332401304422434310311212', // base 5
'1540241003031030222122211', // base 6
'22341010611245052052300', // base 7
'777777777777777777777', // base 8
'67404283172107811827', // base 9
'9223372036854775807', // base 10
'1728002635214590697', // base 11
'41a792678515120367', // base 12
'10b269549075433c37', // base 13
'4340724c6c71dc7a7', // base 14
'160e2ad3246366807', // base 15
'7fffffffffffffff', // base 16
'33d3d8307b214008', // base 17
'16agh595df825fa7', // base 18
'ba643dci0ffeehh', // base 19
'5cbfjia3fh26ja7', // base 20
'2heiciiie82dh97', // base 21
'1adaibb21dckfa7', // base 22
'i6k448cf4192c2', // base 23
'acd772jnc9l0l7', // base 24
'64ie1focnn5g77', // base 25
'3igoecjbmca687', // base 26
'27c48l5b37oaop', // base 27
'1bk39f3ah3dmq7', // base 28
'q1se8f0m04isb', // base 29
'hajppbc1fc207', // base 30
'bm03i95hia437', // base 31
'7vvvvvvvvvvvv', // base 32
'5hg4ck9jd4u37', // base 33
'3tdtk1v8j6tpp', // base 34
'2pijmikexrxp7', // base 35
'1y2p0ij32e8e7' // base 36
];
/**
* The array of minimum values of a Long in string representation for a given
* radix between 2 and 36, inclusive.
* @private @const {!Array<string>}
*/
goog.math.Long.MIN_VALUE_FOR_RADIX_ = [
'', '', // unused
'-1000000000000000000000000000000000000000000000000000000000000000',
// base 2
'-2021110011022210012102010021220101220222', // base 3
'-20000000000000000000000000000000', // base 4
'-1104332401304422434310311213', // base 5
'-1540241003031030222122212', // base 6
'-22341010611245052052301', // base 7
'-1000000000000000000000', // base 8
'-67404283172107811828', // base 9
'-9223372036854775808', // base 10
'-1728002635214590698', // base 11
'-41a792678515120368', // base 12
'-10b269549075433c38', // base 13
'-4340724c6c71dc7a8', // base 14
'-160e2ad3246366808', // base 15
'-8000000000000000', // base 16
'-33d3d8307b214009', // base 17
'-16agh595df825fa8', // base 18
'-ba643dci0ffeehi', // base 19
'-5cbfjia3fh26ja8', // base 20
'-2heiciiie82dh98', // base 21
'-1adaibb21dckfa8', // base 22
'-i6k448cf4192c3', // base 23
'-acd772jnc9l0l8', // base 24
'-64ie1focnn5g78', // base 25
'-3igoecjbmca688', // base 26
'-27c48l5b37oaoq', // base 27
'-1bk39f3ah3dmq8', // base 28
'-q1se8f0m04isc', // base 29
'-hajppbc1fc208', // base 30
'-bm03i95hia438', // base 31
'-8000000000000', // base 32
'-5hg4ck9jd4u38', // base 33
'-3tdtk1v8j6tpq', // base 34
'-2pijmikexrxp8', // base 35
'-1y2p0ij32e8e8' // base 36
];
/**
* Returns a Long representing the given (32-bit) integer value.
* @param {number} value The 32-bit integer in question.
* @return {!goog.math.Long} The corresponding Long value.
*/
goog.math.Long.fromInt = function(value) {
var intValue = value | 0;
goog.asserts.assert(value === intValue, 'value should be a 32-bit integer');
if (-128 <= intValue && intValue < 128) {
return goog.math.Long.getCachedIntValue_(intValue);
} else {
return new goog.math.Long(intValue, intValue < 0 ? -1 : 0);
}
};
/**
* Returns a Long representing the given value.
* NaN will be returned as zero. Infinity is converted to max value and
* -Infinity to min value.
* @param {number} value The number in question.
* @return {!goog.math.Long} The corresponding Long value.
*/
goog.math.Long.fromNumber = function(value) {
if (isNaN(value)) {
return goog.math.Long.getZero();
} else if (value <= -goog.math.Long.TWO_PWR_63_DBL_) {
return goog.math.Long.getMinValue();
} else if (value + 1 >= goog.math.Long.TWO_PWR_63_DBL_) {
return goog.math.Long.getMaxValue();
} else if (value < 0) {
return goog.math.Long.fromNumber(-value).negate();
} else {
return new goog.math.Long(
(value % goog.math.Long.TWO_PWR_32_DBL_) | 0,
(value / goog.math.Long.TWO_PWR_32_DBL_) | 0);
}
};
/**
* Returns a Long representing the 64-bit integer that comes by concatenating
* the given high and low bits. Each is assumed to use 32 bits.
* @param {number} lowBits The low 32-bits.
* @param {number} highBits The high 32-bits.
* @return {!goog.math.Long} The corresponding Long value.
*/
goog.math.Long.fromBits = function(lowBits, highBits) {
return new goog.math.Long(lowBits, highBits);
};
/**
* Returns a Long representation of the given string, written using the given
* radix.
* @param {string} str The textual representation of the Long.
* @param {number=} opt_radix The radix in which the text is written.
* @return {!goog.math.Long} The corresponding Long value.
*/
goog.math.Long.fromString = function(str, opt_radix) {
if (str.length == 0) {
throw Error('number format error: empty string');
}
var radix = opt_radix || 10;
if (radix < 2 || 36 < radix) {
throw Error('radix out of range: ' + radix);
}
if (str.charAt(0) == '-') {
return goog.math.Long.fromString(str.substring(1), radix).negate();
} else if (str.indexOf('-') >= 0) {
throw Error('number format error: interior "-" character: ' + str);
}
// Do several (8) digits each time through the loop, so as to
// minimize the calls to the very expensive emulated div.
var radixToPower = goog.math.Long.fromNumber(Math.pow(radix, 8));
var result = goog.math.Long.getZero();
for (var i = 0; i < str.length; i += 8) {
var size = Math.min(8, str.length - i);
var value = parseInt(str.substring(i, i + size), radix);
if (size < 8) {
var power = goog.math.Long.fromNumber(Math.pow(radix, size));
result = result.multiply(power).add(goog.math.Long.fromNumber(value));
} else {
result = result.multiply(radixToPower);
result = result.add(goog.math.Long.fromNumber(value));
}
}
return result;
};
/**
* Returns the boolean value of whether the input string is within a Long's
* range. Assumes an input string containing only numeric characters with an
* optional preceding '-'.
* @param {string} str The textual representation of the Long.
* @param {number=} opt_radix The radix in which the text is written.
* @return {boolean} Whether the string is within the range of a Long.
*/
goog.math.Long.isStringInRange = function(str, opt_radix) {
var radix = opt_radix || 10;
if (radix < 2 || 36 < radix) {
throw Error('radix out of range: ' + radix);
}
var extremeValue = (str.charAt(0) == '-') ?
goog.math.Long.MIN_VALUE_FOR_RADIX_[radix] :
goog.math.Long.MAX_VALUE_FOR_RADIX_[radix];
if (str.length < extremeValue.length) {
return true;
} else if (str.length == extremeValue.length && str <= extremeValue) {
return true;
} else {
return false;
}
};
// NOTE: the compiler should inline these constant values below and then remove
// these variables, so there should be no runtime penalty for these.
/**
* Number used repeated below in calculations. This must appear before the
* first call to any from* function below.
* @type {number}
* @private
*/
goog.math.Long.TWO_PWR_16_DBL_ = 1 << 16;
/**
* @type {number}
* @private
*/
goog.math.Long.TWO_PWR_32_DBL_ =
goog.math.Long.TWO_PWR_16_DBL_ * goog.math.Long.TWO_PWR_16_DBL_;
/**
* @type {number}
* @private
*/
goog.math.Long.TWO_PWR_64_DBL_ =
goog.math.Long.TWO_PWR_32_DBL_ * goog.math.Long.TWO_PWR_32_DBL_;
/**
* @type {number}
* @private
*/
goog.math.Long.TWO_PWR_63_DBL_ = goog.math.Long.TWO_PWR_64_DBL_ / 2;
/**
* @return {!goog.math.Long}
* @public
*/
goog.math.Long.getZero = function() {
return goog.math.Long.getCachedIntValue_(0);
};
/**
* @return {!goog.math.Long}
* @public
*/
goog.math.Long.getOne = function() {
return goog.math.Long.getCachedIntValue_(1);
};
/**
* @return {!goog.math.Long}
* @public
*/
goog.math.Long.getNegOne = function() {
return goog.math.Long.getCachedIntValue_(-1);
};
/**
* @return {!goog.math.Long}
* @public
*/
goog.math.Long.getMaxValue = function() {
return goog.reflect.cache(
goog.math.Long.valueCache_, goog.math.Long.ValueCacheId_.MAX_VALUE,
function() {
return goog.math.Long.fromBits(0xFFFFFFFF | 0, 0x7FFFFFFF | 0);
});
};
/**
* @return {!goog.math.Long}
* @public
*/
goog.math.Long.getMinValue = function() {
return goog.reflect.cache(
goog.math.Long.valueCache_, goog.math.Long.ValueCacheId_.MIN_VALUE,
function() { return goog.math.Long.fromBits(0, 0x80000000 | 0); });
};
/**
* @return {!goog.math.Long}
* @public
*/
goog.math.Long.getTwoPwr24 = function() {
return goog.reflect.cache(
goog.math.Long.valueCache_, goog.math.Long.ValueCacheId_.TWO_PWR_24,
function() { return goog.math.Long.fromInt(1 << 24); });
};
/** @return {number} The value, assuming it is a 32-bit integer. */
goog.math.Long.prototype.toInt = function() {
return this.low_;
};
/** @return {number} The closest floating-point representation to this value. */
goog.math.Long.prototype.toNumber = function() {
return this.high_ * goog.math.Long.TWO_PWR_32_DBL_ +
this.getLowBitsUnsigned();
};
/**
* @param {number=} opt_radix The radix in which the text should be written.
* @return {string} The textual representation of this value.
* @override
*/
goog.math.Long.prototype.toString = function(opt_radix) {
var radix = opt_radix || 10;
if (radix < 2 || 36 < radix) {
throw Error('radix out of range: ' + radix);
}
if (this.isZero()) {
return '0';
}
if (this.isNegative()) {
if (this.equals(goog.math.Long.getMinValue())) {
// We need to change the Long value before it can be negated, so we remove
// the bottom-most digit in this base and then recurse to do the rest.
var radixLong = goog.math.Long.fromNumber(radix);
var div = this.div(radixLong);
var rem = div.multiply(radixLong).subtract(this);
return div.toString(radix) + rem.toInt().toString(radix);
} else {
return '-' + this.negate().toString(radix);
}
}
// Do several (6) digits each time through the loop, so as to
// minimize the calls to the very expensive emulated div.
var radixToPower = goog.math.Long.fromNumber(Math.pow(radix, 6));
var rem = this;
var result = '';
while (true) {
var remDiv = rem.div(radixToPower);
// The right shifting fixes negative values in the case when
// intval >= 2^31; for more details see
// https://github.com/google/closure-library/pull/498
var intval = rem.subtract(remDiv.multiply(radixToPower)).toInt() >>> 0;
var digits = intval.toString(radix);
rem = remDiv;
if (rem.isZero()) {
return digits + result;
} else {
while (digits.length < 6) {
digits = '0' + digits;
}
result = '' + digits + result;
}
}
};
/** @return {number} The high 32-bits as a signed value. */
goog.math.Long.prototype.getHighBits = function() {
return this.high_;
};
/** @return {number} The low 32-bits as a signed value. */
goog.math.Long.prototype.getLowBits = function() {
return this.low_;
};
/** @return {number} The low 32-bits as an unsigned value. */
goog.math.Long.prototype.getLowBitsUnsigned = function() {
return (this.low_ >= 0) ? this.low_ :
goog.math.Long.TWO_PWR_32_DBL_ + this.low_;
};
/**
* @return {number} Returns the number of bits needed to represent the absolute
* value of this Long.
*/
goog.math.Long.prototype.getNumBitsAbs = function() {
if (this.isNegative()) {
if (this.equals(goog.math.Long.getMinValue())) {
return 64;
} else {
return this.negate().getNumBitsAbs();
}
} else {
var val = this.high_ != 0 ? this.high_ : this.low_;
for (var bit = 31; bit > 0; bit--) {
if ((val & (1 << bit)) != 0) {
break;
}
}
return this.high_ != 0 ? bit + 33 : bit + 1;
}
};
/** @return {boolean} Whether this value is zero. */
goog.math.Long.prototype.isZero = function() {
return this.high_ == 0 && this.low_ == 0;
};
/** @return {boolean} Whether this value is negative. */
goog.math.Long.prototype.isNegative = function() {
return this.high_ < 0;
};
/** @return {boolean} Whether this value is odd. */
goog.math.Long.prototype.isOdd = function() {
return (this.low_ & 1) == 1;
};
/**
* @param {goog.math.Long} other Long to compare against.
* @return {boolean} Whether this Long equals the other.
*/
goog.math.Long.prototype.equals = function(other) {
return (this.high_ == other.high_) && (this.low_ == other.low_);
};
/**
* @param {goog.math.Long} other Long to compare against.
* @return {boolean} Whether this Long does not equal the other.
*/
goog.math.Long.prototype.notEquals = function(other) {
return (this.high_ != other.high_) || (this.low_ != other.low_);
};
/**
* @param {goog.math.Long} other Long to compare against.
* @return {boolean} Whether this Long is less than the other.
*/
goog.math.Long.prototype.lessThan = function(other) {
return this.compare(other) < 0;
};
/**
* @param {goog.math.Long} other Long to compare against.
* @return {boolean} Whether this Long is less than or equal to the other.
*/
goog.math.Long.prototype.lessThanOrEqual = function(other) {
return this.compare(other) <= 0;
};
/**
* @param {goog.math.Long} other Long to compare against.
* @return {boolean} Whether this Long is greater than the other.
*/
goog.math.Long.prototype.greaterThan = function(other) {
return this.compare(other) > 0;
};
/**
* @param {goog.math.Long} other Long to compare against.
* @return {boolean} Whether this Long is greater than or equal to the other.
*/
goog.math.Long.prototype.greaterThanOrEqual = function(other) {
return this.compare(other) >= 0;
};
/**
* Compares this Long with the given one.
* @param {goog.math.Long} other Long to compare against.
* @return {number} 0 if they are the same, 1 if the this is greater, and -1
* if the given one is greater.
*/
goog.math.Long.prototype.compare = function(other) {
if (this.equals(other)) {
return 0;
}
var thisNeg = this.isNegative();
var otherNeg = other.isNegative();
if (thisNeg && !otherNeg) {
return -1;
}
if (!thisNeg && otherNeg) {
return 1;
}
// at this point, the signs are the same, so subtraction will not overflow
if (this.subtract(other).isNegative()) {
return -1;
} else {
return 1;
}
};
/** @return {!goog.math.Long} The negation of this value. */
goog.math.Long.prototype.negate = function() {
if (this.equals(goog.math.Long.getMinValue())) {
return goog.math.Long.getMinValue();
} else {
return this.not().add(goog.math.Long.getOne());
}
};
/**
* Returns the sum of this and the given Long.
* @param {goog.math.Long} other Long to add to this one.
* @return {!goog.math.Long} The sum of this and the given Long.
*/
goog.math.Long.prototype.add = function(other) {
// Divide each number into 4 chunks of 16 bits, and then sum the chunks.
var a48 = this.high_ >>> 16;
var a32 = this.high_ & 0xFFFF;
var a16 = this.low_ >>> 16;
var a00 = this.low_ & 0xFFFF;
var b48 = other.high_ >>> 16;
var b32 = other.high_ & 0xFFFF;
var b16 = other.low_ >>> 16;
var b00 = other.low_ & 0xFFFF;
var c48 = 0, c32 = 0, c16 = 0, c00 = 0;
c00 += a00 + b00;
c16 += c00 >>> 16;
c00 &= 0xFFFF;
c16 += a16 + b16;
c32 += c16 >>> 16;
c16 &= 0xFFFF;
c32 += a32 + b32;
c48 += c32 >>> 16;
c32 &= 0xFFFF;
c48 += a48 + b48;
c48 &= 0xFFFF;
return goog.math.Long.fromBits((c16 << 16) | c00, (c48 << 16) | c32);
};
/**
* Returns the difference of this and the given Long.
* @param {goog.math.Long} other Long to subtract from this.
* @return {!goog.math.Long} The difference of this and the given Long.
*/
goog.math.Long.prototype.subtract = function(other) {
return this.add(other.negate());
};
/**
* Returns the product of this and the given long.
* @param {goog.math.Long} other Long to multiply with this.
* @return {!goog.math.Long} The product of this and the other.
*/
goog.math.Long.prototype.multiply = function(other) {
if (this.isZero()) {
return goog.math.Long.getZero();
} else if (other.isZero()) {
return goog.math.Long.getZero();
}
if (this.equals(goog.math.Long.getMinValue())) {
return other.isOdd() ? goog.math.Long.getMinValue() :
goog.math.Long.getZero();
} else if (other.equals(goog.math.Long.getMinValue())) {
return this.isOdd() ? goog.math.Long.getMinValue() :
goog.math.Long.getZero();
}
if (this.isNegative()) {
if (other.isNegative()) {
return this.negate().multiply(other.negate());
} else {
return this.negate().multiply(other).negate();
}
} else if (other.isNegative()) {
return this.multiply(other.negate()).negate();
}
// If both longs are small, use float multiplication
if (this.lessThan(goog.math.Long.getTwoPwr24()) &&
other.lessThan(goog.math.Long.getTwoPwr24())) {
return goog.math.Long.fromNumber(this.toNumber() * other.toNumber());
}
// Divide each long into 4 chunks of 16 bits, and then add up 4x4 products.
// We can skip products that would overflow.
var a48 = this.high_ >>> 16;
var a32 = this.high_ & 0xFFFF;
var a16 = this.low_ >>> 16;
var a00 = this.low_ & 0xFFFF;
var b48 = other.high_ >>> 16;
var b32 = other.high_ & 0xFFFF;
var b16 = other.low_ >>> 16;
var b00 = other.low_ & 0xFFFF;
var c48 = 0, c32 = 0, c16 = 0, c00 = 0;
c00 += a00 * b00;
c16 += c00 >>> 16;
c00 &= 0xFFFF;
c16 += a16 * b00;
c32 += c16 >>> 16;
c16 &= 0xFFFF;
c16 += a00 * b16;
c32 += c16 >>> 16;
c16 &= 0xFFFF;
c32 += a32 * b00;
c48 += c32 >>> 16;
c32 &= 0xFFFF;
c32 += a16 * b16;
c48 += c32 >>> 16;
c32 &= 0xFFFF;
c32 += a00 * b32;
c48 += c32 >>> 16;
c32 &= 0xFFFF;
c48 += a48 * b00 + a32 * b16 + a16 * b32 + a00 * b48;
c48 &= 0xFFFF;
return goog.math.Long.fromBits((c16 << 16) | c00, (c48 << 16) | c32);
};
/**
* Returns this Long divided by the given one.
* @param {goog.math.Long} other Long by which to divide.
* @return {!goog.math.Long} This Long divided by the given one.
*/
goog.math.Long.prototype.div = function(other) {
if (other.isZero()) {
throw Error('division by zero');
} else if (this.isZero()) {
return goog.math.Long.getZero();
}
if (this.equals(goog.math.Long.getMinValue())) {
if (other.equals(goog.math.Long.getOne()) ||
other.equals(goog.math.Long.getNegOne())) {
return goog.math.Long.getMinValue(); // recall -MIN_VALUE == MIN_VALUE
} else if (other.equals(goog.math.Long.getMinValue())) {
return goog.math.Long.getOne();
} else {
// At this point, we have |other| >= 2, so |this/other| < |MIN_VALUE|.
var halfThis = this.shiftRight(1);
var approx = halfThis.div(other).shiftLeft(1);
if (approx.equals(goog.math.Long.getZero())) {
return other.isNegative() ? goog.math.Long.getOne() :
goog.math.Long.getNegOne();
} else {
var rem = this.subtract(other.multiply(approx));
var result = approx.add(rem.div(other));
return result;
}
}
} else if (other.equals(goog.math.Long.getMinValue())) {
return goog.math.Long.getZero();
}
if (this.isNegative()) {
if (other.isNegative()) {
return this.negate().div(other.negate());
} else {
return this.negate().div(other).negate();
}
} else if (other.isNegative()) {
return this.div(other.negate()).negate();
}
// Repeat the following until the remainder is less than other: find a
// floating-point that approximates remainder / other *from below*, add this
// into the result, and subtract it from the remainder. It is critical that
// the approximate value is less than or equal to the real value so that the
// remainder never becomes negative.
var res = goog.math.Long.getZero();
var rem = this;
while (rem.greaterThanOrEqual(other)) {
// Approximate the result of division. This may be a little greater or
// smaller than the actual value.
var approx = Math.max(1, Math.floor(rem.toNumber() / other.toNumber()));
// We will tweak the approximate result by changing it in the 48-th digit or
// the smallest non-fractional digit, whichever is larger.
var log2 = Math.ceil(Math.log(approx) / Math.LN2);
var delta = (log2 <= 48) ? 1 : Math.pow(2, log2 - 48);
// Decrease the approximation until it is smaller than the remainder. Note
// that if it is too large, the product overflows and is negative.
var approxRes = goog.math.Long.fromNumber(approx);
var approxRem = approxRes.multiply(other);
while (approxRem.isNegative() || approxRem.greaterThan(rem)) {
approx -= delta;
approxRes = goog.math.Long.fromNumber(approx);
approxRem = approxRes.multiply(other);
}
// We know the answer can't be zero... and actually, zero would cause
// infinite recursion since we would make no progress.
if (approxRes.isZero()) {
approxRes = goog.math.Long.getOne();
}
res = res.add(approxRes);
rem = rem.subtract(approxRem);
}
return res;
};
/**
* Returns this Long modulo the given one.
* @param {goog.math.Long} other Long by which to mod.
* @return {!goog.math.Long} This Long modulo the given one.
*/
goog.math.Long.prototype.modulo = function(other) {
return this.subtract(this.div(other).multiply(other));
};
/** @return {!goog.math.Long} The bitwise-NOT of this value. */
goog.math.Long.prototype.not = function() {
return goog.math.Long.fromBits(~this.low_, ~this.high_);
};
/**
* Returns the bitwise-AND of this Long and the given one.
* @param {goog.math.Long} other The Long with which to AND.
* @return {!goog.math.Long} The bitwise-AND of this and the other.
*/
goog.math.Long.prototype.and = function(other) {
return goog.math.Long.fromBits(
this.low_ & other.low_, this.high_ & other.high_);
};
/**
* Returns the bitwise-OR of this Long and the given one.
* @param {goog.math.Long} other The Long with which to OR.
* @return {!goog.math.Long} The bitwise-OR of this and the other.
*/
goog.math.Long.prototype.or = function(other) {
return goog.math.Long.fromBits(
this.low_ | other.low_, this.high_ | other.high_);
};
/**
* Returns the bitwise-XOR of this Long and the given one.
* @param {goog.math.Long} other The Long with which to XOR.
* @return {!goog.math.Long} The bitwise-XOR of this and the other.
*/
goog.math.Long.prototype.xor = function(other) {
return goog.math.Long.fromBits(
this.low_ ^ other.low_, this.high_ ^ other.high_);
};
/**
* Returns this Long with bits shifted to the left by the given amount.
* @param {number} numBits The number of bits by which to shift.
* @return {!goog.math.Long} This shifted to the left by the given amount.
*/
goog.math.Long.prototype.shiftLeft = function(numBits) {
numBits &= 63;
if (numBits == 0) {
return this;
} else {
var low = this.low_;
if (numBits < 32) {
var high = this.high_;
return goog.math.Long.fromBits(
low << numBits, (high << numBits) | (low >>> (32 - numBits)));
} else {
return goog.math.Long.fromBits(0, low << (numBits - 32));
}
}
};
/**
* Returns this Long with bits shifted to the right by the given amount.
* The new leading bits match the current sign bit.
* @param {number} numBits The number of bits by which to shift.
* @return {!goog.math.Long} This shifted to the right by the given amount.
*/
goog.math.Long.prototype.shiftRight = function(numBits) {
numBits &= 63;
if (numBits == 0) {
return this;
} else {
var high = this.high_;
if (numBits < 32) {
var low = this.low_;
return goog.math.Long.fromBits(
(low >>> numBits) | (high << (32 - numBits)), high >> numBits);
} else {
return goog.math.Long.fromBits(
high >> (numBits - 32), high >= 0 ? 0 : -1);
}
}
};
/**
* Returns this Long with bits shifted to the right by the given amount, with
* zeros placed into the new leading bits.
* @param {number} numBits The number of bits by which to shift.
* @return {!goog.math.Long} This shifted to the right by the given amount, with
* zeros placed into the new leading bits.
*/
goog.math.Long.prototype.shiftRightUnsigned = function(numBits) {
numBits &= 63;
if (numBits == 0) {
return this;
} else {
var high = this.high_;
if (numBits < 32) {
var low = this.low_;
return goog.math.Long.fromBits(
(low >>> numBits) | (high << (32 - numBits)), high >>> numBits);
} else if (numBits == 32) {
return goog.math.Long.fromBits(high, 0);
} else {
return goog.math.Long.fromBits(high >>> (numBits - 32), 0);
}
}
};
/**
* @enum {number} Ids of commonly requested Long instances.
* @private
*/
goog.math.Long.ValueCacheId_ = {
MAX_VALUE: 1,
MIN_VALUE: 2,
TWO_PWR_24: 6
};

View File

@ -0,0 +1,448 @@
// Copyright 2006 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Additional mathematical functions.
*/
goog.provide('goog.math');
goog.require('goog.array');
goog.require('goog.asserts');
/**
* Returns a random integer greater than or equal to 0 and less than {@code a}.
* @param {number} a The upper bound for the random integer (exclusive).
* @return {number} A random integer N such that 0 <= N < a.
*/
goog.math.randomInt = function(a) {
return Math.floor(Math.random() * a);
};
/**
* Returns a random number greater than or equal to {@code a} and less than
* {@code b}.
* @param {number} a The lower bound for the random number (inclusive).
* @param {number} b The upper bound for the random number (exclusive).
* @return {number} A random number N such that a <= N < b.
*/
goog.math.uniformRandom = function(a, b) {
return a + Math.random() * (b - a);
};
/**
* Takes a number and clamps it to within the provided bounds.
* @param {number} value The input number.
* @param {number} min The minimum value to return.
* @param {number} max The maximum value to return.
* @return {number} The input number if it is within bounds, or the nearest
* number within the bounds.
*/
goog.math.clamp = function(value, min, max) {
return Math.min(Math.max(value, min), max);
};
/**
* The % operator in JavaScript returns the remainder of a / b, but differs from
* some other languages in that the result will have the same sign as the
* dividend. For example, -1 % 8 == -1, whereas in some other languages
* (such as Python) the result would be 7. This function emulates the more
* correct modulo behavior, which is useful for certain applications such as
* calculating an offset index in a circular list.
*
* @param {number} a The dividend.
* @param {number} b The divisor.
* @return {number} a % b where the result is between 0 and b (either 0 <= x < b
* or b < x <= 0, depending on the sign of b).
*/
goog.math.modulo = function(a, b) {
var r = a % b;
// If r and b differ in sign, add b to wrap the result to the correct sign.
return (r * b < 0) ? r + b : r;
};
/**
* Performs linear interpolation between values a and b. Returns the value
* between a and b proportional to x (when x is between 0 and 1. When x is
* outside this range, the return value is a linear extrapolation).
* @param {number} a A number.
* @param {number} b A number.
* @param {number} x The proportion between a and b.
* @return {number} The interpolated value between a and b.
*/
goog.math.lerp = function(a, b, x) {
return a + x * (b - a);
};
/**
* Tests whether the two values are equal to each other, within a certain
* tolerance to adjust for floating point errors.
* @param {number} a A number.
* @param {number} b A number.
* @param {number=} opt_tolerance Optional tolerance range. Defaults
* to 0.000001. If specified, should be greater than 0.
* @return {boolean} Whether {@code a} and {@code b} are nearly equal.
*/
goog.math.nearlyEquals = function(a, b, opt_tolerance) {
return Math.abs(a - b) <= (opt_tolerance || 0.000001);
};
// TODO(user): Rename to normalizeAngle, retaining old name as deprecated
// alias.
/**
* Normalizes an angle to be in range [0-360). Angles outside this range will
* be normalized to be the equivalent angle with that range.
* @param {number} angle Angle in degrees.
* @return {number} Standardized angle.
*/
goog.math.standardAngle = function(angle) {
return goog.math.modulo(angle, 360);
};
/**
* Normalizes an angle to be in range [0-2*PI). Angles outside this range will
* be normalized to be the equivalent angle with that range.
* @param {number} angle Angle in radians.
* @return {number} Standardized angle.
*/
goog.math.standardAngleInRadians = function(angle) {
return goog.math.modulo(angle, 2 * Math.PI);
};
/**
* Converts degrees to radians.
* @param {number} angleDegrees Angle in degrees.
* @return {number} Angle in radians.
*/
goog.math.toRadians = function(angleDegrees) {
return angleDegrees * Math.PI / 180;
};
/**
* Converts radians to degrees.
* @param {number} angleRadians Angle in radians.
* @return {number} Angle in degrees.
*/
goog.math.toDegrees = function(angleRadians) {
return angleRadians * 180 / Math.PI;
};
/**
* For a given angle and radius, finds the X portion of the offset.
* @param {number} degrees Angle in degrees (zero points in +X direction).
* @param {number} radius Radius.
* @return {number} The x-distance for the angle and radius.
*/
goog.math.angleDx = function(degrees, radius) {
return radius * Math.cos(goog.math.toRadians(degrees));
};
/**
* For a given angle and radius, finds the Y portion of the offset.
* @param {number} degrees Angle in degrees (zero points in +X direction).
* @param {number} radius Radius.
* @return {number} The y-distance for the angle and radius.
*/
goog.math.angleDy = function(degrees, radius) {
return radius * Math.sin(goog.math.toRadians(degrees));
};
/**
* Computes the angle between two points (x1,y1) and (x2,y2).
* Angle zero points in the +X direction, 90 degrees points in the +Y
* direction (down) and from there we grow clockwise towards 360 degrees.
* @param {number} x1 x of first point.
* @param {number} y1 y of first point.
* @param {number} x2 x of second point.
* @param {number} y2 y of second point.
* @return {number} Standardized angle in degrees of the vector from
* x1,y1 to x2,y2.
*/
goog.math.angle = function(x1, y1, x2, y2) {
return goog.math.standardAngle(
goog.math.toDegrees(Math.atan2(y2 - y1, x2 - x1)));
};
/**
* Computes the difference between startAngle and endAngle (angles in degrees).
* @param {number} startAngle Start angle in degrees.
* @param {number} endAngle End angle in degrees.
* @return {number} The number of degrees that when added to
* startAngle will result in endAngle. Positive numbers mean that the
* direction is clockwise. Negative numbers indicate a counter-clockwise
* direction.
* The shortest route (clockwise vs counter-clockwise) between the angles
* is used.
* When the difference is 180 degrees, the function returns 180 (not -180)
* angleDifference(30, 40) is 10, and angleDifference(40, 30) is -10.
* angleDifference(350, 10) is 20, and angleDifference(10, 350) is -20.
*/
goog.math.angleDifference = function(startAngle, endAngle) {
var d =
goog.math.standardAngle(endAngle) - goog.math.standardAngle(startAngle);
if (d > 180) {
d = d - 360;
} else if (d <= -180) {
d = 360 + d;
}
return d;
};
/**
* Returns the sign of a number as per the "sign" or "signum" function.
* @param {number} x The number to take the sign of.
* @return {number} -1 when negative, 1 when positive, 0 when 0. Preserves
* signed zeros and NaN.
*/
goog.math.sign = function(x) {
if (x > 0) {
return 1;
}
if (x < 0) {
return -1;
}
return x; // Preserves signed zeros and NaN.
};
/**
* JavaScript implementation of Longest Common Subsequence problem.
* http://en.wikipedia.org/wiki/Longest_common_subsequence
*
* Returns the longest possible array that is subarray of both of given arrays.
*
* @param {IArrayLike<S>} array1 First array of objects.
* @param {IArrayLike<T>} array2 Second array of objects.
* @param {Function=} opt_compareFn Function that acts as a custom comparator
* for the array ojects. Function should return true if objects are equal,
* otherwise false.
* @param {Function=} opt_collectorFn Function used to decide what to return
* as a result subsequence. It accepts 2 arguments: index of common element
* in the first array and index in the second. The default function returns
* element from the first array.
* @return {!Array<S|T>} A list of objects that are common to both arrays
* such that there is no common subsequence with size greater than the
* length of the list.
* @template S,T
*/
goog.math.longestCommonSubsequence = function(
array1, array2, opt_compareFn, opt_collectorFn) {
var compare = opt_compareFn || function(a, b) { return a == b; };
var collect = opt_collectorFn || function(i1, i2) { return array1[i1]; };
var length1 = array1.length;
var length2 = array2.length;
var arr = [];
for (var i = 0; i < length1 + 1; i++) {
arr[i] = [];
arr[i][0] = 0;
}
for (var j = 0; j < length2 + 1; j++) {
arr[0][j] = 0;
}
for (i = 1; i <= length1; i++) {
for (j = 1; j <= length2; j++) {
if (compare(array1[i - 1], array2[j - 1])) {
arr[i][j] = arr[i - 1][j - 1] + 1;
} else {
arr[i][j] = Math.max(arr[i - 1][j], arr[i][j - 1]);
}
}
}
// Backtracking
var result = [];
var i = length1, j = length2;
while (i > 0 && j > 0) {
if (compare(array1[i - 1], array2[j - 1])) {
result.unshift(collect(i - 1, j - 1));
i--;
j--;
} else {
if (arr[i - 1][j] > arr[i][j - 1]) {
i--;
} else {
j--;
}
}
}
return result;
};
/**
* Returns the sum of the arguments.
* @param {...number} var_args Numbers to add.
* @return {number} The sum of the arguments (0 if no arguments were provided,
* {@code NaN} if any of the arguments is not a valid number).
*/
goog.math.sum = function(var_args) {
return /** @type {number} */ (
goog.array.reduce(
arguments, function(sum, value) { return sum + value; }, 0));
};
/**
* Returns the arithmetic mean of the arguments.
* @param {...number} var_args Numbers to average.
* @return {number} The average of the arguments ({@code NaN} if no arguments
* were provided or any of the arguments is not a valid number).
*/
goog.math.average = function(var_args) {
return goog.math.sum.apply(null, arguments) / arguments.length;
};
/**
* Returns the unbiased sample variance of the arguments. For a definition,
* see e.g. http://en.wikipedia.org/wiki/Variance
* @param {...number} var_args Number samples to analyze.
* @return {number} The unbiased sample variance of the arguments (0 if fewer
* than two samples were provided, or {@code NaN} if any of the samples is
* not a valid number).
*/
goog.math.sampleVariance = function(var_args) {
var sampleSize = arguments.length;
if (sampleSize < 2) {
return 0;
}
var mean = goog.math.average.apply(null, arguments);
var variance =
goog.math.sum.apply(null, goog.array.map(arguments, function(val) {
return Math.pow(val - mean, 2);
})) / (sampleSize - 1);
return variance;
};
/**
* Returns the sample standard deviation of the arguments. For a definition of
* sample standard deviation, see e.g.
* http://en.wikipedia.org/wiki/Standard_deviation
* @param {...number} var_args Number samples to analyze.
* @return {number} The sample standard deviation of the arguments (0 if fewer
* than two samples were provided, or {@code NaN} if any of the samples is
* not a valid number).
*/
goog.math.standardDeviation = function(var_args) {
return Math.sqrt(goog.math.sampleVariance.apply(null, arguments));
};
/**
* Returns whether the supplied number represents an integer, i.e. that is has
* no fractional component. No range-checking is performed on the number.
* @param {number} num The number to test.
* @return {boolean} Whether {@code num} is an integer.
*/
goog.math.isInt = function(num) {
return isFinite(num) && num % 1 == 0;
};
/**
* Returns whether the supplied number is finite and not NaN.
* @param {number} num The number to test.
* @return {boolean} Whether {@code num} is a finite number.
* @deprecated Use {@link isFinite} instead.
*/
goog.math.isFiniteNumber = function(num) {
return isFinite(num);
};
/**
* @param {number} num The number to test.
* @return {boolean} Whether it is negative zero.
*/
goog.math.isNegativeZero = function(num) {
return num == 0 && 1 / num < 0;
};
/**
* Returns the precise value of floor(log10(num)).
* Simpler implementations didn't work because of floating point rounding
* errors. For example
* <ul>
* <li>Math.floor(Math.log(num) / Math.LN10) is off by one for num == 1e+3.
* <li>Math.floor(Math.log(num) * Math.LOG10E) is off by one for num == 1e+15.
* <li>Math.floor(Math.log10(num)) is off by one for num == 1e+15 - 1.
* </ul>
* @param {number} num A floating point number.
* @return {number} Its logarithm to base 10 rounded down to the nearest
* integer if num > 0. -Infinity if num == 0. NaN if num < 0.
*/
goog.math.log10Floor = function(num) {
if (num > 0) {
var x = Math.round(Math.log(num) * Math.LOG10E);
return x - (parseFloat('1e' + x) > num ? 1 : 0);
}
return num == 0 ? -Infinity : NaN;
};
/**
* A tweaked variant of {@code Math.floor} which tolerates if the passed number
* is infinitesimally smaller than the closest integer. It often happens with
* the results of floating point calculations because of the finite precision
* of the intermediate results. For example {@code Math.floor(Math.log(1000) /
* Math.LN10) == 2}, not 3 as one would expect.
* @param {number} num A number.
* @param {number=} opt_epsilon An infinitesimally small positive number, the
* rounding error to tolerate.
* @return {number} The largest integer less than or equal to {@code num}.
*/
goog.math.safeFloor = function(num, opt_epsilon) {
goog.asserts.assert(!goog.isDef(opt_epsilon) || opt_epsilon > 0);
return Math.floor(num + (opt_epsilon || 2e-15));
};
/**
* A tweaked variant of {@code Math.ceil}. See {@code goog.math.safeFloor} for
* details.
* @param {number} num A number.
* @param {number=} opt_epsilon An infinitesimally small positive number, the
* rounding error to tolerate.
* @return {number} The smallest integer greater than or equal to {@code num}.
*/
goog.math.safeCeil = function(num, opt_epsilon) {
goog.asserts.assert(!goog.isDef(opt_epsilon) || opt_epsilon > 0);
return Math.ceil(num - (opt_epsilon || 2e-15));
};

View File

@ -0,0 +1,477 @@
// Copyright 2006 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A utility class for representing rectangles. Some of these
* functions should be migrated over to non-nullable params.
*/
goog.provide('goog.math.Rect');
goog.require('goog.asserts');
goog.require('goog.math.Box');
goog.require('goog.math.Coordinate');
goog.require('goog.math.IRect');
goog.require('goog.math.Size');
/**
* Class for representing rectangular regions.
* @param {number} x Left.
* @param {number} y Top.
* @param {number} w Width.
* @param {number} h Height.
* @struct
* @constructor
* @implements {goog.math.IRect}
*/
goog.math.Rect = function(x, y, w, h) {
/** @type {number} */
this.left = x;
/** @type {number} */
this.top = y;
/** @type {number} */
this.width = w;
/** @type {number} */
this.height = h;
};
/**
* @return {!goog.math.Rect} A new copy of this Rectangle.
*/
goog.math.Rect.prototype.clone = function() {
return new goog.math.Rect(this.left, this.top, this.width, this.height);
};
/**
* Returns a new Box object with the same position and dimensions as this
* rectangle.
* @return {!goog.math.Box} A new Box representation of this Rectangle.
*/
goog.math.Rect.prototype.toBox = function() {
var right = this.left + this.width;
var bottom = this.top + this.height;
return new goog.math.Box(this.top, right, bottom, this.left);
};
/**
* Creates a new Rect object with the position and size given.
* @param {!goog.math.Coordinate} position The top-left coordinate of the Rect
* @param {!goog.math.Size} size The size of the Rect
* @return {!goog.math.Rect} A new Rect initialized with the given position and
* size.
*/
goog.math.Rect.createFromPositionAndSize = function(position, size) {
return new goog.math.Rect(position.x, position.y, size.width, size.height);
};
/**
* Creates a new Rect object with the same position and dimensions as a given
* Box. Note that this is only the inverse of toBox if left/top are defined.
* @param {goog.math.Box} box A box.
* @return {!goog.math.Rect} A new Rect initialized with the box's position
* and size.
*/
goog.math.Rect.createFromBox = function(box) {
return new goog.math.Rect(
box.left, box.top, box.right - box.left, box.bottom - box.top);
};
if (goog.DEBUG) {
/**
* Returns a nice string representing size and dimensions of rectangle.
* @return {string} In the form (50, 73 - 75w x 25h).
* @override
*/
goog.math.Rect.prototype.toString = function() {
return '(' + this.left + ', ' + this.top + ' - ' + this.width + 'w x ' +
this.height + 'h)';
};
}
/**
* Compares rectangles for equality.
* @param {goog.math.IRect} a A Rectangle.
* @param {goog.math.IRect} b A Rectangle.
* @return {boolean} True iff the rectangles have the same left, top, width,
* and height, or if both are null.
*/
goog.math.Rect.equals = function(a, b) {
if (a == b) {
return true;
}
if (!a || !b) {
return false;
}
return a.left == b.left && a.width == b.width && a.top == b.top &&
a.height == b.height;
};
/**
* Computes the intersection of this rectangle and the rectangle parameter. If
* there is no intersection, returns false and leaves this rectangle as is.
* @param {goog.math.IRect} rect A Rectangle.
* @return {boolean} True iff this rectangle intersects with the parameter.
*/
goog.math.Rect.prototype.intersection = function(rect) {
var x0 = Math.max(this.left, rect.left);
var x1 = Math.min(this.left + this.width, rect.left + rect.width);
if (x0 <= x1) {
var y0 = Math.max(this.top, rect.top);
var y1 = Math.min(this.top + this.height, rect.top + rect.height);
if (y0 <= y1) {
this.left = x0;
this.top = y0;
this.width = x1 - x0;
this.height = y1 - y0;
return true;
}
}
return false;
};
/**
* Returns the intersection of two rectangles. Two rectangles intersect if they
* touch at all, for example, two zero width and height rectangles would
* intersect if they had the same top and left.
* @param {goog.math.IRect} a A Rectangle.
* @param {goog.math.IRect} b A Rectangle.
* @return {goog.math.Rect} A new intersection rect (even if width and height
* are 0), or null if there is no intersection.
*/
goog.math.Rect.intersection = function(a, b) {
// There is no nice way to do intersection via a clone, because any such
// clone might be unnecessary if this function returns null. So, we duplicate
// code from above.
var x0 = Math.max(a.left, b.left);
var x1 = Math.min(a.left + a.width, b.left + b.width);
if (x0 <= x1) {
var y0 = Math.max(a.top, b.top);
var y1 = Math.min(a.top + a.height, b.top + b.height);
if (y0 <= y1) {
return new goog.math.Rect(x0, y0, x1 - x0, y1 - y0);
}
}
return null;
};
/**
* Returns whether two rectangles intersect. Two rectangles intersect if they
* touch at all, for example, two zero width and height rectangles would
* intersect if they had the same top and left.
* @param {goog.math.IRect} a A Rectangle.
* @param {goog.math.IRect} b A Rectangle.
* @return {boolean} Whether a and b intersect.
*/
goog.math.Rect.intersects = function(a, b) {
return (
a.left <= b.left + b.width && b.left <= a.left + a.width &&
a.top <= b.top + b.height && b.top <= a.top + a.height);
};
/**
* Returns whether a rectangle intersects this rectangle.
* @param {goog.math.IRect} rect A rectangle.
* @return {boolean} Whether rect intersects this rectangle.
*/
goog.math.Rect.prototype.intersects = function(rect) {
return goog.math.Rect.intersects(this, rect);
};
/**
* Computes the difference regions between two rectangles. The return value is
* an array of 0 to 4 rectangles defining the remaining regions of the first
* rectangle after the second has been subtracted.
* @param {goog.math.Rect} a A Rectangle.
* @param {goog.math.IRect} b A Rectangle.
* @return {!Array<!goog.math.Rect>} An array with 0 to 4 rectangles which
* together define the difference area of rectangle a minus rectangle b.
*/
goog.math.Rect.difference = function(a, b) {
var intersection = goog.math.Rect.intersection(a, b);
if (!intersection || !intersection.height || !intersection.width) {
return [a.clone()];
}
var result = [];
var top = a.top;
var height = a.height;
var ar = a.left + a.width;
var ab = a.top + a.height;
var br = b.left + b.width;
var bb = b.top + b.height;
// Subtract off any area on top where A extends past B
if (b.top > a.top) {
result.push(new goog.math.Rect(a.left, a.top, a.width, b.top - a.top));
top = b.top;
// If we're moving the top down, we also need to subtract the height diff.
height -= b.top - a.top;
}
// Subtract off any area on bottom where A extends past B
if (bb < ab) {
result.push(new goog.math.Rect(a.left, bb, a.width, ab - bb));
height = bb - top;
}
// Subtract any area on left where A extends past B
if (b.left > a.left) {
result.push(new goog.math.Rect(a.left, top, b.left - a.left, height));
}
// Subtract any area on right where A extends past B
if (br < ar) {
result.push(new goog.math.Rect(br, top, ar - br, height));
}
return result;
};
/**
* Computes the difference regions between this rectangle and {@code rect}. The
* return value is an array of 0 to 4 rectangles defining the remaining regions
* of this rectangle after the other has been subtracted.
* @param {goog.math.IRect} rect A Rectangle.
* @return {!Array<!goog.math.Rect>} An array with 0 to 4 rectangles which
* together define the difference area of rectangle a minus rectangle b.
*/
goog.math.Rect.prototype.difference = function(rect) {
return goog.math.Rect.difference(this, rect);
};
/**
* Expand this rectangle to also include the area of the given rectangle.
* @param {goog.math.IRect} rect The other rectangle.
*/
goog.math.Rect.prototype.boundingRect = function(rect) {
// We compute right and bottom before we change left and top below.
var right = Math.max(this.left + this.width, rect.left + rect.width);
var bottom = Math.max(this.top + this.height, rect.top + rect.height);
this.left = Math.min(this.left, rect.left);
this.top = Math.min(this.top, rect.top);
this.width = right - this.left;
this.height = bottom - this.top;
};
/**
* Returns a new rectangle which completely contains both input rectangles.
* @param {goog.math.IRect} a A rectangle.
* @param {goog.math.IRect} b A rectangle.
* @return {goog.math.Rect} A new bounding rect, or null if either rect is
* null.
*/
goog.math.Rect.boundingRect = function(a, b) {
if (!a || !b) {
return null;
}
var newRect = new goog.math.Rect(a.left, a.top, a.width, a.height);
newRect.boundingRect(b);
return newRect;
};
/**
* Tests whether this rectangle entirely contains another rectangle or
* coordinate.
*
* @param {goog.math.IRect|goog.math.Coordinate} another The rectangle or
* coordinate to test for containment.
* @return {boolean} Whether this rectangle contains given rectangle or
* coordinate.
*/
goog.math.Rect.prototype.contains = function(another) {
if (another instanceof goog.math.Coordinate) {
return another.x >= this.left && another.x <= this.left + this.width &&
another.y >= this.top && another.y <= this.top + this.height;
} else { // (another instanceof goog.math.IRect)
return this.left <= another.left &&
this.left + this.width >= another.left + another.width &&
this.top <= another.top &&
this.top + this.height >= another.top + another.height;
}
};
/**
* @param {!goog.math.Coordinate} point A coordinate.
* @return {number} The squared distance between the point and the closest
* point inside the rectangle. Returns 0 if the point is inside the
* rectangle.
*/
goog.math.Rect.prototype.squaredDistance = function(point) {
var dx = point.x < this.left ?
this.left - point.x :
Math.max(point.x - (this.left + this.width), 0);
var dy = point.y < this.top ? this.top - point.y :
Math.max(point.y - (this.top + this.height), 0);
return dx * dx + dy * dy;
};
/**
* @param {!goog.math.Coordinate} point A coordinate.
* @return {number} The distance between the point and the closest point
* inside the rectangle. Returns 0 if the point is inside the rectangle.
*/
goog.math.Rect.prototype.distance = function(point) {
return Math.sqrt(this.squaredDistance(point));
};
/**
* @return {!goog.math.Size} The size of this rectangle.
*/
goog.math.Rect.prototype.getSize = function() {
return new goog.math.Size(this.width, this.height);
};
/**
* @return {!goog.math.Coordinate} A new coordinate for the top-left corner of
* the rectangle.
*/
goog.math.Rect.prototype.getTopLeft = function() {
return new goog.math.Coordinate(this.left, this.top);
};
/**
* @return {!goog.math.Coordinate} A new coordinate for the center of the
* rectangle.
*/
goog.math.Rect.prototype.getCenter = function() {
return new goog.math.Coordinate(
this.left + this.width / 2, this.top + this.height / 2);
};
/**
* @return {!goog.math.Coordinate} A new coordinate for the bottom-right corner
* of the rectangle.
*/
goog.math.Rect.prototype.getBottomRight = function() {
return new goog.math.Coordinate(
this.left + this.width, this.top + this.height);
};
/**
* Rounds the fields to the next larger integer values.
* @return {!goog.math.Rect} This rectangle with ceil'd fields.
*/
goog.math.Rect.prototype.ceil = function() {
this.left = Math.ceil(this.left);
this.top = Math.ceil(this.top);
this.width = Math.ceil(this.width);
this.height = Math.ceil(this.height);
return this;
};
/**
* Rounds the fields to the next smaller integer values.
* @return {!goog.math.Rect} This rectangle with floored fields.
*/
goog.math.Rect.prototype.floor = function() {
this.left = Math.floor(this.left);
this.top = Math.floor(this.top);
this.width = Math.floor(this.width);
this.height = Math.floor(this.height);
return this;
};
/**
* Rounds the fields to nearest integer values.
* @return {!goog.math.Rect} This rectangle with rounded fields.
*/
goog.math.Rect.prototype.round = function() {
this.left = Math.round(this.left);
this.top = Math.round(this.top);
this.width = Math.round(this.width);
this.height = Math.round(this.height);
return this;
};
/**
* Translates this rectangle by the given offsets. If a
* {@code goog.math.Coordinate} is given, then the left and top values are
* translated by the coordinate's x and y values. Otherwise, top and left are
* translated by {@code tx} and {@code opt_ty} respectively.
* @param {number|goog.math.Coordinate} tx The value to translate left by or the
* the coordinate to translate this rect by.
* @param {number=} opt_ty The value to translate top by.
* @return {!goog.math.Rect} This rectangle after translating.
*/
goog.math.Rect.prototype.translate = function(tx, opt_ty) {
if (tx instanceof goog.math.Coordinate) {
this.left += tx.x;
this.top += tx.y;
} else {
this.left += goog.asserts.assertNumber(tx);
if (goog.isNumber(opt_ty)) {
this.top += opt_ty;
}
}
return this;
};
/**
* Scales this rectangle by the given scale factors. The left and width values
* are scaled by {@code sx} and the top and height values are scaled by
* {@code opt_sy}. If {@code opt_sy} is not given, then all fields are scaled
* by {@code sx}.
* @param {number} sx The scale factor to use for the x dimension.
* @param {number=} opt_sy The scale factor to use for the y dimension.
* @return {!goog.math.Rect} This rectangle after scaling.
*/
goog.math.Rect.prototype.scale = function(sx, opt_sy) {
var sy = goog.isNumber(opt_sy) ? opt_sy : sx;
this.left *= sx;
this.width *= sx;
this.top *= sy;
this.height *= sy;
return this;
};

View File

@ -0,0 +1,227 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A utility class for representing two-dimensional sizes.
* @author brenneman@google.com (Shawn Brenneman)
*/
goog.provide('goog.math.Size');
/**
* Class for representing sizes consisting of a width and height. Undefined
* width and height support is deprecated and results in compiler warning.
* @param {number} width Width.
* @param {number} height Height.
* @struct
* @constructor
*/
goog.math.Size = function(width, height) {
/**
* Width
* @type {number}
*/
this.width = width;
/**
* Height
* @type {number}
*/
this.height = height;
};
/**
* Compares sizes for equality.
* @param {goog.math.Size} a A Size.
* @param {goog.math.Size} b A Size.
* @return {boolean} True iff the sizes have equal widths and equal
* heights, or if both are null.
*/
goog.math.Size.equals = function(a, b) {
if (a == b) {
return true;
}
if (!a || !b) {
return false;
}
return a.width == b.width && a.height == b.height;
};
/**
* @return {!goog.math.Size} A new copy of the Size.
*/
goog.math.Size.prototype.clone = function() {
return new goog.math.Size(this.width, this.height);
};
if (goog.DEBUG) {
/**
* Returns a nice string representing size.
* @return {string} In the form (50 x 73).
* @override
*/
goog.math.Size.prototype.toString = function() {
return '(' + this.width + ' x ' + this.height + ')';
};
}
/**
* @return {number} The longer of the two dimensions in the size.
*/
goog.math.Size.prototype.getLongest = function() {
return Math.max(this.width, this.height);
};
/**
* @return {number} The shorter of the two dimensions in the size.
*/
goog.math.Size.prototype.getShortest = function() {
return Math.min(this.width, this.height);
};
/**
* @return {number} The area of the size (width * height).
*/
goog.math.Size.prototype.area = function() {
return this.width * this.height;
};
/**
* @return {number} The perimeter of the size (width + height) * 2.
*/
goog.math.Size.prototype.perimeter = function() {
return (this.width + this.height) * 2;
};
/**
* @return {number} The ratio of the size's width to its height.
*/
goog.math.Size.prototype.aspectRatio = function() {
return this.width / this.height;
};
/**
* @return {boolean} True if the size has zero area, false if both dimensions
* are non-zero numbers.
*/
goog.math.Size.prototype.isEmpty = function() {
return !this.area();
};
/**
* Clamps the width and height parameters upward to integer values.
* @return {!goog.math.Size} This size with ceil'd components.
*/
goog.math.Size.prototype.ceil = function() {
this.width = Math.ceil(this.width);
this.height = Math.ceil(this.height);
return this;
};
/**
* @param {!goog.math.Size} target The target size.
* @return {boolean} True if this Size is the same size or smaller than the
* target size in both dimensions.
*/
goog.math.Size.prototype.fitsInside = function(target) {
return this.width <= target.width && this.height <= target.height;
};
/**
* Clamps the width and height parameters downward to integer values.
* @return {!goog.math.Size} This size with floored components.
*/
goog.math.Size.prototype.floor = function() {
this.width = Math.floor(this.width);
this.height = Math.floor(this.height);
return this;
};
/**
* Rounds the width and height parameters to integer values.
* @return {!goog.math.Size} This size with rounded components.
*/
goog.math.Size.prototype.round = function() {
this.width = Math.round(this.width);
this.height = Math.round(this.height);
return this;
};
/**
* Scales this size by the given scale factors. The width and height are scaled
* by {@code sx} and {@code opt_sy} respectively. If {@code opt_sy} is not
* given, then {@code sx} is used for both the width and height.
* @param {number} sx The scale factor to use for the width.
* @param {number=} opt_sy The scale factor to use for the height.
* @return {!goog.math.Size} This Size object after scaling.
*/
goog.math.Size.prototype.scale = function(sx, opt_sy) {
var sy = goog.isNumber(opt_sy) ? opt_sy : sx;
this.width *= sx;
this.height *= sy;
return this;
};
/**
* Uniformly scales the size to perfectly cover the dimensions of a given size.
* If the size is already larger than the target, it will be scaled down to the
* minimum size at which it still covers the entire target. The original aspect
* ratio will be preserved.
*
* This function assumes that both Sizes contain strictly positive dimensions.
* @param {!goog.math.Size} target The target size.
* @return {!goog.math.Size} This Size object, after optional scaling.
*/
goog.math.Size.prototype.scaleToCover = function(target) {
var s = this.aspectRatio() <= target.aspectRatio() ?
target.width / this.width :
target.height / this.height;
return this.scale(s);
};
/**
* Uniformly scales the size to fit inside the dimensions of a given size. The
* original aspect ratio will be preserved.
*
* This function assumes that both Sizes contain strictly positive dimensions.
* @param {!goog.math.Size} target The target size.
* @return {!goog.math.Size} This Size object, after optional scaling.
*/
goog.math.Size.prototype.scaleToFit = function(target) {
var s = this.aspectRatio() > target.aspectRatio() ?
target.width / this.width :
target.height / this.height;
return this.scale(s);
};

View File

@ -0,0 +1,751 @@
// Copyright 2006 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Utilities for manipulating objects/maps/hashes.
* @author arv@google.com (Erik Arvidsson)
*/
goog.provide('goog.object');
/**
* Whether two values are not observably distinguishable. This
* correctly detects that 0 is not the same as -0 and two NaNs are
* practically equivalent.
*
* The implementation is as suggested by harmony:egal proposal.
*
* @param {*} v The first value to compare.
* @param {*} v2 The second value to compare.
* @return {boolean} Whether two values are not observably distinguishable.
* @see http://wiki.ecmascript.org/doku.php?id=harmony:egal
*/
goog.object.is = function(v, v2) {
if (v === v2) {
// 0 === -0, but they are not identical.
// We need the cast because the compiler requires that v2 is a
// number (although 1/v2 works with non-number). We cast to ? to
// stop the compiler from type-checking this statement.
return v !== 0 || 1 / v === 1 / /** @type {?} */ (v2);
}
// NaN is non-reflexive: NaN !== NaN, although they are identical.
return v !== v && v2 !== v2;
};
/**
* Calls a function for each element in an object/map/hash.
*
* @param {Object<K,V>} obj The object over which to iterate.
* @param {function(this:T,V,?,Object<K,V>):?} f The function to call
* for every element. This function takes 3 arguments (the value, the
* key and the object) and the return value is ignored.
* @param {T=} opt_obj This is used as the 'this' object within f.
* @template T,K,V
*/
goog.object.forEach = function(obj, f, opt_obj) {
for (var key in obj) {
f.call(/** @type {?} */ (opt_obj), obj[key], key, obj);
}
};
/**
* Calls a function for each element in an object/map/hash. If that call returns
* true, adds the element to a new object.
*
* @param {Object<K,V>} obj The object over which to iterate.
* @param {function(this:T,V,?,Object<K,V>):boolean} f The function to call
* for every element. This
* function takes 3 arguments (the value, the key and the object)
* and should return a boolean. If the return value is true the
* element is added to the result object. If it is false the
* element is not included.
* @param {T=} opt_obj This is used as the 'this' object within f.
* @return {!Object<K,V>} a new object in which only elements that passed the
* test are present.
* @template T,K,V
*/
goog.object.filter = function(obj, f, opt_obj) {
var res = {};
for (var key in obj) {
if (f.call(/** @type {?} */ (opt_obj), obj[key], key, obj)) {
res[key] = obj[key];
}
}
return res;
};
/**
* For every element in an object/map/hash calls a function and inserts the
* result into a new object.
*
* @param {Object<K,V>} obj The object over which to iterate.
* @param {function(this:T,V,?,Object<K,V>):R} f The function to call
* for every element. This function
* takes 3 arguments (the value, the key and the object)
* and should return something. The result will be inserted
* into a new object.
* @param {T=} opt_obj This is used as the 'this' object within f.
* @return {!Object<K,R>} a new object with the results from f.
* @template T,K,V,R
*/
goog.object.map = function(obj, f, opt_obj) {
var res = {};
for (var key in obj) {
res[key] = f.call(/** @type {?} */ (opt_obj), obj[key], key, obj);
}
return res;
};
/**
* Calls a function for each element in an object/map/hash. If any
* call returns true, returns true (without checking the rest). If
* all calls return false, returns false.
*
* @param {Object<K,V>} obj The object to check.
* @param {function(this:T,V,?,Object<K,V>):boolean} f The function to
* call for every element. This function
* takes 3 arguments (the value, the key and the object) and should
* return a boolean.
* @param {T=} opt_obj This is used as the 'this' object within f.
* @return {boolean} true if any element passes the test.
* @template T,K,V
*/
goog.object.some = function(obj, f, opt_obj) {
for (var key in obj) {
if (f.call(/** @type {?} */ (opt_obj), obj[key], key, obj)) {
return true;
}
}
return false;
};
/**
* Calls a function for each element in an object/map/hash. If
* all calls return true, returns true. If any call returns false, returns
* false at this point and does not continue to check the remaining elements.
*
* @param {Object<K,V>} obj The object to check.
* @param {?function(this:T,V,?,Object<K,V>):boolean} f The function to
* call for every element. This function
* takes 3 arguments (the value, the key and the object) and should
* return a boolean.
* @param {T=} opt_obj This is used as the 'this' object within f.
* @return {boolean} false if any element fails the test.
* @template T,K,V
*/
goog.object.every = function(obj, f, opt_obj) {
for (var key in obj) {
if (!f.call(/** @type {?} */ (opt_obj), obj[key], key, obj)) {
return false;
}
}
return true;
};
/**
* Returns the number of key-value pairs in the object map.
*
* @param {Object} obj The object for which to get the number of key-value
* pairs.
* @return {number} The number of key-value pairs in the object map.
*/
goog.object.getCount = function(obj) {
var rv = 0;
for (var key in obj) {
rv++;
}
return rv;
};
/**
* Returns one key from the object map, if any exists.
* For map literals the returned key will be the first one in most of the
* browsers (a know exception is Konqueror).
*
* @param {Object} obj The object to pick a key from.
* @return {string|undefined} The key or undefined if the object is empty.
*/
goog.object.getAnyKey = function(obj) {
for (var key in obj) {
return key;
}
};
/**
* Returns one value from the object map, if any exists.
* For map literals the returned value will be the first one in most of the
* browsers (a know exception is Konqueror).
*
* @param {Object<K,V>} obj The object to pick a value from.
* @return {V|undefined} The value or undefined if the object is empty.
* @template K,V
*/
goog.object.getAnyValue = function(obj) {
for (var key in obj) {
return obj[key];
}
};
/**
* Whether the object/hash/map contains the given object as a value.
* An alias for goog.object.containsValue(obj, val).
*
* @param {Object<K,V>} obj The object in which to look for val.
* @param {V} val The object for which to check.
* @return {boolean} true if val is present.
* @template K,V
*/
goog.object.contains = function(obj, val) {
return goog.object.containsValue(obj, val);
};
/**
* Returns the values of the object/map/hash.
*
* @param {Object<K,V>} obj The object from which to get the values.
* @return {!Array<V>} The values in the object/map/hash.
* @template K,V
*/
goog.object.getValues = function(obj) {
var res = [];
var i = 0;
for (var key in obj) {
res[i++] = obj[key];
}
return res;
};
/**
* Returns the keys of the object/map/hash.
*
* @param {Object} obj The object from which to get the keys.
* @return {!Array<string>} Array of property keys.
*/
goog.object.getKeys = function(obj) {
var res = [];
var i = 0;
for (var key in obj) {
res[i++] = key;
}
return res;
};
/**
* Get a value from an object multiple levels deep. This is useful for
* pulling values from deeply nested objects, such as JSON responses.
* Example usage: getValueByKeys(jsonObj, 'foo', 'entries', 3)
*
* @param {!Object} obj An object to get the value from. Can be array-like.
* @param {...(string|number|!IArrayLike<number|string>)}
* var_args A number of keys
* (as strings, or numbers, for array-like objects). Can also be
* specified as a single array of keys.
* @return {*} The resulting value. If, at any point, the value for a key
* is undefined, returns undefined.
*/
goog.object.getValueByKeys = function(obj, var_args) {
var isArrayLike = goog.isArrayLike(var_args);
var keys = isArrayLike ? var_args : arguments;
// Start with the 2nd parameter for the variable parameters syntax.
for (var i = isArrayLike ? 0 : 1; i < keys.length; i++) {
obj = obj[keys[i]];
if (!goog.isDef(obj)) {
break;
}
}
return obj;
};
/**
* Whether the object/map/hash contains the given key.
*
* @param {Object} obj The object in which to look for key.
* @param {?} key The key for which to check.
* @return {boolean} true If the map contains the key.
*/
goog.object.containsKey = function(obj, key) {
return obj !== null && key in obj;
};
/**
* Whether the object/map/hash contains the given value. This is O(n).
*
* @param {Object<K,V>} obj The object in which to look for val.
* @param {V} val The value for which to check.
* @return {boolean} true If the map contains the value.
* @template K,V
*/
goog.object.containsValue = function(obj, val) {
for (var key in obj) {
if (obj[key] == val) {
return true;
}
}
return false;
};
/**
* Searches an object for an element that satisfies the given condition and
* returns its key.
* @param {Object<K,V>} obj The object to search in.
* @param {function(this:T,V,string,Object<K,V>):boolean} f The
* function to call for every element. Takes 3 arguments (the value,
* the key and the object) and should return a boolean.
* @param {T=} opt_this An optional "this" context for the function.
* @return {string|undefined} The key of an element for which the function
* returns true or undefined if no such element is found.
* @template T,K,V
*/
goog.object.findKey = function(obj, f, opt_this) {
for (var key in obj) {
if (f.call(/** @type {?} */ (opt_this), obj[key], key, obj)) {
return key;
}
}
return undefined;
};
/**
* Searches an object for an element that satisfies the given condition and
* returns its value.
* @param {Object<K,V>} obj The object to search in.
* @param {function(this:T,V,string,Object<K,V>):boolean} f The function
* to call for every element. Takes 3 arguments (the value, the key
* and the object) and should return a boolean.
* @param {T=} opt_this An optional "this" context for the function.
* @return {V} The value of an element for which the function returns true or
* undefined if no such element is found.
* @template T,K,V
*/
goog.object.findValue = function(obj, f, opt_this) {
var key = goog.object.findKey(obj, f, opt_this);
return key && obj[key];
};
/**
* Whether the object/map/hash is empty.
*
* @param {Object} obj The object to test.
* @return {boolean} true if obj is empty.
*/
goog.object.isEmpty = function(obj) {
for (var key in obj) {
return false;
}
return true;
};
/**
* Removes all key value pairs from the object/map/hash.
*
* @param {Object} obj The object to clear.
*/
goog.object.clear = function(obj) {
for (var i in obj) {
delete obj[i];
}
};
/**
* Removes a key-value pair based on the key.
*
* @param {Object} obj The object from which to remove the key.
* @param {?} key The key to remove.
* @return {boolean} Whether an element was removed.
*/
goog.object.remove = function(obj, key) {
var rv;
if (rv = key in /** @type {!Object} */ (obj)) {
delete obj[key];
}
return rv;
};
/**
* Adds a key-value pair to the object. Throws an exception if the key is
* already in use. Use set if you want to change an existing pair.
*
* @param {Object<K,V>} obj The object to which to add the key-value pair.
* @param {string} key The key to add.
* @param {V} val The value to add.
* @template K,V
*/
goog.object.add = function(obj, key, val) {
if (obj !== null && key in obj) {
throw Error('The object already contains the key "' + key + '"');
}
goog.object.set(obj, key, val);
};
/**
* Returns the value for the given key.
*
* @param {Object<K,V>} obj The object from which to get the value.
* @param {string} key The key for which to get the value.
* @param {R=} opt_val The value to return if no item is found for the given
* key (default is undefined).
* @return {V|R|undefined} The value for the given key.
* @template K,V,R
*/
goog.object.get = function(obj, key, opt_val) {
if (obj !== null && key in obj) {
return obj[key];
}
return opt_val;
};
/**
* Adds a key-value pair to the object/map/hash.
*
* @param {Object<K,V>} obj The object to which to add the key-value pair.
* @param {string} key The key to add.
* @param {V} value The value to add.
* @template K,V
*/
goog.object.set = function(obj, key, value) {
obj[key] = value;
};
/**
* Adds a key-value pair to the object/map/hash if it doesn't exist yet.
*
* @param {Object<K,V>} obj The object to which to add the key-value pair.
* @param {string} key The key to add.
* @param {V} value The value to add if the key wasn't present.
* @return {V} The value of the entry at the end of the function.
* @template K,V
*/
goog.object.setIfUndefined = function(obj, key, value) {
return key in /** @type {!Object} */ (obj) ? obj[key] : (obj[key] = value);
};
/**
* Sets a key and value to an object if the key is not set. The value will be
* the return value of the given function. If the key already exists, the
* object will not be changed and the function will not be called (the function
* will be lazily evaluated -- only called if necessary).
*
* This function is particularly useful for use with a map used a as a cache.
*
* @param {!Object<K,V>} obj The object to which to add the key-value pair.
* @param {string} key The key to add.
* @param {function():V} f The value to add if the key wasn't present.
* @return {V} The value of the entry at the end of the function.
* @template K,V
*/
goog.object.setWithReturnValueIfNotSet = function(obj, key, f) {
if (key in obj) {
return obj[key];
}
var val = f();
obj[key] = val;
return val;
};
/**
* Compares two objects for equality using === on the values.
*
* @param {!Object<K,V>} a
* @param {!Object<K,V>} b
* @return {boolean}
* @template K,V
*/
goog.object.equals = function(a, b) {
for (var k in a) {
if (!(k in b) || a[k] !== b[k]) {
return false;
}
}
for (var k in b) {
if (!(k in a)) {
return false;
}
}
return true;
};
/**
* Returns a shallow clone of the object.
*
* @param {Object<K,V>} obj Object to clone.
* @return {!Object<K,V>} Clone of the input object.
* @template K,V
*/
goog.object.clone = function(obj) {
// We cannot use the prototype trick because a lot of methods depend on where
// the actual key is set.
var res = {};
for (var key in obj) {
res[key] = obj[key];
}
return res;
// We could also use goog.mixin but I wanted this to be independent from that.
};
/**
* Clones a value. The input may be an Object, Array, or basic type. Objects and
* arrays will be cloned recursively.
*
* WARNINGS:
* <code>goog.object.unsafeClone</code> does not detect reference loops. Objects
* that refer to themselves will cause infinite recursion.
*
* <code>goog.object.unsafeClone</code> is unaware of unique identifiers, and
* copies UIDs created by <code>getUid</code> into cloned results.
*
* @param {T} obj The value to clone.
* @return {T} A clone of the input value.
* @template T
*/
goog.object.unsafeClone = function(obj) {
var type = goog.typeOf(obj);
if (type == 'object' || type == 'array') {
if (goog.isFunction(obj.clone)) {
return obj.clone();
}
var clone = type == 'array' ? [] : {};
for (var key in obj) {
clone[key] = goog.object.unsafeClone(obj[key]);
}
return clone;
}
return obj;
};
/**
* Returns a new object in which all the keys and values are interchanged
* (keys become values and values become keys). If multiple keys map to the
* same value, the chosen transposed value is implementation-dependent.
*
* @param {Object} obj The object to transpose.
* @return {!Object} The transposed object.
*/
goog.object.transpose = function(obj) {
var transposed = {};
for (var key in obj) {
transposed[obj[key]] = key;
}
return transposed;
};
/**
* The names of the fields that are defined on Object.prototype.
* @type {Array<string>}
* @private
*/
goog.object.PROTOTYPE_FIELDS_ = [
'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',
'toLocaleString', 'toString', 'valueOf'
];
/**
* Extends an object with another object.
* This operates 'in-place'; it does not create a new Object.
*
* Example:
* var o = {};
* goog.object.extend(o, {a: 0, b: 1});
* o; // {a: 0, b: 1}
* goog.object.extend(o, {b: 2, c: 3});
* o; // {a: 0, b: 2, c: 3}
*
* @param {Object} target The object to modify. Existing properties will be
* overwritten if they are also present in one of the objects in
* {@code var_args}.
* @param {...Object} var_args The objects from which values will be copied.
*/
goog.object.extend = function(target, var_args) {
var key, source;
for (var i = 1; i < arguments.length; i++) {
source = arguments[i];
for (key in source) {
target[key] = source[key];
}
// For IE the for-in-loop does not contain any properties that are not
// enumerable on the prototype object (for example isPrototypeOf from
// Object.prototype) and it will also not include 'replace' on objects that
// extend String and change 'replace' (not that it is common for anyone to
// extend anything except Object).
for (var j = 0; j < goog.object.PROTOTYPE_FIELDS_.length; j++) {
key = goog.object.PROTOTYPE_FIELDS_[j];
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
};
/**
* Creates a new object built from the key-value pairs provided as arguments.
* @param {...*} var_args If only one argument is provided and it is an array
* then this is used as the arguments, otherwise even arguments are used as
* the property names and odd arguments are used as the property values.
* @return {!Object} The new object.
* @throws {Error} If there are uneven number of arguments or there is only one
* non array argument.
*/
goog.object.create = function(var_args) {
var argLength = arguments.length;
if (argLength == 1 && goog.isArray(arguments[0])) {
return goog.object.create.apply(null, arguments[0]);
}
if (argLength % 2) {
throw Error('Uneven number of arguments');
}
var rv = {};
for (var i = 0; i < argLength; i += 2) {
rv[arguments[i]] = arguments[i + 1];
}
return rv;
};
/**
* Creates a new object where the property names come from the arguments but
* the value is always set to true
* @param {...*} var_args If only one argument is provided and it is an array
* then this is used as the arguments, otherwise the arguments are used
* as the property names.
* @return {!Object} The new object.
*/
goog.object.createSet = function(var_args) {
var argLength = arguments.length;
if (argLength == 1 && goog.isArray(arguments[0])) {
return goog.object.createSet.apply(null, arguments[0]);
}
var rv = {};
for (var i = 0; i < argLength; i++) {
rv[arguments[i]] = true;
}
return rv;
};
/**
* Creates an immutable view of the underlying object, if the browser
* supports immutable objects.
*
* In default mode, writes to this view will fail silently. In strict mode,
* they will throw an error.
*
* @param {!Object<K,V>} obj An object.
* @return {!Object<K,V>} An immutable view of that object, or the
* original object if this browser does not support immutables.
* @template K,V
*/
goog.object.createImmutableView = function(obj) {
var result = obj;
if (Object.isFrozen && !Object.isFrozen(obj)) {
result = Object.create(obj);
Object.freeze(result);
}
return result;
};
/**
* @param {!Object} obj An object.
* @return {boolean} Whether this is an immutable view of the object.
*/
goog.object.isImmutableView = function(obj) {
return !!Object.isFrozen && Object.isFrozen(obj);
};
/**
* Get all properties names on a given Object regardless of enumerability.
*
* <p> If the browser does not support {@code Object.getOwnPropertyNames} nor
* {@code Object.getPrototypeOf} then this is equivalent to using {@code
* goog.object.getKeys}
*
* @param {?Object} obj The object to get the properties of.
* @param {boolean=} opt_includeObjectPrototype Whether properties defined on
* {@code Object.prototype} should be included in the result.
* @param {boolean=} opt_includeFunctionPrototype Whether properties defined on
* {@code Function.prototype} should be included in the result.
* @return {!Array<string>}
* @public
*/
goog.object.getAllPropertyNames = function(
obj, opt_includeObjectPrototype, opt_includeFunctionPrototype) {
if (!obj) {
return [];
}
// Naively use a for..in loop to get the property names if the browser doesn't
// support any other APIs for getting it.
if (!Object.getOwnPropertyNames || !Object.getPrototypeOf) {
return goog.object.getKeys(obj);
}
var visitedSet = {};
// Traverse the prototype chain and add all properties to the visited set.
var proto = obj;
while (proto &&
(proto !== Object.prototype || !!opt_includeObjectPrototype) &&
(proto !== Function.prototype || !!opt_includeFunctionPrototype)) {
var names = Object.getOwnPropertyNames(proto);
for (var i = 0; i < names.length; i++) {
visitedSet[names[i]] = true;
}
proto = Object.getPrototypeOf(proto);
}
return goog.object.getKeys(visitedSet);
};

View File

@ -0,0 +1,138 @@
// Copyright 2009 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Useful compiler idioms.
*
* @author johnlenz@google.com (John Lenz)
*/
goog.provide('goog.reflect');
/**
* Syntax for object literal casts.
* @see http://go/jscompiler-renaming
* @see https://goo.gl/CRs09P
*
* Use this if you have an object literal whose keys need to have the same names
* as the properties of some class even after they are renamed by the compiler.
*
* @param {!Function} type Type to cast to.
* @param {Object} object Object literal to cast.
* @return {Object} The object literal.
*/
goog.reflect.object = function(type, object) {
return object;
};
/**
* Syntax for renaming property strings.
* @see http://go/jscompiler-renaming
* @see https://goo.gl/CRs09P
*
* Use this if you have an need to access a property as a string, but want
* to also have the property renamed by the compiler. In contrast to
* goog.reflect.object, this method takes an instance of an object.
*
* Properties must be simple names (not qualified names).
*
* @param {string} prop Name of the property
* @param {!Object} object Instance of the object whose type will be used
* for renaming
* @return {string} The renamed property.
*/
goog.reflect.objectProperty = function(prop, object) {
return prop;
};
/**
* To assert to the compiler that an operation is needed when it would
* otherwise be stripped. For example:
* <code>
* // Force a layout
* goog.reflect.sinkValue(dialog.offsetHeight);
* </code>
* @param {T} x
* @return {T}
* @template T
*/
goog.reflect.sinkValue = function(x) {
goog.reflect.sinkValue[' '](x);
return x;
};
/**
* The compiler should optimize this function away iff no one ever uses
* goog.reflect.sinkValue.
*/
goog.reflect.sinkValue[' '] = goog.nullFunction;
/**
* Check if a property can be accessed without throwing an exception.
* @param {Object} obj The owner of the property.
* @param {string} prop The property name.
* @return {boolean} Whether the property is accessible. Will also return true
* if obj is null.
*/
goog.reflect.canAccessProperty = function(obj, prop) {
try {
goog.reflect.sinkValue(obj[prop]);
return true;
} catch (e) {
}
return false;
};
/**
* Retrieves a value from a cache given a key. The compiler provides special
* consideration for this call such that it is generally considered side-effect
* free. However, if the {@code opt_keyFn} or {@code valueFn} have side-effects
* then the entire call is considered to have side-effects.
*
* Conventionally storing the value on the cache would be considered a
* side-effect and preclude unused calls from being pruned, ie. even if
* the value was never used, it would still always be stored in the cache.
*
* Providing a side-effect free {@code valueFn} and {@code opt_keyFn}
* allows unused calls to {@code goog.reflect.cache} to be pruned.
*
* @param {!Object<K, V>} cacheObj The object that contains the cached values.
* @param {?} key The key to lookup in the cache. If it is not string or number
* then a {@code opt_keyFn} should be provided. The key is also used as the
* parameter to the {@code valueFn}.
* @param {function(?):V} valueFn The value provider to use to calculate the
* value to store in the cache. This function should be side-effect free
* to take advantage of the optimization.
* @param {function(?):K=} opt_keyFn The key provider to determine the cache
* map key. This should be used if the given key is not a string or number.
* If not provided then the given key is used. This function should be
* side-effect free to take advantage of the optimization.
* @return {V} The cached or calculated value.
* @template K
* @template V
*/
goog.reflect.cache = function(cacheObj, key, valueFn, opt_keyFn) {
var storedKey = opt_keyFn ? opt_keyFn(key) : key;
if (Object.prototype.hasOwnProperty.call(cacheObj, storedKey)) {
return cacheObj[storedKey];
}
return (cacheObj[storedKey] = valueFn(key));
};

View File

@ -0,0 +1,186 @@
// Copyright 2013 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
goog.provide('goog.string.Const');
goog.require('goog.asserts');
goog.require('goog.string.TypedString');
/**
* Wrapper for compile-time-constant strings.
*
* Const is a wrapper for strings that can only be created from program
* constants (i.e., string literals). This property relies on a custom Closure
* compiler check that {@code goog.string.Const.from} is only invoked on
* compile-time-constant expressions.
*
* Const is useful in APIs whose correct and secure use requires that certain
* arguments are not attacker controlled: Compile-time constants are inherently
* under the control of the application and not under control of external
* attackers, and hence are safe to use in such contexts.
*
* Instances of this type must be created via its factory method
* {@code goog.string.Const.from} and not by invoking its constructor. The
* constructor intentionally takes no parameters and the type is immutable;
* hence only a default instance corresponding to the empty string can be
* obtained via constructor invocation.
*
* @see goog.string.Const#from
* @constructor
* @final
* @struct
* @implements {goog.string.TypedString}
*/
goog.string.Const = function() {
/**
* The wrapped value of this Const object. The field has a purposely ugly
* name to make (non-compiled) code that attempts to directly access this
* field stand out.
* @private {string}
*/
this.stringConstValueWithSecurityContract__googStringSecurityPrivate_ = '';
/**
* A type marker used to implement additional run-time type checking.
* @see goog.string.Const#unwrap
* @const {!Object}
* @private
*/
this.STRING_CONST_TYPE_MARKER__GOOG_STRING_SECURITY_PRIVATE_ =
goog.string.Const.TYPE_MARKER_;
};
/**
* @override
* @const
*/
goog.string.Const.prototype.implementsGoogStringTypedString = true;
/**
* Returns this Const's value a string.
*
* IMPORTANT: In code where it is security-relevant that an object's type is
* indeed {@code goog.string.Const}, use {@code goog.string.Const.unwrap}
* instead of this method.
*
* @see goog.string.Const#unwrap
* @override
*/
goog.string.Const.prototype.getTypedStringValue = function() {
return this.stringConstValueWithSecurityContract__googStringSecurityPrivate_;
};
/**
* Returns a debug-string representation of this value.
*
* To obtain the actual string value wrapped inside an object of this type,
* use {@code goog.string.Const.unwrap}.
*
* @see goog.string.Const#unwrap
* @override
*/
goog.string.Const.prototype.toString = function() {
return 'Const{' +
this.stringConstValueWithSecurityContract__googStringSecurityPrivate_ +
'}';
};
/**
* Performs a runtime check that the provided object is indeed an instance
* of {@code goog.string.Const}, and returns its value.
* @param {!goog.string.Const} stringConst The object to extract from.
* @return {string} The Const object's contained string, unless the run-time
* type check fails. In that case, {@code unwrap} returns an innocuous
* string, or, if assertions are enabled, throws
* {@code goog.asserts.AssertionError}.
*/
goog.string.Const.unwrap = function(stringConst) {
// Perform additional run-time type-checking to ensure that stringConst is
// indeed an instance of the expected type. This provides some additional
// protection against security bugs due to application code that disables type
// checks.
if (stringConst instanceof goog.string.Const &&
stringConst.constructor === goog.string.Const &&
stringConst.STRING_CONST_TYPE_MARKER__GOOG_STRING_SECURITY_PRIVATE_ ===
goog.string.Const.TYPE_MARKER_) {
return stringConst
.stringConstValueWithSecurityContract__googStringSecurityPrivate_;
} else {
goog.asserts.fail(
'expected object of type Const, got \'' + stringConst + '\'');
return 'type_error:Const';
}
};
/**
* Creates a Const object from a compile-time constant string.
*
* It is illegal to invoke this function on an expression whose
* compile-time-contant value cannot be determined by the Closure compiler.
*
* Correct invocations include,
* <pre>
* var s = goog.string.Const.from('hello');
* var t = goog.string.Const.from('hello' + 'world');
* </pre>
*
* In contrast, the following are illegal:
* <pre>
* var s = goog.string.Const.from(getHello());
* var t = goog.string.Const.from('hello' + world);
* </pre>
*
* @param {string} s A constant string from which to create a Const.
* @return {!goog.string.Const} A Const object initialized to stringConst.
*/
goog.string.Const.from = function(s) {
return goog.string.Const.create__googStringSecurityPrivate_(s);
};
/**
* Type marker for the Const type, used to implement additional run-time
* type checking.
* @const {!Object}
* @private
*/
goog.string.Const.TYPE_MARKER_ = {};
/**
* Utility method to create Const instances.
* @param {string} s The string to initialize the Const object with.
* @return {!goog.string.Const} The initialized Const object.
* @private
*/
goog.string.Const.create__googStringSecurityPrivate_ = function(s) {
var stringConst = new goog.string.Const();
stringConst.stringConstValueWithSecurityContract__googStringSecurityPrivate_ =
s;
return stringConst;
};
/**
* A Const instance wrapping the empty string.
* @const {!goog.string.Const}
*/
goog.string.Const.EMPTY = goog.string.Const.from('');

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,103 @@
// Copyright 2006 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Utility for fast string concatenation.
*/
goog.provide('goog.string.StringBuffer');
/**
* Utility class to facilitate string concatenation.
*
* @param {*=} opt_a1 Optional first initial item to append.
* @param {...*} var_args Other initial items to
* append, e.g., new goog.string.StringBuffer('foo', 'bar').
* @constructor
*/
goog.string.StringBuffer = function(opt_a1, var_args) {
if (opt_a1 != null) {
this.append.apply(this, arguments);
}
};
/**
* Internal buffer for the string to be concatenated.
* @type {string}
* @private
*/
goog.string.StringBuffer.prototype.buffer_ = '';
/**
* Sets the contents of the string buffer object, replacing what's currently
* there.
*
* @param {*} s String to set.
*/
goog.string.StringBuffer.prototype.set = function(s) {
this.buffer_ = '' + s;
};
/**
* Appends one or more items to the buffer.
*
* Calling this with null, undefined, or empty arguments is an error.
*
* @param {*} a1 Required first string.
* @param {*=} opt_a2 Optional second string.
* @param {...?} var_args Other items to append,
* e.g., sb.append('foo', 'bar', 'baz').
* @return {!goog.string.StringBuffer} This same StringBuffer object.
* @suppress {duplicate}
*/
goog.string.StringBuffer.prototype.append = function(a1, opt_a2, var_args) {
// Use a1 directly to avoid arguments instantiation for single-arg case.
this.buffer_ += String(a1);
if (opt_a2 != null) { // second argument is undefined (null == undefined)
for (var i = 1; i < arguments.length; i++) {
this.buffer_ += arguments[i];
}
}
return this;
};
/**
* Clears the internal buffer.
*/
goog.string.StringBuffer.prototype.clear = function() {
this.buffer_ = '';
};
/**
* @return {number} the length of the current contents of the buffer.
*/
goog.string.StringBuffer.prototype.getLength = function() {
return this.buffer_.length;
};
/**
* @return {string} The concatenated string.
* @override
*/
goog.string.StringBuffer.prototype.toString = function() {
return this.buffer_;
};

View File

@ -0,0 +1,48 @@
// Copyright 2013 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
goog.provide('goog.string.TypedString');
/**
* Wrapper for strings that conform to a data type or language.
*
* Implementations of this interface are wrappers for strings, and typically
* associate a type contract with the wrapped string. Concrete implementations
* of this interface may choose to implement additional run-time type checking,
* see for example {@code goog.html.SafeHtml}. If available, client code that
* needs to ensure type membership of an object should use the type's function
* to assert type membership, such as {@code goog.html.SafeHtml.unwrap}.
* @interface
*/
goog.string.TypedString = function() {};
/**
* Interface marker of the TypedString interface.
*
* This property can be used to determine at runtime whether or not an object
* implements this interface. All implementations of this interface set this
* property to {@code true}.
* @type {boolean}
*/
goog.string.TypedString.prototype.implementsGoogStringTypedString;
/**
* Retrieves this wrapped string's value.
* @return {string} The wrapped string's value.
*/
goog.string.TypedString.prototype.getTypedStringValue;

View File

@ -0,0 +1,458 @@
// Copyright 2006 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Datastructure: Hash Map.
*
* @author arv@google.com (Erik Arvidsson)
*
* This file contains an implementation of a Map structure. It implements a lot
* of the methods used in goog.structs so those functions work on hashes. This
* is best suited for complex key types. For simple keys such as numbers and
* strings consider using the lighter-weight utilities in goog.object.
*/
goog.provide('goog.structs.Map');
goog.require('goog.iter.Iterator');
goog.require('goog.iter.StopIteration');
goog.require('goog.object');
/**
* Class for Hash Map datastructure.
* @param {*=} opt_map Map or Object to initialize the map with.
* @param {...*} var_args If 2 or more arguments are present then they
* will be used as key-value pairs.
* @constructor
* @template K, V
* @deprecated This type is misleading: use ES6 Map instead.
*/
goog.structs.Map = function(opt_map, var_args) {
/**
* Underlying JS object used to implement the map.
* @private {!Object}
*/
this.map_ = {};
/**
* An array of keys. This is necessary for two reasons:
* 1. Iterating the keys using for (var key in this.map_) allocates an
* object for every key in IE which is really bad for IE6 GC perf.
* 2. Without a side data structure, we would need to escape all the keys
* as that would be the only way we could tell during iteration if the
* key was an internal key or a property of the object.
*
* This array can contain deleted keys so it's necessary to check the map
* as well to see if the key is still in the map (this doesn't require a
* memory allocation in IE).
* @private {!Array<string>}
*/
this.keys_ = [];
/**
* The number of key value pairs in the map.
* @private {number}
*/
this.count_ = 0;
/**
* Version used to detect changes while iterating.
* @private {number}
*/
this.version_ = 0;
var argLength = arguments.length;
if (argLength > 1) {
if (argLength % 2) {
throw Error('Uneven number of arguments');
}
for (var i = 0; i < argLength; i += 2) {
this.set(arguments[i], arguments[i + 1]);
}
} else if (opt_map) {
this.addAll(/** @type {Object} */ (opt_map));
}
};
/**
* @return {number} The number of key-value pairs in the map.
*/
goog.structs.Map.prototype.getCount = function() {
return this.count_;
};
/**
* Returns the values of the map.
* @return {!Array<V>} The values in the map.
*/
goog.structs.Map.prototype.getValues = function() {
this.cleanupKeysArray_();
var rv = [];
for (var i = 0; i < this.keys_.length; i++) {
var key = this.keys_[i];
rv.push(this.map_[key]);
}
return rv;
};
/**
* Returns the keys of the map.
* @return {!Array<string>} Array of string values.
*/
goog.structs.Map.prototype.getKeys = function() {
this.cleanupKeysArray_();
return /** @type {!Array<string>} */ (this.keys_.concat());
};
/**
* Whether the map contains the given key.
* @param {*} key The key to check for.
* @return {boolean} Whether the map contains the key.
*/
goog.structs.Map.prototype.containsKey = function(key) {
return goog.structs.Map.hasKey_(this.map_, key);
};
/**
* Whether the map contains the given value. This is O(n).
* @param {V} val The value to check for.
* @return {boolean} Whether the map contains the value.
*/
goog.structs.Map.prototype.containsValue = function(val) {
for (var i = 0; i < this.keys_.length; i++) {
var key = this.keys_[i];
if (goog.structs.Map.hasKey_(this.map_, key) && this.map_[key] == val) {
return true;
}
}
return false;
};
/**
* Whether this map is equal to the argument map.
* @param {goog.structs.Map} otherMap The map against which to test equality.
* @param {function(V, V): boolean=} opt_equalityFn Optional equality function
* to test equality of values. If not specified, this will test whether
* the values contained in each map are identical objects.
* @return {boolean} Whether the maps are equal.
*/
goog.structs.Map.prototype.equals = function(otherMap, opt_equalityFn) {
if (this === otherMap) {
return true;
}
if (this.count_ != otherMap.getCount()) {
return false;
}
var equalityFn = opt_equalityFn || goog.structs.Map.defaultEquals;
this.cleanupKeysArray_();
for (var key, i = 0; key = this.keys_[i]; i++) {
if (!equalityFn(this.get(key), otherMap.get(key))) {
return false;
}
}
return true;
};
/**
* Default equality test for values.
* @param {*} a The first value.
* @param {*} b The second value.
* @return {boolean} Whether a and b reference the same object.
*/
goog.structs.Map.defaultEquals = function(a, b) {
return a === b;
};
/**
* @return {boolean} Whether the map is empty.
*/
goog.structs.Map.prototype.isEmpty = function() {
return this.count_ == 0;
};
/**
* Removes all key-value pairs from the map.
*/
goog.structs.Map.prototype.clear = function() {
this.map_ = {};
this.keys_.length = 0;
this.count_ = 0;
this.version_ = 0;
};
/**
* Removes a key-value pair based on the key. This is O(logN) amortized due to
* updating the keys array whenever the count becomes half the size of the keys
* in the keys array.
* @param {*} key The key to remove.
* @return {boolean} Whether object was removed.
*/
goog.structs.Map.prototype.remove = function(key) {
if (goog.structs.Map.hasKey_(this.map_, key)) {
delete this.map_[key];
this.count_--;
this.version_++;
// clean up the keys array if the threshold is hit
if (this.keys_.length > 2 * this.count_) {
this.cleanupKeysArray_();
}
return true;
}
return false;
};
/**
* Cleans up the temp keys array by removing entries that are no longer in the
* map.
* @private
*/
goog.structs.Map.prototype.cleanupKeysArray_ = function() {
if (this.count_ != this.keys_.length) {
// First remove keys that are no longer in the map.
var srcIndex = 0;
var destIndex = 0;
while (srcIndex < this.keys_.length) {
var key = this.keys_[srcIndex];
if (goog.structs.Map.hasKey_(this.map_, key)) {
this.keys_[destIndex++] = key;
}
srcIndex++;
}
this.keys_.length = destIndex;
}
if (this.count_ != this.keys_.length) {
// If the count still isn't correct, that means we have duplicates. This can
// happen when the same key is added and removed multiple times. Now we have
// to allocate one extra Object to remove the duplicates. This could have
// been done in the first pass, but in the common case, we can avoid
// allocating an extra object by only doing this when necessary.
var seen = {};
var srcIndex = 0;
var destIndex = 0;
while (srcIndex < this.keys_.length) {
var key = this.keys_[srcIndex];
if (!(goog.structs.Map.hasKey_(seen, key))) {
this.keys_[destIndex++] = key;
seen[key] = 1;
}
srcIndex++;
}
this.keys_.length = destIndex;
}
};
/**
* Returns the value for the given key. If the key is not found and the default
* value is not given this will return {@code undefined}.
* @param {*} key The key to get the value for.
* @param {DEFAULT=} opt_val The value to return if no item is found for the
* given key, defaults to undefined.
* @return {V|DEFAULT} The value for the given key.
* @template DEFAULT
*/
goog.structs.Map.prototype.get = function(key, opt_val) {
if (goog.structs.Map.hasKey_(this.map_, key)) {
return this.map_[key];
}
return opt_val;
};
/**
* Adds a key-value pair to the map.
* @param {*} key The key.
* @param {V} value The value to add.
* @return {*} Some subclasses return a value.
*/
goog.structs.Map.prototype.set = function(key, value) {
if (!(goog.structs.Map.hasKey_(this.map_, key))) {
this.count_++;
// TODO(johnlenz): This class lies, it claims to return an array of string
// keys, but instead returns the original object used.
this.keys_.push(/** @type {?} */ (key));
// Only change the version if we add a new key.
this.version_++;
}
this.map_[key] = value;
};
/**
* Adds multiple key-value pairs from another goog.structs.Map or Object.
* @param {Object} map Object containing the data to add.
*/
goog.structs.Map.prototype.addAll = function(map) {
var keys, values;
if (map instanceof goog.structs.Map) {
keys = map.getKeys();
values = map.getValues();
} else {
keys = goog.object.getKeys(map);
values = goog.object.getValues(map);
}
// we could use goog.array.forEach here but I don't want to introduce that
// dependency just for this.
for (var i = 0; i < keys.length; i++) {
this.set(keys[i], values[i]);
}
};
/**
* Calls the given function on each entry in the map.
* @param {function(this:T, V, K, goog.structs.Map<K,V>)} f
* @param {T=} opt_obj The value of "this" inside f.
* @template T
*/
goog.structs.Map.prototype.forEach = function(f, opt_obj) {
var keys = this.getKeys();
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var value = this.get(key);
f.call(opt_obj, value, key, this);
}
};
/**
* Clones a map and returns a new map.
* @return {!goog.structs.Map} A new map with the same key-value pairs.
*/
goog.structs.Map.prototype.clone = function() {
return new goog.structs.Map(this);
};
/**
* Returns a new map in which all the keys and values are interchanged
* (keys become values and values become keys). If multiple keys map to the
* same value, the chosen transposed value is implementation-dependent.
*
* It acts very similarly to {goog.object.transpose(Object)}.
*
* @return {!goog.structs.Map} The transposed map.
*/
goog.structs.Map.prototype.transpose = function() {
var transposed = new goog.structs.Map();
for (var i = 0; i < this.keys_.length; i++) {
var key = this.keys_[i];
var value = this.map_[key];
transposed.set(value, key);
}
return transposed;
};
/**
* @return {!Object} Object representation of the map.
*/
goog.structs.Map.prototype.toObject = function() {
this.cleanupKeysArray_();
var obj = {};
for (var i = 0; i < this.keys_.length; i++) {
var key = this.keys_[i];
obj[key] = this.map_[key];
}
return obj;
};
/**
* Returns an iterator that iterates over the keys in the map. Removal of keys
* while iterating might have undesired side effects.
* @return {!goog.iter.Iterator} An iterator over the keys in the map.
*/
goog.structs.Map.prototype.getKeyIterator = function() {
return this.__iterator__(true);
};
/**
* Returns an iterator that iterates over the values in the map. Removal of
* keys while iterating might have undesired side effects.
* @return {!goog.iter.Iterator} An iterator over the values in the map.
*/
goog.structs.Map.prototype.getValueIterator = function() {
return this.__iterator__(false);
};
/**
* Returns an iterator that iterates over the values or the keys in the map.
* This throws an exception if the map was mutated since the iterator was
* created.
* @param {boolean=} opt_keys True to iterate over the keys. False to iterate
* over the values. The default value is false.
* @return {!goog.iter.Iterator} An iterator over the values or keys in the map.
*/
goog.structs.Map.prototype.__iterator__ = function(opt_keys) {
// Clean up keys to minimize the risk of iterating over dead keys.
this.cleanupKeysArray_();
var i = 0;
var version = this.version_;
var selfObj = this;
var newIter = new goog.iter.Iterator;
newIter.next = function() {
if (version != selfObj.version_) {
throw Error('The map has changed since the iterator was created');
}
if (i >= selfObj.keys_.length) {
throw goog.iter.StopIteration;
}
var key = selfObj.keys_[i++];
return opt_keys ? key : selfObj.map_[key];
};
return newIter;
};
/**
* Safe way to test for hasOwnProperty. It even allows testing for
* 'hasOwnProperty'.
* @param {Object} obj The object to test for presence of the given key.
* @param {*} key The key to check for.
* @return {boolean} Whether the object has the key.
* @private
*/
goog.structs.Map.hasKey_ = function(obj, key) {
return Object.prototype.hasOwnProperty.call(obj, key);
};

View File

@ -0,0 +1,354 @@
// Copyright 2006 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Generics method for collection-like classes and objects.
*
* @author arv@google.com (Erik Arvidsson)
*
* This file contains functions to work with collections. It supports using
* Map, Set, Array and Object and other classes that implement collection-like
* methods.
*/
goog.provide('goog.structs');
goog.require('goog.array');
goog.require('goog.object');
// We treat an object as a dictionary if it has getKeys or it is an object that
// isn't arrayLike.
/**
* Returns the number of values in the collection-like object.
* @param {Object} col The collection-like object.
* @return {number} The number of values in the collection-like object.
*/
goog.structs.getCount = function(col) {
if (col.getCount && typeof col.getCount == 'function') {
return col.getCount();
}
if (goog.isArrayLike(col) || goog.isString(col)) {
return col.length;
}
return goog.object.getCount(col);
};
/**
* Returns the values of the collection-like object.
* @param {Object} col The collection-like object.
* @return {!Array<?>} The values in the collection-like object.
*/
goog.structs.getValues = function(col) {
if (col.getValues && typeof col.getValues == 'function') {
return col.getValues();
}
if (goog.isString(col)) {
return col.split('');
}
if (goog.isArrayLike(col)) {
var rv = [];
var l = col.length;
for (var i = 0; i < l; i++) {
rv.push(col[i]);
}
return rv;
}
return goog.object.getValues(col);
};
/**
* Returns the keys of the collection. Some collections have no notion of
* keys/indexes and this function will return undefined in those cases.
* @param {Object} col The collection-like object.
* @return {!Array|undefined} The keys in the collection.
*/
goog.structs.getKeys = function(col) {
if (col.getKeys && typeof col.getKeys == 'function') {
return col.getKeys();
}
// if we have getValues but no getKeys we know this is a key-less collection
if (col.getValues && typeof col.getValues == 'function') {
return undefined;
}
if (goog.isArrayLike(col) || goog.isString(col)) {
var rv = [];
var l = col.length;
for (var i = 0; i < l; i++) {
rv.push(i);
}
return rv;
}
return goog.object.getKeys(col);
};
/**
* Whether the collection contains the given value. This is O(n) and uses
* equals (==) to test the existence.
* @param {Object} col The collection-like object.
* @param {*} val The value to check for.
* @return {boolean} True if the map contains the value.
*/
goog.structs.contains = function(col, val) {
if (col.contains && typeof col.contains == 'function') {
return col.contains(val);
}
if (col.containsValue && typeof col.containsValue == 'function') {
return col.containsValue(val);
}
if (goog.isArrayLike(col) || goog.isString(col)) {
return goog.array.contains(/** @type {!Array<?>} */ (col), val);
}
return goog.object.containsValue(col, val);
};
/**
* Whether the collection is empty.
* @param {Object} col The collection-like object.
* @return {boolean} True if empty.
*/
goog.structs.isEmpty = function(col) {
if (col.isEmpty && typeof col.isEmpty == 'function') {
return col.isEmpty();
}
// We do not use goog.string.isEmptyOrWhitespace because here we treat the
// string as
// collection and as such even whitespace matters
if (goog.isArrayLike(col) || goog.isString(col)) {
return goog.array.isEmpty(/** @type {!Array<?>} */ (col));
}
return goog.object.isEmpty(col);
};
/**
* Removes all the elements from the collection.
* @param {Object} col The collection-like object.
*/
goog.structs.clear = function(col) {
// NOTE(arv): This should not contain strings because strings are immutable
if (col.clear && typeof col.clear == 'function') {
col.clear();
} else if (goog.isArrayLike(col)) {
goog.array.clear(/** @type {IArrayLike<?>} */ (col));
} else {
goog.object.clear(col);
}
};
/**
* Calls a function for each value in a collection. The function takes
* three arguments; the value, the key and the collection.
*
* @param {S} col The collection-like object.
* @param {function(this:T,?,?,S):?} f The function to call for every value.
* This function takes
* 3 arguments (the value, the key or undefined if the collection has no
* notion of keys, and the collection) and the return value is irrelevant.
* @param {T=} opt_obj The object to be used as the value of 'this'
* within {@code f}.
* @template T,S
* @deprecated Use a more specific method, e.g. goog.array.forEach,
* goog.object.forEach, or for-of.
*/
goog.structs.forEach = function(col, f, opt_obj) {
if (col.forEach && typeof col.forEach == 'function') {
col.forEach(f, opt_obj);
} else if (goog.isArrayLike(col) || goog.isString(col)) {
goog.array.forEach(/** @type {!Array<?>} */ (col), f, opt_obj);
} else {
var keys = goog.structs.getKeys(col);
var values = goog.structs.getValues(col);
var l = values.length;
for (var i = 0; i < l; i++) {
f.call(/** @type {?} */ (opt_obj), values[i], keys && keys[i], col);
}
}
};
/**
* Calls a function for every value in the collection. When a call returns true,
* adds the value to a new collection (Array is returned by default).
*
* @param {S} col The collection-like object.
* @param {function(this:T,?,?,S):boolean} f The function to call for every
* value. This function takes
* 3 arguments (the value, the key or undefined if the collection has no
* notion of keys, and the collection) and should return a Boolean. If the
* return value is true the value is added to the result collection. If it
* is false the value is not included.
* @param {T=} opt_obj The object to be used as the value of 'this'
* within {@code f}.
* @return {!Object|!Array<?>} A new collection where the passed values are
* present. If col is a key-less collection an array is returned. If col
* has keys and values a plain old JS object is returned.
* @template T,S
*/
goog.structs.filter = function(col, f, opt_obj) {
if (typeof col.filter == 'function') {
return col.filter(f, opt_obj);
}
if (goog.isArrayLike(col) || goog.isString(col)) {
return goog.array.filter(/** @type {!Array<?>} */ (col), f, opt_obj);
}
var rv;
var keys = goog.structs.getKeys(col);
var values = goog.structs.getValues(col);
var l = values.length;
if (keys) {
rv = {};
for (var i = 0; i < l; i++) {
if (f.call(/** @type {?} */ (opt_obj), values[i], keys[i], col)) {
rv[keys[i]] = values[i];
}
}
} else {
// We should not use goog.array.filter here since we want to make sure that
// the index is undefined as well as make sure that col is passed to the
// function.
rv = [];
for (var i = 0; i < l; i++) {
if (f.call(opt_obj, values[i], undefined, col)) {
rv.push(values[i]);
}
}
}
return rv;
};
/**
* Calls a function for every value in the collection and adds the result into a
* new collection (defaults to creating a new Array).
*
* @param {S} col The collection-like object.
* @param {function(this:T,?,?,S):V} f The function to call for every value.
* This function takes 3 arguments (the value, the key or undefined if the
* collection has no notion of keys, and the collection) and should return
* something. The result will be used as the value in the new collection.
* @param {T=} opt_obj The object to be used as the value of 'this'
* within {@code f}.
* @return {!Object<V>|!Array<V>} A new collection with the new values. If
* col is a key-less collection an array is returned. If col has keys and
* values a plain old JS object is returned.
* @template T,S,V
*/
goog.structs.map = function(col, f, opt_obj) {
if (typeof col.map == 'function') {
return col.map(f, opt_obj);
}
if (goog.isArrayLike(col) || goog.isString(col)) {
return goog.array.map(/** @type {!Array<?>} */ (col), f, opt_obj);
}
var rv;
var keys = goog.structs.getKeys(col);
var values = goog.structs.getValues(col);
var l = values.length;
if (keys) {
rv = {};
for (var i = 0; i < l; i++) {
rv[keys[i]] = f.call(/** @type {?} */ (opt_obj), values[i], keys[i], col);
}
} else {
// We should not use goog.array.map here since we want to make sure that
// the index is undefined as well as make sure that col is passed to the
// function.
rv = [];
for (var i = 0; i < l; i++) {
rv[i] = f.call(/** @type {?} */ (opt_obj), values[i], undefined, col);
}
}
return rv;
};
/**
* Calls f for each value in a collection. If any call returns true this returns
* true (without checking the rest). If all returns false this returns false.
*
* @param {S} col The collection-like object.
* @param {function(this:T,?,?,S):boolean} f The function to call for every
* value. This function takes 3 arguments (the value, the key or undefined
* if the collection has no notion of keys, and the collection) and should
* return a boolean.
* @param {T=} opt_obj The object to be used as the value of 'this'
* within {@code f}.
* @return {boolean} True if any value passes the test.
* @template T,S
*/
goog.structs.some = function(col, f, opt_obj) {
if (typeof col.some == 'function') {
return col.some(f, opt_obj);
}
if (goog.isArrayLike(col) || goog.isString(col)) {
return goog.array.some(/** @type {!Array<?>} */ (col), f, opt_obj);
}
var keys = goog.structs.getKeys(col);
var values = goog.structs.getValues(col);
var l = values.length;
for (var i = 0; i < l; i++) {
if (f.call(/** @type {?} */ (opt_obj), values[i], keys && keys[i], col)) {
return true;
}
}
return false;
};
/**
* Calls f for each value in a collection. If all calls return true this return
* true this returns true. If any returns false this returns false at this point
* and does not continue to check the remaining values.
*
* @param {S} col The collection-like object.
* @param {function(this:T,?,?,S):boolean} f The function to call for every
* value. This function takes 3 arguments (the value, the key or
* undefined if the collection has no notion of keys, and the collection)
* and should return a boolean.
* @param {T=} opt_obj The object to be used as the value of 'this'
* within {@code f}.
* @return {boolean} True if all key-value pairs pass the test.
* @template T,S
*/
goog.structs.every = function(col, f, opt_obj) {
if (typeof col.every == 'function') {
return col.every(f, opt_obj);
}
if (goog.isArrayLike(col) || goog.isString(col)) {
return goog.array.every(/** @type {!Array<?>} */ (col), f, opt_obj);
}
var keys = goog.structs.getKeys(col);
var values = goog.structs.getValues(col);
var l = values.length;
for (var i = 0; i < l; i++) {
if (!f.call(/** @type {?} */ (opt_obj), values[i], keys && keys[i], col)) {
return false;
}
}
return true;
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,580 @@
// Copyright 2006 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Rendering engine detection.
* @see <a href="http://www.useragentstring.com/">User agent strings</a>
* For information on the browser brand (such as Safari versus Chrome), see
* goog.userAgent.product.
* @author arv@google.com (Erik Arvidsson)
* @see ../demos/useragent.html
*/
goog.provide('goog.userAgent');
goog.require('goog.labs.userAgent.browser');
goog.require('goog.labs.userAgent.engine');
goog.require('goog.labs.userAgent.platform');
goog.require('goog.labs.userAgent.util');
goog.require('goog.reflect');
goog.require('goog.string');
/**
* @define {boolean} Whether we know at compile-time that the browser is IE.
*/
goog.define('goog.userAgent.ASSUME_IE', false);
/**
* @define {boolean} Whether we know at compile-time that the browser is EDGE.
*/
goog.define('goog.userAgent.ASSUME_EDGE', false);
/**
* @define {boolean} Whether we know at compile-time that the browser is GECKO.
*/
goog.define('goog.userAgent.ASSUME_GECKO', false);
/**
* @define {boolean} Whether we know at compile-time that the browser is WEBKIT.
*/
goog.define('goog.userAgent.ASSUME_WEBKIT', false);
/**
* @define {boolean} Whether we know at compile-time that the browser is a
* mobile device running WebKit e.g. iPhone or Android.
*/
goog.define('goog.userAgent.ASSUME_MOBILE_WEBKIT', false);
/**
* @define {boolean} Whether we know at compile-time that the browser is OPERA.
*/
goog.define('goog.userAgent.ASSUME_OPERA', false);
/**
* @define {boolean} Whether the
* {@code goog.userAgent.isVersionOrHigher}
* function will return true for any version.
*/
goog.define('goog.userAgent.ASSUME_ANY_VERSION', false);
/**
* Whether we know the browser engine at compile-time.
* @type {boolean}
* @private
*/
goog.userAgent.BROWSER_KNOWN_ = goog.userAgent.ASSUME_IE ||
goog.userAgent.ASSUME_EDGE || goog.userAgent.ASSUME_GECKO ||
goog.userAgent.ASSUME_MOBILE_WEBKIT || goog.userAgent.ASSUME_WEBKIT ||
goog.userAgent.ASSUME_OPERA;
/**
* Returns the userAgent string for the current browser.
*
* @return {string} The userAgent string.
*/
goog.userAgent.getUserAgentString = function() {
return goog.labs.userAgent.util.getUserAgent();
};
/**
* TODO(nnaze): Change type to "Navigator" and update compilation targets.
* @return {?Object} The native navigator object.
*/
goog.userAgent.getNavigator = function() {
// Need a local navigator reference instead of using the global one,
// to avoid the rare case where they reference different objects.
// (in a WorkerPool, for example).
return goog.global['navigator'] || null;
};
/**
* Whether the user agent is Opera.
* @type {boolean}
*/
goog.userAgent.OPERA = goog.userAgent.BROWSER_KNOWN_ ?
goog.userAgent.ASSUME_OPERA :
goog.labs.userAgent.browser.isOpera();
/**
* Whether the user agent is Internet Explorer.
* @type {boolean}
*/
goog.userAgent.IE = goog.userAgent.BROWSER_KNOWN_ ?
goog.userAgent.ASSUME_IE :
goog.labs.userAgent.browser.isIE();
/**
* Whether the user agent is Microsoft Edge.
* @type {boolean}
*/
goog.userAgent.EDGE = goog.userAgent.BROWSER_KNOWN_ ?
goog.userAgent.ASSUME_EDGE :
goog.labs.userAgent.engine.isEdge();
/**
* Whether the user agent is MS Internet Explorer or MS Edge.
* @type {boolean}
*/
goog.userAgent.EDGE_OR_IE = goog.userAgent.EDGE || goog.userAgent.IE;
/**
* Whether the user agent is Gecko. Gecko is the rendering engine used by
* Mozilla, Firefox, and others.
* @type {boolean}
*/
goog.userAgent.GECKO = goog.userAgent.BROWSER_KNOWN_ ?
goog.userAgent.ASSUME_GECKO :
goog.labs.userAgent.engine.isGecko();
/**
* Whether the user agent is WebKit. WebKit is the rendering engine that
* Safari, Android and others use.
* @type {boolean}
*/
goog.userAgent.WEBKIT = goog.userAgent.BROWSER_KNOWN_ ?
goog.userAgent.ASSUME_WEBKIT || goog.userAgent.ASSUME_MOBILE_WEBKIT :
goog.labs.userAgent.engine.isWebKit();
/**
* Whether the user agent is running on a mobile device.
*
* This is a separate function so that the logic can be tested.
*
* TODO(nnaze): Investigate swapping in goog.labs.userAgent.device.isMobile().
*
* @return {boolean} Whether the user agent is running on a mobile device.
* @private
*/
goog.userAgent.isMobile_ = function() {
return goog.userAgent.WEBKIT &&
goog.labs.userAgent.util.matchUserAgent('Mobile');
};
/**
* Whether the user agent is running on a mobile device.
*
* TODO(nnaze): Consider deprecating MOBILE when labs.userAgent
* is promoted as the gecko/webkit logic is likely inaccurate.
*
* @type {boolean}
*/
goog.userAgent.MOBILE =
goog.userAgent.ASSUME_MOBILE_WEBKIT || goog.userAgent.isMobile_();
/**
* Used while transitioning code to use WEBKIT instead.
* @type {boolean}
* @deprecated Use {@link goog.userAgent.product.SAFARI} instead.
* TODO(nicksantos): Delete this from goog.userAgent.
*/
goog.userAgent.SAFARI = goog.userAgent.WEBKIT;
/**
* @return {string} the platform (operating system) the user agent is running
* on. Default to empty string because navigator.platform may not be defined
* (on Rhino, for example).
* @private
*/
goog.userAgent.determinePlatform_ = function() {
var navigator = goog.userAgent.getNavigator();
return navigator && navigator.platform || '';
};
/**
* The platform (operating system) the user agent is running on. Default to
* empty string because navigator.platform may not be defined (on Rhino, for
* example).
* @type {string}
*/
goog.userAgent.PLATFORM = goog.userAgent.determinePlatform_();
/**
* @define {boolean} Whether the user agent is running on a Macintosh operating
* system.
*/
goog.define('goog.userAgent.ASSUME_MAC', false);
/**
* @define {boolean} Whether the user agent is running on a Windows operating
* system.
*/
goog.define('goog.userAgent.ASSUME_WINDOWS', false);
/**
* @define {boolean} Whether the user agent is running on a Linux operating
* system.
*/
goog.define('goog.userAgent.ASSUME_LINUX', false);
/**
* @define {boolean} Whether the user agent is running on a X11 windowing
* system.
*/
goog.define('goog.userAgent.ASSUME_X11', false);
/**
* @define {boolean} Whether the user agent is running on Android.
*/
goog.define('goog.userAgent.ASSUME_ANDROID', false);
/**
* @define {boolean} Whether the user agent is running on an iPhone.
*/
goog.define('goog.userAgent.ASSUME_IPHONE', false);
/**
* @define {boolean} Whether the user agent is running on an iPad.
*/
goog.define('goog.userAgent.ASSUME_IPAD', false);
/**
* @define {boolean} Whether the user agent is running on an iPod.
*/
goog.define('goog.userAgent.ASSUME_IPOD', false);
/**
* @type {boolean}
* @private
*/
goog.userAgent.PLATFORM_KNOWN_ = goog.userAgent.ASSUME_MAC ||
goog.userAgent.ASSUME_WINDOWS || goog.userAgent.ASSUME_LINUX ||
goog.userAgent.ASSUME_X11 || goog.userAgent.ASSUME_ANDROID ||
goog.userAgent.ASSUME_IPHONE || goog.userAgent.ASSUME_IPAD ||
goog.userAgent.ASSUME_IPOD;
/**
* Whether the user agent is running on a Macintosh operating system.
* @type {boolean}
*/
goog.userAgent.MAC = goog.userAgent.PLATFORM_KNOWN_ ?
goog.userAgent.ASSUME_MAC :
goog.labs.userAgent.platform.isMacintosh();
/**
* Whether the user agent is running on a Windows operating system.
* @type {boolean}
*/
goog.userAgent.WINDOWS = goog.userAgent.PLATFORM_KNOWN_ ?
goog.userAgent.ASSUME_WINDOWS :
goog.labs.userAgent.platform.isWindows();
/**
* Whether the user agent is Linux per the legacy behavior of
* goog.userAgent.LINUX, which considered ChromeOS to also be
* Linux.
* @return {boolean}
* @private
*/
goog.userAgent.isLegacyLinux_ = function() {
return goog.labs.userAgent.platform.isLinux() ||
goog.labs.userAgent.platform.isChromeOS();
};
/**
* Whether the user agent is running on a Linux operating system.
*
* Note that goog.userAgent.LINUX considers ChromeOS to be Linux,
* while goog.labs.userAgent.platform considers ChromeOS and
* Linux to be different OSes.
*
* @type {boolean}
*/
goog.userAgent.LINUX = goog.userAgent.PLATFORM_KNOWN_ ?
goog.userAgent.ASSUME_LINUX :
goog.userAgent.isLegacyLinux_();
/**
* @return {boolean} Whether the user agent is an X11 windowing system.
* @private
*/
goog.userAgent.isX11_ = function() {
var navigator = goog.userAgent.getNavigator();
return !!navigator &&
goog.string.contains(navigator['appVersion'] || '', 'X11');
};
/**
* Whether the user agent is running on a X11 windowing system.
* @type {boolean}
*/
goog.userAgent.X11 = goog.userAgent.PLATFORM_KNOWN_ ?
goog.userAgent.ASSUME_X11 :
goog.userAgent.isX11_();
/**
* Whether the user agent is running on Android.
* @type {boolean}
*/
goog.userAgent.ANDROID = goog.userAgent.PLATFORM_KNOWN_ ?
goog.userAgent.ASSUME_ANDROID :
goog.labs.userAgent.platform.isAndroid();
/**
* Whether the user agent is running on an iPhone.
* @type {boolean}
*/
goog.userAgent.IPHONE = goog.userAgent.PLATFORM_KNOWN_ ?
goog.userAgent.ASSUME_IPHONE :
goog.labs.userAgent.platform.isIphone();
/**
* Whether the user agent is running on an iPad.
* @type {boolean}
*/
goog.userAgent.IPAD = goog.userAgent.PLATFORM_KNOWN_ ?
goog.userAgent.ASSUME_IPAD :
goog.labs.userAgent.platform.isIpad();
/**
* Whether the user agent is running on an iPod.
* @type {boolean}
*/
goog.userAgent.IPOD = goog.userAgent.PLATFORM_KNOWN_ ?
goog.userAgent.ASSUME_IPOD :
goog.labs.userAgent.platform.isIpod();
/**
* Whether the user agent is running on iOS.
* @type {boolean}
*/
goog.userAgent.IOS = goog.userAgent.PLATFORM_KNOWN_ ?
(goog.userAgent.ASSUME_IPHONE || goog.userAgent.ASSUME_IPAD ||
goog.userAgent.ASSUME_IPOD) :
goog.labs.userAgent.platform.isIos();
/**
* @return {string} The string that describes the version number of the user
* agent.
* @private
*/
goog.userAgent.determineVersion_ = function() {
// All browsers have different ways to detect the version and they all have
// different naming schemes.
// version is a string rather than a number because it may contain 'b', 'a',
// and so on.
var version = '';
var arr = goog.userAgent.getVersionRegexResult_();
if (arr) {
version = arr ? arr[1] : '';
}
if (goog.userAgent.IE) {
// IE9 can be in document mode 9 but be reporting an inconsistent user agent
// version. If it is identifying as a version lower than 9 we take the
// documentMode as the version instead. IE8 has similar behavior.
// It is recommended to set the X-UA-Compatible header to ensure that IE9
// uses documentMode 9.
var docMode = goog.userAgent.getDocumentMode_();
if (docMode != null && docMode > parseFloat(version)) {
return String(docMode);
}
}
return version;
};
/**
* @return {?Array|undefined} The version regex matches from parsing the user
* agent string. These regex statements must be executed inline so they can
* be compiled out by the closure compiler with the rest of the useragent
* detection logic when ASSUME_* is specified.
* @private
*/
goog.userAgent.getVersionRegexResult_ = function() {
var userAgent = goog.userAgent.getUserAgentString();
if (goog.userAgent.GECKO) {
return /rv\:([^\);]+)(\)|;)/.exec(userAgent);
}
if (goog.userAgent.EDGE) {
return /Edge\/([\d\.]+)/.exec(userAgent);
}
if (goog.userAgent.IE) {
return /\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/.exec(userAgent);
}
if (goog.userAgent.WEBKIT) {
// WebKit/125.4
return /WebKit\/(\S+)/.exec(userAgent);
}
if (goog.userAgent.OPERA) {
// If none of the above browsers were detected but the browser is Opera, the
// only string that is of interest is 'Version/<number>'.
return /(?:Version)[ \/]?(\S+)/.exec(userAgent);
}
return undefined;
};
/**
* @return {number|undefined} Returns the document mode (for testing).
* @private
*/
goog.userAgent.getDocumentMode_ = function() {
// NOTE(user): goog.userAgent may be used in context where there is no DOM.
var doc = goog.global['document'];
return doc ? doc['documentMode'] : undefined;
};
/**
* The version of the user agent. This is a string because it might contain
* 'b' (as in beta) as well as multiple dots.
* @type {string}
*/
goog.userAgent.VERSION = goog.userAgent.determineVersion_();
/**
* Compares two version numbers.
*
* @param {string} v1 Version of first item.
* @param {string} v2 Version of second item.
*
* @return {number} 1 if first argument is higher
* 0 if arguments are equal
* -1 if second argument is higher.
* @deprecated Use goog.string.compareVersions.
*/
goog.userAgent.compare = function(v1, v2) {
return goog.string.compareVersions(v1, v2);
};
/**
* Cache for {@link goog.userAgent.isVersionOrHigher}.
* Calls to compareVersions are surprisingly expensive and, as a browser's
* version number is unlikely to change during a session, we cache the results.
* @const
* @private
*/
goog.userAgent.isVersionOrHigherCache_ = {};
/**
* Whether the user agent version is higher or the same as the given version.
* NOTE: When checking the version numbers for Firefox and Safari, be sure to
* use the engine's version, not the browser's version number. For example,
* Firefox 3.0 corresponds to Gecko 1.9 and Safari 3.0 to Webkit 522.11.
* Opera and Internet Explorer versions match the product release number.<br>
* @see <a href="http://en.wikipedia.org/wiki/Safari_version_history">
* Webkit</a>
* @see <a href="http://en.wikipedia.org/wiki/Gecko_engine">Gecko</a>
*
* @param {string|number} version The version to check.
* @return {boolean} Whether the user agent version is higher or the same as
* the given version.
*/
goog.userAgent.isVersionOrHigher = function(version) {
return goog.userAgent.ASSUME_ANY_VERSION ||
goog.reflect.cache(
goog.userAgent.isVersionOrHigherCache_, version, function() {
return goog.string.compareVersions(
goog.userAgent.VERSION, version) >= 0;
});
};
/**
* Deprecated alias to {@code goog.userAgent.isVersionOrHigher}.
* @param {string|number} version The version to check.
* @return {boolean} Whether the user agent version is higher or the same as
* the given version.
* @deprecated Use goog.userAgent.isVersionOrHigher().
*/
goog.userAgent.isVersion = goog.userAgent.isVersionOrHigher;
/**
* Whether the IE effective document mode is higher or the same as the given
* document mode version.
* NOTE: Only for IE, return false for another browser.
*
* @param {number} documentMode The document mode version to check.
* @return {boolean} Whether the IE effective document mode is higher or the
* same as the given version.
*/
goog.userAgent.isDocumentModeOrHigher = function(documentMode) {
return Number(goog.userAgent.DOCUMENT_MODE) >= documentMode;
};
/**
* Deprecated alias to {@code goog.userAgent.isDocumentModeOrHigher}.
* @param {number} version The version to check.
* @return {boolean} Whether the IE effective document mode is higher or the
* same as the given version.
* @deprecated Use goog.userAgent.isDocumentModeOrHigher().
*/
goog.userAgent.isDocumentMode = goog.userAgent.isDocumentModeOrHigher;
/**
* For IE version < 7, documentMode is undefined, so attempt to use the
* CSS1Compat property to see if we are in standards mode. If we are in
* standards mode, treat the browser version as the document mode. Otherwise,
* IE is emulating version 5.
* @type {number|undefined}
* @const
*/
goog.userAgent.DOCUMENT_MODE = (function() {
var doc = goog.global['document'];
var mode = goog.userAgent.getDocumentMode_();
if (!doc || !goog.userAgent.IE) {
return undefined;
}
return mode || (doc['compatMode'] == 'CSS1Compat' ?
parseInt(goog.userAgent.VERSION, 10) :
5);
})();

View File

@ -0,0 +1,12 @@
;; Copyright (c) Rich Hickey. All rights reserved.
;; The use and distribution terms for this software are covered by the
;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
;; which can be found in the file epl-v10.html at the root of this distribution.
;; By using this software in any fashion, you are agreeing to be bound by
;; the terms of this license.
;; You must not remove this notice, or any other, from this software.
(ns process.env
"A shim namespace for the Node.js process library")
(goog-define NODE_ENV "development")

View File

@ -0,0 +1,7 @@
// Compiled by ClojureScript 1.10.520 {:static-fns true, :optimize-constants true}
goog.provide('process.env');
goog.require('cljs.core');
goog.require('cljs.core.constants');
/** @define {string} */
goog.define("process.env.NODE_ENV","development");

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,143 @@
(ns ^{:doc "Quil middleware."}
quil.middleware
(:require [quil.middlewares.fun-mode :as fun-mode]
#?(:clj [quil.middlewares.pause-on-error :as pause-on-error])
[quil.middlewares.navigation-3d :as navigation-3d]
[quil.middlewares.navigation-2d :as navigation-2d]))
(defn ^{:requires-bindings false
:category "Middleware"
:subcategory nil
:ns "quil.middleware"
:added "2.1.0"}
fun-mode
"Introduces `function mode`. Adds `update` function which takes current state
and returns new state. Makes all other functions (`setup`, `draw`,
`mouse-click`, etc) state-aware.
See [wiki](https://github.com/quil/quil/wiki/Functional-mode-%28fun-mode%29)
for more details."
[options]
(fun-mode/fun-mode options))
#?(:clj
(defn ^{:requires-bindings false
:category "Middleware"
:subcategory nil
:ns "quil.middleware"
:added "2.2.0"}
pause-on-error
"Pauses sketch if any of the user-provided handlers throws an error.
It allows to fix the error on the fly and continue sketch.
May be good alternative to default '500ms pause if exception'
behaviour."
[options]
(pause-on-error/pause-on-error options)))
(defn ^{:requires-bindings false
:category "Middleware"
:subcategory nil
:ns "quil.middleware"
:added "2.2.0"}
navigation-3d
"Enables navigation in 3D space. Similar to how it is done in
shooters: WASD navigation, space is go up, drag mouse to look around.
This middleware requires [[fun-mode]].
Navigation
* Drag mouse to look around. You can change settings to bind
mouse-moved instead of mouse-dragged to look around. See
customization info below.
* Keyboard:
* `w` - go forward
* `s` - go backward
* `a` - strafe left
* `d` - strafe right
* `space` - go up
* `z` - go down, can't bind to `ctrl`, limitation of Processing
Customization
You can customize this middleware by providing a map as
`:navigation-3d` option in [[quil.sketch/defsketch]]/[[quil.sketch/sketch]].
The map can have the following optional keys:
* `:position` - vector of 3 numbers, initial camera position. Default
is the same as in [[quil.core/camera]] function.
* `:straight` - vector of 3 numbers, direction you'll be looking at.
Default is `[0 0 -1]` (looking down).
* `:up` - vector of 3 numbers, 'up' direction. Default is `[0 1 0]`.
* `:pixels-in-360` - number, mouse sensitivity. Defines how many pixels
you need to move/drag your mouse to rotate 360 degrees.
The less the number the more sensitive the mouse.
Default is `1000`.
* `:step-size` - number, number of pixels you move on each key event (wasd).
Default is `20`.
* `:rotate-on` - keyword, either `:mouse-dragged` or `:mouse-moved`. Specifies
on which mouse event camera should rotate. Default is
`:mouse-dragged`.
Accessing position information from a sketch
[[navigation-3d]] uses [[fun-mode]] under the hood so all position-related
information is stored in the state map. It means that you can access in
draw/update/any handler and modify it if you need to. Position
information is a map which is stored under `:navigation-3d` key in the
state map. Position consists of 3 values: `:position`, `:straight` and `:up`.
See \"Customization\" section above for more details.
Example:
```
(q/defsketch my-sketch
...
:middleware [m/fun-mode m/navigation-3d])
```
See wiki article for more(?) details:
https://github.com/quil/quil/wiki/Navigation-3D"
[options]
(navigation-3d/navigation-3d options))
(defn ^{:requires-bindings false
:category "Middleware"
:subcategory nil
:ns "quil.middleware"
:added "2.2.6"}
navigation-2d
"Enables navigation over 2D sketch. Drag mouse to change the center of the
sketch and mouse wheel controls zoom. This middleware requires [[fun-mode]].
Customization
You can customize this middleware by providing a map as
`:navigation-2d` option in [[quil.sketch/defsketch]]/[[quil.sketch/sketch]].
The map can have the following optional keys:
* `:position` - vector of 2 numbers, x and y - center of the screen.
Default is `width/2`, `height/2`.
* `:zoom` - number indicating current zoom level. Default is `1`.
Accessing position information from a sketch
[[navigation-2d]] uses [[fun-mode]] under the hood so all position-related
information is stored in the state map. It means that you can access in
draw/update/any handler and modify it if you need to. Position
information is a map which is stored under `:navigation-2d` key in the
state map. Position consists of 2 values: `:position` and `:zoom`.
See \"Customization\" section above for more details.
Example:
```
(q/defsketch my-sketch
...
:middleware [m/fun-mode m/navigation-2d])
```"
[options]
(navigation-2d/navigation-2d options))

View File

@ -0,0 +1,118 @@
// Compiled by ClojureScript 1.10.520 {:static-fns true, :optimize-constants true}
goog.provide('quil.middleware');
goog.require('cljs.core');
goog.require('cljs.core.constants');
goog.require('quil.middlewares.fun_mode');
goog.require('quil.middlewares.navigation_3d');
goog.require('quil.middlewares.navigation_2d');
/**
* Introduces `function mode`. Adds `update` function which takes current state
* and returns new state. Makes all other functions (`setup`, `draw`,
* `mouse-click`, etc) state-aware.
* See [wiki](https://github.com/quil/quil/wiki/Functional-mode-%28fun-mode%29)
* for more details.
*/
quil.middleware.fun_mode = (function quil$middleware$fun_mode(options){
return quil.middlewares.fun_mode.fun_mode(options);
});
/**
* Enables navigation in 3D space. Similar to how it is done in
* shooters: WASD navigation, space is go up, drag mouse to look around.
* This middleware requires [[fun-mode]].
*
* Navigation
*
* * Drag mouse to look around. You can change settings to bind
* mouse-moved instead of mouse-dragged to look around. See
* customization info below.
*
* * Keyboard:
* * `w` - go forward
* * `s` - go backward
* * `a` - strafe left
* * `d` - strafe right
* * `space` - go up
* * `z` - go down, can't bind to `ctrl`, limitation of Processing
*
* Customization
*
* You can customize this middleware by providing a map as
* `:navigation-3d` option in [[quil.sketch/defsketch]]/[[quil.sketch/sketch]].
* The map can have the following optional keys:
*
* * `:position` - vector of 3 numbers, initial camera position. Default
* is the same as in [[quil.core/camera]] function.
*
* * `:straight` - vector of 3 numbers, direction you'll be looking at.
* Default is `[0 0 -1]` (looking down).
*
* * `:up` - vector of 3 numbers, 'up' direction. Default is `[0 1 0]`.
*
* * `:pixels-in-360` - number, mouse sensitivity. Defines how many pixels
* you need to move/drag your mouse to rotate 360 degrees.
* The less the number the more sensitive the mouse.
* Default is `1000`.
*
* * `:step-size` - number, number of pixels you move on each key event (wasd).
* Default is `20`.
*
* * `:rotate-on` - keyword, either `:mouse-dragged` or `:mouse-moved`. Specifies
* on which mouse event camera should rotate. Default is
* `:mouse-dragged`.
*
* Accessing position information from a sketch
*
* [[navigation-3d]] uses [[fun-mode]] under the hood so all position-related
* information is stored in the state map. It means that you can access in
* draw/update/any handler and modify it if you need to. Position
* information is a map which is stored under `:navigation-3d` key in the
* state map. Position consists of 3 values: `:position`, `:straight` and `:up`.
* See "Customization" section above for more details.
*
* Example:
* ```
* (q/defsketch my-sketch
* ...
* :middleware [m/fun-mode m/navigation-3d])
* ```
*
* See wiki article for more(?) details:
* https://github.com/quil/quil/wiki/Navigation-3D
*/
quil.middleware.navigation_3d = (function quil$middleware$navigation_3d(options){
return quil.middlewares.navigation_3d.navigation_3d(options);
});
/**
* Enables navigation over 2D sketch. Drag mouse to change the center of the
* sketch and mouse wheel controls zoom. This middleware requires [[fun-mode]].
*
* Customization
*
* You can customize this middleware by providing a map as
* `:navigation-2d` option in [[quil.sketch/defsketch]]/[[quil.sketch/sketch]].
* The map can have the following optional keys:
*
* * `:position` - vector of 2 numbers, x and y - center of the screen.
* Default is `width/2`, `height/2`.
*
* * `:zoom` - number indicating current zoom level. Default is `1`.
*
* Accessing position information from a sketch
*
* [[navigation-2d]] uses [[fun-mode]] under the hood so all position-related
* information is stored in the state map. It means that you can access in
* draw/update/any handler and modify it if you need to. Position
* information is a map which is stored under `:navigation-2d` key in the
* state map. Position consists of 2 values: `:position` and `:zoom`.
* See "Customization" section above for more details.
*
* Example:
* ```
* (q/defsketch my-sketch
* ...
* :middleware [m/fun-mode m/navigation-2d])
* ```
*/
quil.middleware.navigation_2d = (function quil$middleware$navigation_2d(options){
return quil.middlewares.navigation_2d.navigation_2d(options);
});

View File

@ -0,0 +1,26 @@
(ns quil.middlewares.deprecated-options)
(def ^:private deprecated
{:decor ["2.0" "Try :features [:present] for similar effect"]
:target ["2.0" "Use :features [:keep-on-top] instead."]
:safe-draw-fn ["2.0" "Use :features [:no-safe-fns] instead."]})
(defn- check-features-vector [features]
(let [features (set features)]
(when (features :no-safe-draw)
(println "Feature :no-safe-draw was renamed to :no-safe-fns in Quil 2.1."
"Use :feature [:no-safe-fns] now."))
(disj features :no-safe-draw)))
(defn deprecated-options
"Checks `options` map for deprecated options, removes them and
prints messages explaining how to fix them."
[options]
(let [options (update-in options [:features] check-features-vector)]
(->> (for [[name value] options]
(if-let [[version message] (deprecated name)]
(do (println name "option was removed in Quil" version "." message)
nil)
[name value]))
(remove nil?)
(into {}))))

View File

@ -0,0 +1,96 @@
// Compiled by ClojureScript 1.10.520 {:static-fns true, :optimize-constants true}
goog.provide('quil.middlewares.deprecated_options');
goog.require('cljs.core');
goog.require('cljs.core.constants');
quil.middlewares.deprecated_options.deprecated = new cljs.core.PersistentArrayMap(null, 3, [cljs.core.cst$kw$decor,new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, ["2.0","Try :features [:present] for similar effect"], null),cljs.core.cst$kw$target,new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, ["2.0","Use :features [:keep-on-top] instead."], null),cljs.core.cst$kw$safe_DASH_draw_DASH_fn,new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, ["2.0","Use :features [:no-safe-fns] instead."], null)], null);
quil.middlewares.deprecated_options.check_features_vector = (function quil$middlewares$deprecated_options$check_features_vector(features){
var features__$1 = cljs.core.set(features);
if(cljs.core.truth_((function (){var G__5517 = cljs.core.cst$kw$no_DASH_safe_DASH_draw;
return (features__$1.cljs$core$IFn$_invoke$arity$1 ? features__$1.cljs$core$IFn$_invoke$arity$1(G__5517) : features__$1.call(null,G__5517));
})())){
cljs.core.println.cljs$core$IFn$_invoke$arity$variadic(cljs.core.prim_seq.cljs$core$IFn$_invoke$arity$2(["Feature :no-safe-draw was renamed to :no-safe-fns in Quil 2.1.","Use :feature [:no-safe-fns] now."], 0));
} else {
}
return cljs.core.disj.cljs$core$IFn$_invoke$arity$2(features__$1,cljs.core.cst$kw$no_DASH_safe_DASH_draw);
});
/**
* Checks `options` map for deprecated options, removes them and
* prints messages explaining how to fix them.
*/
quil.middlewares.deprecated_options.deprecated_options = (function quil$middlewares$deprecated_options$deprecated_options(options){
var options__$1 = cljs.core.update_in.cljs$core$IFn$_invoke$arity$3(options,new cljs.core.PersistentVector(null, 1, 5, cljs.core.PersistentVector.EMPTY_NODE, [cljs.core.cst$kw$features], null),quil.middlewares.deprecated_options.check_features_vector);
return cljs.core.into.cljs$core$IFn$_invoke$arity$2(cljs.core.PersistentArrayMap.EMPTY,cljs.core.remove.cljs$core$IFn$_invoke$arity$2(cljs.core.nil_QMARK_,(function (){var iter__4523__auto__ = ((function (options__$1){
return (function quil$middlewares$deprecated_options$deprecated_options_$_iter__5518(s__5519){
return (new cljs.core.LazySeq(null,((function (options__$1){
return (function (){
var s__5519__$1 = s__5519;
while(true){
var temp__5735__auto__ = cljs.core.seq(s__5519__$1);
if(temp__5735__auto__){
var s__5519__$2 = temp__5735__auto__;
if(cljs.core.chunked_seq_QMARK_(s__5519__$2)){
var c__4521__auto__ = cljs.core.chunk_first(s__5519__$2);
var size__4522__auto__ = cljs.core.count(c__4521__auto__);
var b__5521 = cljs.core.chunk_buffer(size__4522__auto__);
if((function (){var i__5520 = (0);
while(true){
if((i__5520 < size__4522__auto__)){
var vec__5522 = cljs.core._nth.cljs$core$IFn$_invoke$arity$2(c__4521__auto__,i__5520);
var name = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__5522,(0),null);
var value = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__5522,(1),null);
cljs.core.chunk_append(b__5521,(function (){var temp__5733__auto__ = (quil.middlewares.deprecated_options.deprecated.cljs$core$IFn$_invoke$arity$1 ? quil.middlewares.deprecated_options.deprecated.cljs$core$IFn$_invoke$arity$1(name) : quil.middlewares.deprecated_options.deprecated.call(null,name));
if(cljs.core.truth_(temp__5733__auto__)){
var vec__5525 = temp__5733__auto__;
var version = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__5525,(0),null);
var message = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__5525,(1),null);
cljs.core.println.cljs$core$IFn$_invoke$arity$variadic(cljs.core.prim_seq.cljs$core$IFn$_invoke$arity$2([name,"option was removed in Quil",version,".",message], 0));
return null;
} else {
return new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, [name,value], null);
}
})());
var G__5534 = (i__5520 + (1));
i__5520 = G__5534;
continue;
} else {
return true;
}
break;
}
})()){
return cljs.core.chunk_cons(cljs.core.chunk(b__5521),quil$middlewares$deprecated_options$deprecated_options_$_iter__5518(cljs.core.chunk_rest(s__5519__$2)));
} else {
return cljs.core.chunk_cons(cljs.core.chunk(b__5521),null);
}
} else {
var vec__5528 = cljs.core.first(s__5519__$2);
var name = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__5528,(0),null);
var value = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__5528,(1),null);
return cljs.core.cons((function (){var temp__5733__auto__ = (quil.middlewares.deprecated_options.deprecated.cljs$core$IFn$_invoke$arity$1 ? quil.middlewares.deprecated_options.deprecated.cljs$core$IFn$_invoke$arity$1(name) : quil.middlewares.deprecated_options.deprecated.call(null,name));
if(cljs.core.truth_(temp__5733__auto__)){
var vec__5531 = temp__5733__auto__;
var version = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__5531,(0),null);
var message = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__5531,(1),null);
cljs.core.println.cljs$core$IFn$_invoke$arity$variadic(cljs.core.prim_seq.cljs$core$IFn$_invoke$arity$2([name,"option was removed in Quil",version,".",message], 0));
return null;
} else {
return new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, [name,value], null);
}
})(),quil$middlewares$deprecated_options$deprecated_options_$_iter__5518(cljs.core.rest(s__5519__$2)));
}
} else {
return null;
}
break;
}
});})(options__$1))
,null,null));
});})(options__$1))
;
return iter__4523__auto__(options__$1);
})()));
});

View File

@ -0,0 +1,78 @@
(ns quil.middlewares.fun-mode
(:require [quil.core :as q]))
(defn- wrap-setup [options]
(let [setup (:setup options (fn [] nil))]
(assoc options
:setup #(reset! (q/state-atom) (setup)))))
(defn- wrap-draw-update [options]
(let [draw (:draw options (fn [_]))
update (:update options identity)
quil-draw #(-> (q/state-atom)
(swap! (if (= (q/frame-count) 1)
identity
update))
(draw))]
(-> options
(dissoc :update)
(assoc :draw quil-draw))))
(defn- mouse-event []
{:x (q/mouse-x)
:y (q/mouse-y)})
(defn- mouse-event-full []
{:x (q/mouse-x)
:y (q/mouse-y)
:button (q/mouse-button)})
(defn- key-event []
{:key (q/key-as-keyword)
:key-code (q/key-code)
:raw-key (q/raw-key)
#?@(:clj [:modifiers (q/key-modifiers)])})
(defn- wrap-handler
([options handler-key]
(wrap-handler options handler-key nil))
([options handler-key event-fn]
(if-let [handler (options handler-key)]
(assoc options handler-key
(if event-fn
#(swap! (q/state-atom) handler (event-fn))
#(swap! (q/state-atom) handler)))
options)))
(defn- wrap-handlers [options & handlers]
(reduce (fn [options handler]
(if (keyword? handler)
(wrap-handler options handler)
(apply wrap-handler options handler)))
options handlers))
(defn- wrap-mouse-wheel [options]
(if-let [handler (:mouse-wheel options)]
(assoc options :mouse-wheel
(fn [rotation]
(swap! (q/state-atom) handler rotation)))
options))
(defn fun-mode
"Introduces `function mode` making all handlers (`setup`, `draw`,
`mouse-click`, etc) state-aware. Adds support for `update` function."
[options]
(-> options
wrap-setup
wrap-draw-update
(wrap-handlers :focus-gained :focus-lost [:mouse-entered mouse-event]
[:mouse-exited mouse-event] [:mouse-pressed mouse-event-full]
[:mouse-released mouse-event] [:mouse-clicked mouse-event-full]
[:mouse-moved (fn [] {:x (q/mouse-x) :y (q/mouse-y)
:p-x (q/pmouse-x) :p-y (q/pmouse-y)})]
[:mouse-dragged (fn [] {:x (q/mouse-x) :y (q/mouse-y)
:p-x (q/pmouse-x) :p-y (q/pmouse-y)
:button (q/mouse-button)})]
[:key-pressed key-event] [:key-released key-event] [:key-typed key-event]
:on-close)
wrap-mouse-wheel))

View File

@ -0,0 +1,142 @@
// Compiled by ClojureScript 1.10.520 {:static-fns true, :optimize-constants true}
goog.provide('quil.middlewares.fun_mode');
goog.require('cljs.core');
goog.require('cljs.core.constants');
goog.require('quil.core');
quil.middlewares.fun_mode.wrap_setup = (function quil$middlewares$fun_mode$wrap_setup(options){
var setup = cljs.core.cst$kw$setup.cljs$core$IFn$_invoke$arity$2(options,(function (){
return null;
}));
return cljs.core.assoc.cljs$core$IFn$_invoke$arity$3(options,cljs.core.cst$kw$setup,((function (setup){
return (function (){
return cljs.core.reset_BANG_(quil.core.state_atom(),(setup.cljs$core$IFn$_invoke$arity$0 ? setup.cljs$core$IFn$_invoke$arity$0() : setup.call(null)));
});})(setup))
);
});
quil.middlewares.fun_mode.wrap_draw_update = (function quil$middlewares$fun_mode$wrap_draw_update(options){
var draw = cljs.core.cst$kw$draw.cljs$core$IFn$_invoke$arity$2(options,(function (_){
return null;
}));
var update = cljs.core.cst$kw$update.cljs$core$IFn$_invoke$arity$2(options,cljs.core.identity);
var quil_draw = ((function (draw,update){
return (function (){
var G__6085 = cljs.core.swap_BANG_.cljs$core$IFn$_invoke$arity$2(quil.core.state_atom(),((cljs.core._EQ_.cljs$core$IFn$_invoke$arity$2(quil.core.frame_count(),(1)))?cljs.core.identity:update));
return (draw.cljs$core$IFn$_invoke$arity$1 ? draw.cljs$core$IFn$_invoke$arity$1(G__6085) : draw.call(null,G__6085));
});})(draw,update))
;
return cljs.core.assoc.cljs$core$IFn$_invoke$arity$3(cljs.core.dissoc.cljs$core$IFn$_invoke$arity$2(options,cljs.core.cst$kw$update),cljs.core.cst$kw$draw,quil_draw);
});
quil.middlewares.fun_mode.mouse_event = (function quil$middlewares$fun_mode$mouse_event(){
return new cljs.core.PersistentArrayMap(null, 2, [cljs.core.cst$kw$x,quil.core.mouse_x(),cljs.core.cst$kw$y,quil.core.mouse_y()], null);
});
quil.middlewares.fun_mode.mouse_event_full = (function quil$middlewares$fun_mode$mouse_event_full(){
return new cljs.core.PersistentArrayMap(null, 3, [cljs.core.cst$kw$x,quil.core.mouse_x(),cljs.core.cst$kw$y,quil.core.mouse_y(),cljs.core.cst$kw$button,quil.core.mouse_button()], null);
});
quil.middlewares.fun_mode.key_event = (function quil$middlewares$fun_mode$key_event(){
return new cljs.core.PersistentArrayMap(null, 3, [cljs.core.cst$kw$key,quil.core.key_as_keyword(),cljs.core.cst$kw$key_DASH_code,quil.core.key_code(),cljs.core.cst$kw$raw_DASH_key,quil.core.raw_key()], null);
});
quil.middlewares.fun_mode.wrap_handler = (function quil$middlewares$fun_mode$wrap_handler(var_args){
var G__6087 = arguments.length;
switch (G__6087) {
case 2:
return quil.middlewares.fun_mode.wrap_handler.cljs$core$IFn$_invoke$arity$2((arguments[(0)]),(arguments[(1)]));
break;
case 3:
return quil.middlewares.fun_mode.wrap_handler.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('')));
}
});
quil.middlewares.fun_mode.wrap_handler.cljs$core$IFn$_invoke$arity$2 = (function (options,handler_key){
return quil.middlewares.fun_mode.wrap_handler.cljs$core$IFn$_invoke$arity$3(options,handler_key,null);
});
quil.middlewares.fun_mode.wrap_handler.cljs$core$IFn$_invoke$arity$3 = (function (options,handler_key,event_fn){
var temp__5733__auto__ = (options.cljs$core$IFn$_invoke$arity$1 ? options.cljs$core$IFn$_invoke$arity$1(handler_key) : options.call(null,handler_key));
if(cljs.core.truth_(temp__5733__auto__)){
var handler = temp__5733__auto__;
return cljs.core.assoc.cljs$core$IFn$_invoke$arity$3(options,handler_key,(cljs.core.truth_(event_fn)?((function (handler,temp__5733__auto__){
return (function (){
return cljs.core.swap_BANG_.cljs$core$IFn$_invoke$arity$3(quil.core.state_atom(),handler,(event_fn.cljs$core$IFn$_invoke$arity$0 ? event_fn.cljs$core$IFn$_invoke$arity$0() : event_fn.call(null)));
});})(handler,temp__5733__auto__))
:((function (handler,temp__5733__auto__){
return (function (){
return cljs.core.swap_BANG_.cljs$core$IFn$_invoke$arity$2(quil.core.state_atom(),handler);
});})(handler,temp__5733__auto__))
));
} else {
return options;
}
});
quil.middlewares.fun_mode.wrap_handler.cljs$lang$maxFixedArity = 3;
quil.middlewares.fun_mode.wrap_handlers = (function quil$middlewares$fun_mode$wrap_handlers(var_args){
var args__4736__auto__ = [];
var len__4730__auto___6091 = arguments.length;
var i__4731__auto___6092 = (0);
while(true){
if((i__4731__auto___6092 < len__4730__auto___6091)){
args__4736__auto__.push((arguments[i__4731__auto___6092]));
var G__6093 = (i__4731__auto___6092 + (1));
i__4731__auto___6092 = G__6093;
continue;
} else {
}
break;
}
var argseq__4737__auto__ = ((((1) < args__4736__auto__.length))?(new cljs.core.IndexedSeq(args__4736__auto__.slice((1)),(0),null)):null);
return quil.middlewares.fun_mode.wrap_handlers.cljs$core$IFn$_invoke$arity$variadic((arguments[(0)]),argseq__4737__auto__);
});
quil.middlewares.fun_mode.wrap_handlers.cljs$core$IFn$_invoke$arity$variadic = (function (options,handlers){
return cljs.core.reduce.cljs$core$IFn$_invoke$arity$3((function (options__$1,handler){
if((handler instanceof cljs.core.Keyword)){
return quil.middlewares.fun_mode.wrap_handler.cljs$core$IFn$_invoke$arity$2(options__$1,handler);
} else {
return cljs.core.apply.cljs$core$IFn$_invoke$arity$3(quil.middlewares.fun_mode.wrap_handler,options__$1,handler);
}
}),options,handlers);
});
quil.middlewares.fun_mode.wrap_handlers.cljs$lang$maxFixedArity = (1);
/** @this {Function} */
quil.middlewares.fun_mode.wrap_handlers.cljs$lang$applyTo = (function (seq6089){
var G__6090 = cljs.core.first(seq6089);
var seq6089__$1 = cljs.core.next(seq6089);
var self__4717__auto__ = this;
return self__4717__auto__.cljs$core$IFn$_invoke$arity$variadic(G__6090,seq6089__$1);
});
quil.middlewares.fun_mode.wrap_mouse_wheel = (function quil$middlewares$fun_mode$wrap_mouse_wheel(options){
var temp__5733__auto__ = cljs.core.cst$kw$mouse_DASH_wheel.cljs$core$IFn$_invoke$arity$1(options);
if(cljs.core.truth_(temp__5733__auto__)){
var handler = temp__5733__auto__;
return cljs.core.assoc.cljs$core$IFn$_invoke$arity$3(options,cljs.core.cst$kw$mouse_DASH_wheel,((function (handler,temp__5733__auto__){
return (function (rotation){
return cljs.core.swap_BANG_.cljs$core$IFn$_invoke$arity$3(quil.core.state_atom(),handler,rotation);
});})(handler,temp__5733__auto__))
);
} else {
return options;
}
});
/**
* Introduces `function mode` making all handlers (`setup`, `draw`,
* `mouse-click`, etc) state-aware. Adds support for `update` function.
*/
quil.middlewares.fun_mode.fun_mode = (function quil$middlewares$fun_mode$fun_mode(options){
return quil.middlewares.fun_mode.wrap_mouse_wheel(quil.middlewares.fun_mode.wrap_handlers.cljs$core$IFn$_invoke$arity$variadic(quil.middlewares.fun_mode.wrap_draw_update(quil.middlewares.fun_mode.wrap_setup(options)),cljs.core.prim_seq.cljs$core$IFn$_invoke$arity$2([cljs.core.cst$kw$focus_DASH_gained,cljs.core.cst$kw$focus_DASH_lost,new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, [cljs.core.cst$kw$mouse_DASH_entered,quil.middlewares.fun_mode.mouse_event], null),new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, [cljs.core.cst$kw$mouse_DASH_exited,quil.middlewares.fun_mode.mouse_event], null),new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, [cljs.core.cst$kw$mouse_DASH_pressed,quil.middlewares.fun_mode.mouse_event_full], null),new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, [cljs.core.cst$kw$mouse_DASH_released,quil.middlewares.fun_mode.mouse_event], null),new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, [cljs.core.cst$kw$mouse_DASH_clicked,quil.middlewares.fun_mode.mouse_event_full], null),new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, [cljs.core.cst$kw$mouse_DASH_moved,(function (){
return new cljs.core.PersistentArrayMap(null, 4, [cljs.core.cst$kw$x,quil.core.mouse_x(),cljs.core.cst$kw$y,quil.core.mouse_y(),cljs.core.cst$kw$p_DASH_x,quil.core.pmouse_x(),cljs.core.cst$kw$p_DASH_y,quil.core.pmouse_y()], null);
})], null),new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, [cljs.core.cst$kw$mouse_DASH_dragged,(function (){
return new cljs.core.PersistentArrayMap(null, 5, [cljs.core.cst$kw$x,quil.core.mouse_x(),cljs.core.cst$kw$y,quil.core.mouse_y(),cljs.core.cst$kw$p_DASH_x,quil.core.pmouse_x(),cljs.core.cst$kw$p_DASH_y,quil.core.pmouse_y(),cljs.core.cst$kw$button,quil.core.mouse_button()], null);
})], null),new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, [cljs.core.cst$kw$key_DASH_pressed,quil.middlewares.fun_mode.key_event], null),new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, [cljs.core.cst$kw$key_DASH_released,quil.middlewares.fun_mode.key_event], null),new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, [cljs.core.cst$kw$key_DASH_typed,quil.middlewares.fun_mode.key_event], null),cljs.core.cst$kw$on_DASH_close], 0)));
});

View File

@ -0,0 +1,89 @@
(ns quil.middlewares.navigation-2d
(:require [quil.core :as q :include-macros true]))
(def ^:private ^String missing-navigation-key-error
(str "state map is missing :navigation-2d key. "
"Did you accidentally removed it from the state in "
":update or any other handler?"))
(defn- assert-state-has-navigation
"Asserts that `state` map contains `:navigation-2d` object."
[state]
(when-not (:navigation-2d state)
(throw #?(:clj (RuntimeException. missing-navigation-key-error)
:cljs (js/Error. missing-navigation-key-error)))))
(defn- default-position
"Default position configuration: zoom is neutral and central point is
`width/2, height/2`."
[]
{:position [(/ (q/width) 2.0)
(/ (q/height) 2.0)]
:zoom 1})
(defn- setup-2d-nav
"Custom 'setup' function which creates initial position
configuration and puts it to the state map."
[user-setup user-settings]
(let [initial-state (-> user-settings
(select-keys [:position :zoom])
(->> (merge (default-position))))]
(update-in (user-setup) [:navigation-2d]
#(merge initial-state %))))
(defn- mouse-dragged
"Changes center of the sketch depending on the last mouse move. Takes
zoom into account as well."
[state event]
(assert-state-has-navigation state)
(let [dx (- (:p-x event) (:x event))
dy (- (:p-y event) (:y event))
zoom (-> state :navigation-2d :zoom)]
(-> state
(update-in [:navigation-2d :position 0] + (/ dx zoom))
(update-in [:navigation-2d :position 1] + (/ dy zoom)))))
(defn- mouse-wheel
"Changes zoom settings based on scroll."
[state event]
(assert-state-has-navigation state)
(update-in state [:navigation-2d :zoom] * (+ 1 (* -0.1 event))))
(defn- draw
"Calls user draw function with all necessary transformations (position
and zoom) applied."
[user-draw state]
(assert-state-has-navigation state)
(q/push-matrix)
(let [nav-2d (:navigation-2d state)
zoom (:zoom nav-2d)
pos (:position nav-2d)]
(q/scale zoom)
(q/with-translation [(- (/ (q/width) 2 zoom) (first pos))
(- (/ (q/height) 2 zoom) (second pos))]
(user-draw state)))
(q/pop-matrix))
(defn navigation-2d
"Enables navigation over 2D sketch. Dragging mouse will move center of the
sketch and mouse wheel controls zoom."
[options]
(let [; 2d-navigation related user settings
user-settings (:navigation-2d options)
; user-provided handlers which will be overridden
; by 3d-navigation
user-draw (:draw options (fn [state]))
user-mouse-dragged (:mouse-dragged options (fn [state _] state))
user-mouse-wheel (:mouse-wheel options (fn [state _] state))
setup (:setup options (fn [] {}))]
(assoc options
:setup (partial setup-2d-nav setup user-settings)
:draw (partial draw user-draw)
:mouse-dragged (fn [state event]
(user-mouse-dragged (mouse-dragged state event) event))
:mouse-wheel (fn [state event]
(user-mouse-wheel (mouse-wheel state event) event)))))

View File

@ -0,0 +1,119 @@
// Compiled by ClojureScript 1.10.520 {:static-fns true, :optimize-constants true}
goog.provide('quil.middlewares.navigation_2d');
goog.require('cljs.core');
goog.require('cljs.core.constants');
goog.require('quil.core');
quil.middlewares.navigation_2d.missing_navigation_key_error = ["state map is missing :navigation-2d key. ","Did you accidentally removed it from the state in ",":update or any other handler?"].join('');
/**
* Asserts that `state` map contains `:navigation-2d` object.
*/
quil.middlewares.navigation_2d.assert_state_has_navigation = (function quil$middlewares$navigation_2d$assert_state_has_navigation(state){
if(cljs.core.truth_(cljs.core.cst$kw$navigation_DASH_2d.cljs$core$IFn$_invoke$arity$1(state))){
return null;
} else {
throw (new Error(quil.middlewares.navigation_2d.missing_navigation_key_error));
}
});
/**
* Default position configuration: zoom is neutral and central point is
* `width/2, height/2`.
*/
quil.middlewares.navigation_2d.default_position = (function quil$middlewares$navigation_2d$default_position(){
return new cljs.core.PersistentArrayMap(null, 2, [cljs.core.cst$kw$position,new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, [(quil.core.width() / 2.0),(quil.core.height() / 2.0)], null),cljs.core.cst$kw$zoom,(1)], null);
});
/**
* Custom 'setup' function which creates initial position
* configuration and puts it to the state map.
*/
quil.middlewares.navigation_2d.setup_2d_nav = (function quil$middlewares$navigation_2d$setup_2d_nav(user_setup,user_settings){
var initial_state = cljs.core.merge.cljs$core$IFn$_invoke$arity$variadic(cljs.core.prim_seq.cljs$core$IFn$_invoke$arity$2([quil.middlewares.navigation_2d.default_position(),cljs.core.select_keys(user_settings,new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, [cljs.core.cst$kw$position,cljs.core.cst$kw$zoom], null))], 0));
return cljs.core.update_in.cljs$core$IFn$_invoke$arity$3((user_setup.cljs$core$IFn$_invoke$arity$0 ? user_setup.cljs$core$IFn$_invoke$arity$0() : user_setup.call(null)),new cljs.core.PersistentVector(null, 1, 5, cljs.core.PersistentVector.EMPTY_NODE, [cljs.core.cst$kw$navigation_DASH_2d], null),((function (initial_state){
return (function (p1__6074_SHARP_){
return cljs.core.merge.cljs$core$IFn$_invoke$arity$variadic(cljs.core.prim_seq.cljs$core$IFn$_invoke$arity$2([initial_state,p1__6074_SHARP_], 0));
});})(initial_state))
);
});
/**
* Changes center of the sketch depending on the last mouse move. Takes
* zoom into account as well.
*/
quil.middlewares.navigation_2d.mouse_dragged = (function quil$middlewares$navigation_2d$mouse_dragged(state,event){
quil.middlewares.navigation_2d.assert_state_has_navigation(state);
var dx = (cljs.core.cst$kw$p_DASH_x.cljs$core$IFn$_invoke$arity$1(event) - cljs.core.cst$kw$x.cljs$core$IFn$_invoke$arity$1(event));
var dy = (cljs.core.cst$kw$p_DASH_y.cljs$core$IFn$_invoke$arity$1(event) - cljs.core.cst$kw$y.cljs$core$IFn$_invoke$arity$1(event));
var zoom = cljs.core.cst$kw$zoom.cljs$core$IFn$_invoke$arity$1(cljs.core.cst$kw$navigation_DASH_2d.cljs$core$IFn$_invoke$arity$1(state));
return cljs.core.update_in.cljs$core$IFn$_invoke$arity$4(cljs.core.update_in.cljs$core$IFn$_invoke$arity$4(state,new cljs.core.PersistentVector(null, 3, 5, cljs.core.PersistentVector.EMPTY_NODE, [cljs.core.cst$kw$navigation_DASH_2d,cljs.core.cst$kw$position,(0)], null),cljs.core._PLUS_,(dx / zoom)),new cljs.core.PersistentVector(null, 3, 5, cljs.core.PersistentVector.EMPTY_NODE, [cljs.core.cst$kw$navigation_DASH_2d,cljs.core.cst$kw$position,(1)], null),cljs.core._PLUS_,(dy / zoom));
});
/**
* Changes zoom settings based on scroll.
*/
quil.middlewares.navigation_2d.mouse_wheel = (function quil$middlewares$navigation_2d$mouse_wheel(state,event){
quil.middlewares.navigation_2d.assert_state_has_navigation(state);
return cljs.core.update_in.cljs$core$IFn$_invoke$arity$4(state,new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, [cljs.core.cst$kw$navigation_DASH_2d,cljs.core.cst$kw$zoom], null),cljs.core._STAR_,((1) + (-0.1 * event)));
});
/**
* Calls user draw function with all necessary transformations (position
* and zoom) applied.
*/
quil.middlewares.navigation_2d.draw = (function quil$middlewares$navigation_2d$draw(user_draw,state){
quil.middlewares.navigation_2d.assert_state_has_navigation(state);
quil.core.push_matrix();
var nav_2d_6075 = cljs.core.cst$kw$navigation_DASH_2d.cljs$core$IFn$_invoke$arity$1(state);
var zoom_6076 = cljs.core.cst$kw$zoom.cljs$core$IFn$_invoke$arity$1(nav_2d_6075);
var pos_6077 = cljs.core.cst$kw$position.cljs$core$IFn$_invoke$arity$1(nav_2d_6075);
quil.core.scale.cljs$core$IFn$_invoke$arity$1(zoom_6076);
var tr__569__auto___6078 = new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, [(((quil.core.width() / (2)) / zoom_6076) - cljs.core.first(pos_6077)),(((quil.core.height() / (2)) / zoom_6076) - cljs.core.second(pos_6077))], null);
quil.core.push_matrix();
try{quil.core.translate.cljs$core$IFn$_invoke$arity$1(tr__569__auto___6078);
(user_draw.cljs$core$IFn$_invoke$arity$1 ? user_draw.cljs$core$IFn$_invoke$arity$1(state) : user_draw.call(null,state));
}finally {quil.core.pop_matrix();
}
return quil.core.pop_matrix();
});
/**
* Enables navigation over 2D sketch. Dragging mouse will move center of the
* sketch and mouse wheel controls zoom.
*/
quil.middlewares.navigation_2d.navigation_2d = (function quil$middlewares$navigation_2d$navigation_2d(options){
var user_settings = cljs.core.cst$kw$navigation_DASH_2d.cljs$core$IFn$_invoke$arity$1(options);
var user_draw = cljs.core.cst$kw$draw.cljs$core$IFn$_invoke$arity$2(options,((function (user_settings){
return (function (state){
return null;
});})(user_settings))
);
var user_mouse_dragged = cljs.core.cst$kw$mouse_DASH_dragged.cljs$core$IFn$_invoke$arity$2(options,((function (user_settings,user_draw){
return (function (state,_){
return state;
});})(user_settings,user_draw))
);
var user_mouse_wheel = cljs.core.cst$kw$mouse_DASH_wheel.cljs$core$IFn$_invoke$arity$2(options,((function (user_settings,user_draw,user_mouse_dragged){
return (function (state,_){
return state;
});})(user_settings,user_draw,user_mouse_dragged))
);
var setup = cljs.core.cst$kw$setup.cljs$core$IFn$_invoke$arity$2(options,((function (user_settings,user_draw,user_mouse_dragged,user_mouse_wheel){
return (function (){
return cljs.core.PersistentArrayMap.EMPTY;
});})(user_settings,user_draw,user_mouse_dragged,user_mouse_wheel))
);
return cljs.core.assoc.cljs$core$IFn$_invoke$arity$variadic(options,cljs.core.cst$kw$setup,cljs.core.partial.cljs$core$IFn$_invoke$arity$3(quil.middlewares.navigation_2d.setup_2d_nav,setup,user_settings),cljs.core.prim_seq.cljs$core$IFn$_invoke$arity$2([cljs.core.cst$kw$draw,cljs.core.partial.cljs$core$IFn$_invoke$arity$2(quil.middlewares.navigation_2d.draw,user_draw),cljs.core.cst$kw$mouse_DASH_dragged,((function (user_settings,user_draw,user_mouse_dragged,user_mouse_wheel,setup){
return (function (state,event){
var G__6079 = quil.middlewares.navigation_2d.mouse_dragged(state,event);
var G__6080 = event;
return (user_mouse_dragged.cljs$core$IFn$_invoke$arity$2 ? user_mouse_dragged.cljs$core$IFn$_invoke$arity$2(G__6079,G__6080) : user_mouse_dragged.call(null,G__6079,G__6080));
});})(user_settings,user_draw,user_mouse_dragged,user_mouse_wheel,setup))
,cljs.core.cst$kw$mouse_DASH_wheel,((function (user_settings,user_draw,user_mouse_dragged,user_mouse_wheel,setup){
return (function (state,event){
var G__6081 = quil.middlewares.navigation_2d.mouse_wheel(state,event);
var G__6082 = event;
return (user_mouse_wheel.cljs$core$IFn$_invoke$arity$2 ? user_mouse_wheel.cljs$core$IFn$_invoke$arity$2(G__6081,G__6082) : user_mouse_wheel.call(null,G__6081,G__6082));
});})(user_settings,user_draw,user_mouse_dragged,user_mouse_wheel,setup))
], 0));
});

View File

@ -0,0 +1,188 @@
(ns quil.middlewares.navigation-3d
(:require [quil.core :as q]))
(def ^:private ^String missing-navigation-key-error
(str "state map is missing :navigation-3d key. "
"Did you accidentally removed it from the state in "
":update or any other handler?"))
(defn- assert-state-has-navigation
"Asserts that `state` map contains `:navigation-2d` object."
[state]
(when-not (:navigation-3d state)
(throw #?(:clj (RuntimeException. missing-navigation-key-error)
:cljs (js/Error. missing-navigation-key-error)))))
(defn- default-position
"Default position configuration. Check default configuration in
'camera' function."
[]
{:position [(/ (q/width) 2.0)
(/ (q/height) 2.0)
(/ (q/height) 2.0 (q/tan (/ (* q/PI 60.0) 360.0)))]
:straight [0 0 -1]
:up [0 1 0]})
(defn- rotate-by-axis-and-angle
"Rotates vector `v` by `angle` with `axis`.
Formula is taken from wiki:
http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle"
[v axis angle]
(let [[a-x a-y a-z] axis
[x y z] v
cs (q/cos angle)
-cs (- 1 cs)
sn (q/sin angle)
; Matrix is
; [a b c]
; [d e f]
; [g h i]
a (+ cs (* a-x a-x -cs))
b (- (* a-x a-y -cs)
(* a-z sn))
c (+ (* a-x a-z -cs)
(* a-y sn))
d (+ (* a-x a-y -cs)
(* a-z sn))
e (+ cs (* a-y a-y -cs))
f (- (* a-y a-z -cs)
(* a-x sn))
g (- (* a-x a-z -cs)
(* a-y sn))
h (+ (* a-y a-z -cs)
(* a-x sn))
i (+ cs (* a-z a-z -cs))]
[(+ (* a x) (* b y) (* c z))
(+ (* d x) (* e y) (* f z))
(+ (* g x) (* h y) (* i z))]))
(defn- rotate-lr
"Rotates `nav-3d` configuration left-right. `angle` positive - rotate right,
negative - left."
[nav-3d angle]
(update-in nav-3d [:straight] rotate-by-axis-and-angle (:up nav-3d) angle))
(defn- cross-product
"Vector cross-product: http://en.wikipedia.org/wiki/Cross_product"
[[u1 u2 u3] [v1 v2 v3]]
[(- (* u2 v3) (* u3 v2))
(- (* u3 v1) (* u1 v3))
(- (* u1 v2) (* u2 v1))])
(defn- v-mult
"Multiply vector `v` by scalar `mult`."
[v mult]
(mapv #(* % mult) v))
(defn- v-plus
"Sum of 2 vectors."
[v1 v2]
(mapv + v1 v2))
(defn- v-opposite
"Returns vector opposite to vector `v`."
[v]
(v-mult v -1))
(defn- v-normalize
"Normalize vector, returning vector
which has same direction but with norm equals to 1."
[v]
(let [norm (->> (map q/sq v)
(apply +)
(q/sqrt))]
(v-mult v (/ norm))))
(defn- rotate-ud
"Rotates `nav-3d` configuration up-down."
[nav-3d angle]
(let [axis (cross-product (:straight nav-3d) (:up nav-3d))
rotate #(rotate-by-axis-and-angle % axis angle)]
(-> nav-3d
(update-in [:straight] rotate)
(update-in [:up] rotate))))
(defn- rotate
"Mouse handler function which rotates nav-3d configuration.
It uses mouse from `event` object and `pixels-in-360` to calculate
angles to rotate."
[state event pixels-in-360]
(assert-state-has-navigation state)
(if (= 0 (:p-x event) (:p-y event))
state
(let [dx (- (:p-x event) (:x event))
dy (- (:y event) (:p-y event))
angle-lr (q/map-range dx 0 pixels-in-360 0 q/TWO-PI)
angle-ud (q/map-range dy 0 pixels-in-360 0 q/TWO-PI)]
(update-in state [:navigation-3d]
#(-> %
(rotate-lr angle-lr)
(rotate-ud angle-ud))))))
(def ^:private space (keyword " "))
(defn- move
"Keyboard handler function which moves nav-3d configuration.
It uses keyboard key from `event` object to determine in which
direction to move."
[state event step-size]
(assert-state-has-navigation state)
(let [{:keys [up straight]} (:navigation-3d state)]
(if-let [dir (condp = (:key event)
:w straight
:s (v-opposite straight)
space (v-opposite up)
:z up
:d (cross-product straight up)
:a (cross-product up straight)
nil)]
(update-in state [:navigation-3d :position]
#(v-plus % (v-mult dir step-size)))
state)))
(defn- setup-3d-nav
"Custom 'setup' function which creates initial position
configuration and puts it to the state map."
[user-setup user-settings]
(let [initial-state (-> user-settings
(select-keys [:straight :up :position])
(->> (merge (default-position)))
(update-in [:straight] v-normalize)
(update-in [:up] v-normalize))]
(update-in (user-setup) [:navigation-3d]
#(merge initial-state %))))
(defn navigation-3d
"Enables navigation in 3D space. Similar to how it is done in
shooters: WASD navigation, space is go up, z is go down,
drag mouse to look around."
[options]
(let [; 3d-navigation related user settings
user-settings (:navigation-3d options)
pixels-in-360 (:pixels-in-360 user-settings 1000)
step-size (:step-size user-settings 20)
rotate-on (:rotate-on user-settings :mouse-dragged)
; user-provided handlers which will be overridden
; by 3d-navigation
draw (:draw options (fn [state]))
key-pressed (:key-pressed options (fn [state _] state))
rotate-on-fn (rotate-on options (fn [state _] state))
setup (:setup options (fn [] {}))]
(assoc options
:setup (partial setup-3d-nav setup user-settings)
:draw (fn [state]
(assert-state-has-navigation state)
(let [{[c-x c-y c-z] :straight
[u-x u-y u-z] :up
[p-x p-y p-z] :position} (:navigation-3d state)]
(q/camera p-x p-y p-z (+ p-x c-x) (+ p-y c-y) (+ p-z c-z) u-x u-y u-z))
(draw state))
:key-pressed (fn [state event]
(key-pressed (move state event step-size) event))
rotate-on (fn [state event]
(rotate-on-fn (rotate state event pixels-in-360) event)))))

View File

@ -0,0 +1,282 @@
// Compiled by ClojureScript 1.10.520 {:static-fns true, :optimize-constants true}
goog.provide('quil.middlewares.navigation_3d');
goog.require('cljs.core');
goog.require('cljs.core.constants');
goog.require('quil.core');
quil.middlewares.navigation_3d.missing_navigation_key_error = ["state map is missing :navigation-3d key. ","Did you accidentally removed it from the state in ",":update or any other handler?"].join('');
/**
* Asserts that `state` map contains `:navigation-2d` object.
*/
quil.middlewares.navigation_3d.assert_state_has_navigation = (function quil$middlewares$navigation_3d$assert_state_has_navigation(state){
if(cljs.core.truth_(cljs.core.cst$kw$navigation_DASH_3d.cljs$core$IFn$_invoke$arity$1(state))){
return null;
} else {
throw (new Error(quil.middlewares.navigation_3d.missing_navigation_key_error));
}
});
/**
* Default position configuration. Check default configuration in
* 'camera' function.
*/
quil.middlewares.navigation_3d.default_position = (function quil$middlewares$navigation_3d$default_position(){
return new cljs.core.PersistentArrayMap(null, 3, [cljs.core.cst$kw$position,new cljs.core.PersistentVector(null, 3, 5, cljs.core.PersistentVector.EMPTY_NODE, [(quil.core.width() / 2.0),(quil.core.height() / 2.0),((quil.core.height() / 2.0) / quil.core.tan(((quil.core.PI * 60.0) / 360.0)))], null),cljs.core.cst$kw$straight,new cljs.core.PersistentVector(null, 3, 5, cljs.core.PersistentVector.EMPTY_NODE, [(0),(0),(-1)], null),cljs.core.cst$kw$up,new cljs.core.PersistentVector(null, 3, 5, cljs.core.PersistentVector.EMPTY_NODE, [(0),(1),(0)], null)], null);
});
/**
* Rotates vector `v` by `angle` with `axis`.
* Formula is taken from wiki:
* http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle
*/
quil.middlewares.navigation_3d.rotate_by_axis_and_angle = (function quil$middlewares$navigation_3d$rotate_by_axis_and_angle(v,axis,angle){
var vec__6007 = axis;
var a_x = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6007,(0),null);
var a_y = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6007,(1),null);
var a_z = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6007,(2),null);
var vec__6010 = v;
var x = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6010,(0),null);
var y = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6010,(1),null);
var z = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6010,(2),null);
var cs = quil.core.cos(angle);
var _cs = ((1) - cs);
var sn = quil.core.sin(angle);
var a = (cs + ((a_x * a_x) * _cs));
var b = (((a_x * a_y) * _cs) - (a_z * sn));
var c = (((a_x * a_z) * _cs) + (a_y * sn));
var d = (((a_x * a_y) * _cs) + (a_z * sn));
var e = (cs + ((a_y * a_y) * _cs));
var f = (((a_y * a_z) * _cs) - (a_x * sn));
var g = (((a_x * a_z) * _cs) - (a_y * sn));
var h = (((a_y * a_z) * _cs) + (a_x * sn));
var i = (cs + ((a_z * a_z) * _cs));
return new cljs.core.PersistentVector(null, 3, 5, cljs.core.PersistentVector.EMPTY_NODE, [(((a * x) + (b * y)) + (c * z)),(((d * x) + (e * y)) + (f * z)),(((g * x) + (h * y)) + (i * z))], null);
});
/**
* Rotates `nav-3d` configuration left-right. `angle` positive - rotate right,
* negative - left.
*/
quil.middlewares.navigation_3d.rotate_lr = (function quil$middlewares$navigation_3d$rotate_lr(nav_3d,angle){
return cljs.core.update_in.cljs$core$IFn$_invoke$arity$5(nav_3d,new cljs.core.PersistentVector(null, 1, 5, cljs.core.PersistentVector.EMPTY_NODE, [cljs.core.cst$kw$straight], null),quil.middlewares.navigation_3d.rotate_by_axis_and_angle,cljs.core.cst$kw$up.cljs$core$IFn$_invoke$arity$1(nav_3d),angle);
});
/**
* Vector cross-product: http://en.wikipedia.org/wiki/Cross_product
*/
quil.middlewares.navigation_3d.cross_product = (function quil$middlewares$navigation_3d$cross_product(p__6013,p__6014){
var vec__6015 = p__6013;
var u1 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6015,(0),null);
var u2 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6015,(1),null);
var u3 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6015,(2),null);
var vec__6018 = p__6014;
var v1 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6018,(0),null);
var v2 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6018,(1),null);
var v3 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6018,(2),null);
return new cljs.core.PersistentVector(null, 3, 5, cljs.core.PersistentVector.EMPTY_NODE, [((u2 * v3) - (u3 * v2)),((u3 * v1) - (u1 * v3)),((u1 * v2) - (u2 * v1))], null);
});
/**
* Multiply vector `v` by scalar `mult`.
*/
quil.middlewares.navigation_3d.v_mult = (function quil$middlewares$navigation_3d$v_mult(v,mult){
return cljs.core.mapv.cljs$core$IFn$_invoke$arity$2((function (p1__6021_SHARP_){
return (p1__6021_SHARP_ * mult);
}),v);
});
/**
* Sum of 2 vectors.
*/
quil.middlewares.navigation_3d.v_plus = (function quil$middlewares$navigation_3d$v_plus(v1,v2){
return cljs.core.mapv.cljs$core$IFn$_invoke$arity$3(cljs.core._PLUS_,v1,v2);
});
/**
* Returns vector opposite to vector `v`.
*/
quil.middlewares.navigation_3d.v_opposite = (function quil$middlewares$navigation_3d$v_opposite(v){
return quil.middlewares.navigation_3d.v_mult(v,(-1));
});
/**
* Normalize vector, returning vector
* which has same direction but with norm equals to 1.
*/
quil.middlewares.navigation_3d.v_normalize = (function quil$middlewares$navigation_3d$v_normalize(v){
var norm = quil.core.sqrt(cljs.core.apply.cljs$core$IFn$_invoke$arity$2(cljs.core._PLUS_,cljs.core.map.cljs$core$IFn$_invoke$arity$2(quil.core.sq,v)));
return quil.middlewares.navigation_3d.v_mult(v,((1) / norm));
});
/**
* Rotates `nav-3d` configuration up-down.
*/
quil.middlewares.navigation_3d.rotate_ud = (function quil$middlewares$navigation_3d$rotate_ud(nav_3d,angle){
var axis = quil.middlewares.navigation_3d.cross_product(cljs.core.cst$kw$straight.cljs$core$IFn$_invoke$arity$1(nav_3d),cljs.core.cst$kw$up.cljs$core$IFn$_invoke$arity$1(nav_3d));
var rotate = ((function (axis){
return (function (p1__6022_SHARP_){
return quil.middlewares.navigation_3d.rotate_by_axis_and_angle(p1__6022_SHARP_,axis,angle);
});})(axis))
;
return cljs.core.update_in.cljs$core$IFn$_invoke$arity$3(cljs.core.update_in.cljs$core$IFn$_invoke$arity$3(nav_3d,new cljs.core.PersistentVector(null, 1, 5, cljs.core.PersistentVector.EMPTY_NODE, [cljs.core.cst$kw$straight], null),rotate),new cljs.core.PersistentVector(null, 1, 5, cljs.core.PersistentVector.EMPTY_NODE, [cljs.core.cst$kw$up], null),rotate);
});
/**
* Mouse handler function which rotates nav-3d configuration.
* It uses mouse from `event` object and `pixels-in-360` to calculate
* angles to rotate.
*/
quil.middlewares.navigation_3d.rotate = (function quil$middlewares$navigation_3d$rotate(state,event,pixels_in_360){
quil.middlewares.navigation_3d.assert_state_has_navigation(state);
if(cljs.core._EQ_.cljs$core$IFn$_invoke$arity$variadic((0),cljs.core.cst$kw$p_DASH_x.cljs$core$IFn$_invoke$arity$1(event),cljs.core.prim_seq.cljs$core$IFn$_invoke$arity$2([cljs.core.cst$kw$p_DASH_y.cljs$core$IFn$_invoke$arity$1(event)], 0))){
return state;
} else {
var dx = (cljs.core.cst$kw$p_DASH_x.cljs$core$IFn$_invoke$arity$1(event) - cljs.core.cst$kw$x.cljs$core$IFn$_invoke$arity$1(event));
var dy = (cljs.core.cst$kw$y.cljs$core$IFn$_invoke$arity$1(event) - cljs.core.cst$kw$p_DASH_y.cljs$core$IFn$_invoke$arity$1(event));
var angle_lr = quil.core.map_range(dx,(0),pixels_in_360,(0),quil.core.TWO_PI);
var angle_ud = quil.core.map_range(dy,(0),pixels_in_360,(0),quil.core.TWO_PI);
return cljs.core.update_in.cljs$core$IFn$_invoke$arity$3(state,new cljs.core.PersistentVector(null, 1, 5, cljs.core.PersistentVector.EMPTY_NODE, [cljs.core.cst$kw$navigation_DASH_3d], null),((function (dx,dy,angle_lr,angle_ud){
return (function (p1__6023_SHARP_){
return quil.middlewares.navigation_3d.rotate_ud(quil.middlewares.navigation_3d.rotate_lr(p1__6023_SHARP_,angle_lr),angle_ud);
});})(dx,dy,angle_lr,angle_ud))
);
}
});
quil.middlewares.navigation_3d.space = cljs.core.keyword.cljs$core$IFn$_invoke$arity$1(" ");
/**
* Keyboard handler function which moves nav-3d configuration.
* It uses keyboard key from `event` object to determine in which
* direction to move.
*/
quil.middlewares.navigation_3d.move = (function quil$middlewares$navigation_3d$move(state,event,step_size){
quil.middlewares.navigation_3d.assert_state_has_navigation(state);
var map__6025 = cljs.core.cst$kw$navigation_DASH_3d.cljs$core$IFn$_invoke$arity$1(state);
var map__6025__$1 = (((((!((map__6025 == null))))?(((((map__6025.cljs$lang$protocol_mask$partition0$ & (64))) || ((cljs.core.PROTOCOL_SENTINEL === map__6025.cljs$core$ISeq$))))?true:false):false))?cljs.core.apply.cljs$core$IFn$_invoke$arity$2(cljs.core.hash_map,map__6025):map__6025);
var up = cljs.core.get.cljs$core$IFn$_invoke$arity$2(map__6025__$1,cljs.core.cst$kw$up);
var straight = cljs.core.get.cljs$core$IFn$_invoke$arity$2(map__6025__$1,cljs.core.cst$kw$straight);
var temp__5733__auto__ = (function (){var pred__6027 = cljs.core._EQ_;
var expr__6028 = cljs.core.cst$kw$key.cljs$core$IFn$_invoke$arity$1(event);
if(cljs.core.truth_((function (){var G__6030 = cljs.core.cst$kw$w;
var G__6031 = expr__6028;
return (pred__6027.cljs$core$IFn$_invoke$arity$2 ? pred__6027.cljs$core$IFn$_invoke$arity$2(G__6030,G__6031) : pred__6027.call(null,G__6030,G__6031));
})())){
return straight;
} else {
if(cljs.core.truth_((function (){var G__6032 = cljs.core.cst$kw$s;
var G__6033 = expr__6028;
return (pred__6027.cljs$core$IFn$_invoke$arity$2 ? pred__6027.cljs$core$IFn$_invoke$arity$2(G__6032,G__6033) : pred__6027.call(null,G__6032,G__6033));
})())){
return quil.middlewares.navigation_3d.v_opposite(straight);
} else {
if(cljs.core.truth_((pred__6027.cljs$core$IFn$_invoke$arity$2 ? pred__6027.cljs$core$IFn$_invoke$arity$2(quil.middlewares.navigation_3d.space,expr__6028) : pred__6027.call(null,quil.middlewares.navigation_3d.space,expr__6028)))){
return quil.middlewares.navigation_3d.v_opposite(up);
} else {
if(cljs.core.truth_((function (){var G__6034 = cljs.core.cst$kw$z;
var G__6035 = expr__6028;
return (pred__6027.cljs$core$IFn$_invoke$arity$2 ? pred__6027.cljs$core$IFn$_invoke$arity$2(G__6034,G__6035) : pred__6027.call(null,G__6034,G__6035));
})())){
return up;
} else {
if(cljs.core.truth_((function (){var G__6036 = cljs.core.cst$kw$d;
var G__6037 = expr__6028;
return (pred__6027.cljs$core$IFn$_invoke$arity$2 ? pred__6027.cljs$core$IFn$_invoke$arity$2(G__6036,G__6037) : pred__6027.call(null,G__6036,G__6037));
})())){
return quil.middlewares.navigation_3d.cross_product(straight,up);
} else {
if(cljs.core.truth_((function (){var G__6038 = cljs.core.cst$kw$a;
var G__6039 = expr__6028;
return (pred__6027.cljs$core$IFn$_invoke$arity$2 ? pred__6027.cljs$core$IFn$_invoke$arity$2(G__6038,G__6039) : pred__6027.call(null,G__6038,G__6039));
})())){
return quil.middlewares.navigation_3d.cross_product(up,straight);
} else {
return null;
}
}
}
}
}
}
})();
if(cljs.core.truth_(temp__5733__auto__)){
var dir = temp__5733__auto__;
return cljs.core.update_in.cljs$core$IFn$_invoke$arity$3(state,new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, [cljs.core.cst$kw$navigation_DASH_3d,cljs.core.cst$kw$position], null),((function (dir,temp__5733__auto__,map__6025,map__6025__$1,up,straight){
return (function (p1__6024_SHARP_){
return quil.middlewares.navigation_3d.v_plus(p1__6024_SHARP_,quil.middlewares.navigation_3d.v_mult(dir,step_size));
});})(dir,temp__5733__auto__,map__6025,map__6025__$1,up,straight))
);
} else {
return state;
}
});
/**
* Custom 'setup' function which creates initial position
* configuration and puts it to the state map.
*/
quil.middlewares.navigation_3d.setup_3d_nav = (function quil$middlewares$navigation_3d$setup_3d_nav(user_setup,user_settings){
var initial_state = cljs.core.update_in.cljs$core$IFn$_invoke$arity$3(cljs.core.update_in.cljs$core$IFn$_invoke$arity$3(cljs.core.merge.cljs$core$IFn$_invoke$arity$variadic(cljs.core.prim_seq.cljs$core$IFn$_invoke$arity$2([quil.middlewares.navigation_3d.default_position(),cljs.core.select_keys(user_settings,new cljs.core.PersistentVector(null, 3, 5, cljs.core.PersistentVector.EMPTY_NODE, [cljs.core.cst$kw$straight,cljs.core.cst$kw$up,cljs.core.cst$kw$position], null))], 0)),new cljs.core.PersistentVector(null, 1, 5, cljs.core.PersistentVector.EMPTY_NODE, [cljs.core.cst$kw$straight], null),quil.middlewares.navigation_3d.v_normalize),new cljs.core.PersistentVector(null, 1, 5, cljs.core.PersistentVector.EMPTY_NODE, [cljs.core.cst$kw$up], null),quil.middlewares.navigation_3d.v_normalize);
return cljs.core.update_in.cljs$core$IFn$_invoke$arity$3((user_setup.cljs$core$IFn$_invoke$arity$0 ? user_setup.cljs$core$IFn$_invoke$arity$0() : user_setup.call(null)),new cljs.core.PersistentVector(null, 1, 5, cljs.core.PersistentVector.EMPTY_NODE, [cljs.core.cst$kw$navigation_DASH_3d], null),((function (initial_state){
return (function (p1__6040_SHARP_){
return cljs.core.merge.cljs$core$IFn$_invoke$arity$variadic(cljs.core.prim_seq.cljs$core$IFn$_invoke$arity$2([initial_state,p1__6040_SHARP_], 0));
});})(initial_state))
);
});
/**
* Enables navigation in 3D space. Similar to how it is done in
* shooters: WASD navigation, space is go up, z is go down,
* drag mouse to look around.
*/
quil.middlewares.navigation_3d.navigation_3d = (function quil$middlewares$navigation_3d$navigation_3d(options){
var user_settings = cljs.core.cst$kw$navigation_DASH_3d.cljs$core$IFn$_invoke$arity$1(options);
var pixels_in_360 = cljs.core.cst$kw$pixels_DASH_in_DASH_360.cljs$core$IFn$_invoke$arity$2(user_settings,(1000));
var step_size = cljs.core.cst$kw$step_DASH_size.cljs$core$IFn$_invoke$arity$2(user_settings,(20));
var rotate_on = cljs.core.cst$kw$rotate_DASH_on.cljs$core$IFn$_invoke$arity$2(user_settings,cljs.core.cst$kw$mouse_DASH_dragged);
var draw = cljs.core.cst$kw$draw.cljs$core$IFn$_invoke$arity$2(options,((function (user_settings,pixels_in_360,step_size,rotate_on){
return (function (state){
return null;
});})(user_settings,pixels_in_360,step_size,rotate_on))
);
var key_pressed = cljs.core.cst$kw$key_DASH_pressed.cljs$core$IFn$_invoke$arity$2(options,((function (user_settings,pixels_in_360,step_size,rotate_on,draw){
return (function (state,_){
return state;
});})(user_settings,pixels_in_360,step_size,rotate_on,draw))
);
var rotate_on_fn = (function (){var G__6041 = options;
var G__6042 = ((function (G__6041,user_settings,pixels_in_360,step_size,rotate_on,draw,key_pressed){
return (function (state,_){
return state;
});})(G__6041,user_settings,pixels_in_360,step_size,rotate_on,draw,key_pressed))
;
return (rotate_on.cljs$core$IFn$_invoke$arity$2 ? rotate_on.cljs$core$IFn$_invoke$arity$2(G__6041,G__6042) : rotate_on.call(null,G__6041,G__6042));
})();
var setup = cljs.core.cst$kw$setup.cljs$core$IFn$_invoke$arity$2(options,((function (user_settings,pixels_in_360,step_size,rotate_on,draw,key_pressed,rotate_on_fn){
return (function (){
return cljs.core.PersistentArrayMap.EMPTY;
});})(user_settings,pixels_in_360,step_size,rotate_on,draw,key_pressed,rotate_on_fn))
);
return cljs.core.assoc.cljs$core$IFn$_invoke$arity$variadic(options,cljs.core.cst$kw$setup,cljs.core.partial.cljs$core$IFn$_invoke$arity$3(quil.middlewares.navigation_3d.setup_3d_nav,setup,user_settings),cljs.core.prim_seq.cljs$core$IFn$_invoke$arity$2([cljs.core.cst$kw$draw,((function (user_settings,pixels_in_360,step_size,rotate_on,draw,key_pressed,rotate_on_fn,setup){
return (function (state){
quil.middlewares.navigation_3d.assert_state_has_navigation(state);
var map__6043_6058 = cljs.core.cst$kw$navigation_DASH_3d.cljs$core$IFn$_invoke$arity$1(state);
var map__6043_6059__$1 = (((((!((map__6043_6058 == null))))?(((((map__6043_6058.cljs$lang$protocol_mask$partition0$ & (64))) || ((cljs.core.PROTOCOL_SENTINEL === map__6043_6058.cljs$core$ISeq$))))?true:false):false))?cljs.core.apply.cljs$core$IFn$_invoke$arity$2(cljs.core.hash_map,map__6043_6058):map__6043_6058);
var vec__6044_6060 = cljs.core.get.cljs$core$IFn$_invoke$arity$2(map__6043_6059__$1,cljs.core.cst$kw$straight);
var c_x_6061 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6044_6060,(0),null);
var c_y_6062 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6044_6060,(1),null);
var c_z_6063 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6044_6060,(2),null);
var vec__6047_6064 = cljs.core.get.cljs$core$IFn$_invoke$arity$2(map__6043_6059__$1,cljs.core.cst$kw$up);
var u_x_6065 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6047_6064,(0),null);
var u_y_6066 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6047_6064,(1),null);
var u_z_6067 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6047_6064,(2),null);
var vec__6050_6068 = cljs.core.get.cljs$core$IFn$_invoke$arity$2(map__6043_6059__$1,cljs.core.cst$kw$position);
var p_x_6069 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6050_6068,(0),null);
var p_y_6070 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6050_6068,(1),null);
var p_z_6071 = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__6050_6068,(2),null);
quil.core.camera.cljs$core$IFn$_invoke$arity$9(p_x_6069,p_y_6070,p_z_6071,(p_x_6069 + c_x_6061),(p_y_6070 + c_y_6062),(p_z_6071 + c_z_6063),u_x_6065,u_y_6066,u_z_6067);
return (draw.cljs$core$IFn$_invoke$arity$1 ? draw.cljs$core$IFn$_invoke$arity$1(state) : draw.call(null,state));
});})(user_settings,pixels_in_360,step_size,rotate_on,draw,key_pressed,rotate_on_fn,setup))
,cljs.core.cst$kw$key_DASH_pressed,((function (user_settings,pixels_in_360,step_size,rotate_on,draw,key_pressed,rotate_on_fn,setup){
return (function (state,event){
var G__6054 = quil.middlewares.navigation_3d.move(state,event,step_size);
var G__6055 = event;
return (key_pressed.cljs$core$IFn$_invoke$arity$2 ? key_pressed.cljs$core$IFn$_invoke$arity$2(G__6054,G__6055) : key_pressed.call(null,G__6054,G__6055));
});})(user_settings,pixels_in_360,step_size,rotate_on,draw,key_pressed,rotate_on_fn,setup))
,rotate_on,((function (user_settings,pixels_in_360,step_size,rotate_on,draw,key_pressed,rotate_on_fn,setup){
return (function (state,event){
var G__6056 = quil.middlewares.navigation_3d.rotate(state,event,pixels_in_360);
var G__6057 = event;
return (rotate_on_fn.cljs$core$IFn$_invoke$arity$2 ? rotate_on_fn.cljs$core$IFn$_invoke$arity$2(G__6056,G__6057) : rotate_on_fn.call(null,G__6056,G__6057));
});})(user_settings,pixels_in_360,step_size,rotate_on,draw,key_pressed,rotate_on_fn,setup))
], 0));
});

View File

@ -0,0 +1,191 @@
(ns quil.sketch
(:require [quil.util :as u :include-macros true]
[quil.middlewares.deprecated-options :as do]
[goog.dom :as dom]
[goog.events :as events]
[goog.style :as style]
[goog.object :as object]
[goog.events.EventType :as EventType])
(:require-macros [quil.sketch]))
(def ^:dynamic
*applet* nil)
(defn current-applet [] *applet*)
(u/generate-quil-constants :cljs
rendering-modes (:java2d :p2d :p3d :opengl))
(defn resolve-renderer [mode]
(if (= :p3d mode)
(aget js/p5.prototype "WEBGL")
(u/resolve-constant-key mode rendering-modes)))
(defn set-size [applet width height]
(when-let [el (.-quil-canvas applet)]
; p5js creates a <canvas> element inside provided <div> element
; we need to resize only the canvas as outer div will adapt automatically
(let [inner-canvas (.querySelector el "canvas")]
(.resizeCanvas applet width height)
(.setAttribute inner-canvas "width" width)
(.setAttribute inner-canvas "height" height)
(aset (.-style inner-canvas) "width" (str width "px"))
(aset (.-style inner-canvas) "height" (str height "px"))
(set! (.-width applet)
(.parseInt js/window (style/getComputedStyle inner-canvas "width")))
(set! (.-height applet)
(.parseInt js/window (style/getComputedStyle inner-canvas "height"))))))
(defn size
([width height]
(.createCanvas (current-applet) (int width) (int height)))
([width height mode]
(.createCanvas (current-applet) (int width) (int height) (resolve-renderer mode))))
(defn- bind-handlers [prc opts]
(doseq [[processing-name quil-name] {:setup :setup
:draw :draw
:keyPressed :key-pressed
:keyReleased :key-released
:keyTyped :key-typed
:mouseClicked :mouse-clicked
:mouseDragged :mouse-dragged
:mouseMoved :mouse-moved
:mousePressed :mouse-pressed
:mouseReleased :mouse-released
:mouseOut :mouse-exited
:mouseOver :mouse-entered
:mouseWheel :mouse-wheel}]
(when-let [handler (opts quil-name)]
(aset prc (name processing-name)
(fn [& args]
(quil.sketch/with-sketch prc
(apply handler args)))))))
(defn in-fullscreen? []
(or (.-fullscreenElement js/document)
(.-mozFullScreenElement js/document)))
(defn add-fullscreen-support
"Adds fullscreen support for the provided `p5` object.
Fullscreen is enabled when the user presses `F11`. We turn
the sketch `<canvas>` element to fullscreen storing the old size
in an `atom`. When the user cancels fullscreen (`F11` or `Esc`)
we resize the sketch back to the old size."
[applet]
(let [old-size (atom nil)
adjust-canvas-size
(fn []
(if (in-fullscreen?)
(do
(reset! old-size
[(.-width applet) (.-height applet)])
(set-size applet
(-> js/window .-screen .-width)
(-> js/window .-screen .-height)))
(apply set-size applet @old-size)))]
(events/listen js/window EventType/KEYDOWN
(fn [event]
(when (and (= (.-key event) "F11")
(not (in-fullscreen?)))
(.preventDefault event)
(let [canvas (.-quil-canvas applet)]
(cond (.-requestFullscreen canvas) (.requestFullscreen canvas)
(.-mozRequestFullScreen canvas) (.mozRequestFullScreen canvas)
:else (.warn js/console "Fullscreen mode is not supported in current browser."))))))
(events/listen js/document "fullscreenchange" adjust-canvas-size)
(events/listen js/document "mozfullscreenchange" adjust-canvas-size)
(events/listen js/document "fullscreenerror"
#(.error js/console "Error while switching to/from fullscreen: " %))))
(defn make-sketch [options]
(let [opts (->> (:middleware options [])
(cons do/deprecated-options)
(apply comp)
(#(% options))
(merge {:size [500 300]}))
sketch-size (:size opts)
renderer (:renderer opts)
features (set (:features opts))
setup (fn []
(->> (if renderer [renderer] [])
(concat sketch-size)
(apply size))
(when (:settings opts) ((:settings opts)))
(when (:setup opts) ((:setup opts))))
mouse-wheel (when-let [wheel-handler (:mouse-wheel opts)]
; using (get "delta") because in advanced mode
; it will be renamed otherwise.
(fn [evt] (wheel-handler (object/get evt "delta"))))
opts (assoc opts
:setup setup
:mouse-wheel mouse-wheel)
sketch (fn [prc]
(bind-handlers prc opts)
(set! (.-quil prc) (atom nil))
(set! (.-quil-internal-state prc) (atom u/initial-internal-state)))]
sketch))
(defn destroy-previous-sketch [host-elem]
(when-let [proc-obj (.-processing-obj host-elem)]
(.remove proc-obj)))
(defn sketch [& opts]
(let [opts-map (apply hash-map opts)
host-elem (:host opts-map)
renderer (or (:renderer opts-map) :p2d)
host-elem (if (string? host-elem)
(.getElementById js/document host-elem)
host-elem)]
(if host-elem
(do
(if (.-processing-context host-elem)
(when-not (= renderer (.-processing-context host-elem))
(.warn js/console "WARNING: Using different context on one canvas!"))
(set! (.-processing-context host-elem) renderer))
(destroy-previous-sketch host-elem)
(let [proc-obj (js/p5. (make-sketch opts-map) host-elem)]
(set! (.-processing-obj host-elem) proc-obj)
(set! (.-quil-canvas proc-obj) host-elem)
(add-fullscreen-support proc-obj)
proc-obj))
(.error js/console
(if (:host opts-map)
(str "ERROR: Cannot find host element: " (:host opts-map))
"ERROR: Cannot create sketch. :host is not specified or element not found.")))))
(def sketch-init-list (atom (list)))
(defn empty-body? []
(let [child (.-childNodes (.-body js/document))]
; seems hacky, we should come up with better way of
; checking whether body is empty or not
(<= (.-length child) 1)))
(defn add-canvas [canvas-id]
(let [canvas (.createElement js/document "canvas")]
(.setAttribute canvas "id" canvas-id)
(.appendChild (.-body js/document) canvas)))
(defn init-sketches []
(let [add-elem? (empty-body?)]
(doseq [sk @sketch-init-list]
(when add-elem?
(add-canvas (:host-id sk)))
((:fn sk))))
(reset! sketch-init-list []))
(defn add-sketch-to-init-list [sk]
(swap! sketch-init-list conj sk)
; if page already loaded immediately init sketch we just added
(when (= (.-readyState js/document) "complete")
(init-sketches)))
(events/listenOnce js/window EventType/LOAD init-sketches)

View File

@ -0,0 +1,483 @@
// Compiled by ClojureScript 1.10.520 {:static-fns true, :optimize-constants true}
goog.provide('quil.sketch');
goog.require('cljs.core');
goog.require('cljs.core.constants');
goog.require('quil.util');
goog.require('quil.middlewares.deprecated_options');
goog.require('goog.dom');
goog.require('goog.events');
goog.require('goog.style');
goog.require('goog.object');
goog.require('goog.events.EventType');
quil.sketch._STAR_applet_STAR_ = null;
quil.sketch.current_applet = (function quil$sketch$current_applet(){
return quil.sketch._STAR_applet_STAR_;
});
quil.sketch.rendering_modes = new cljs.core.PersistentArrayMap(null, 4, [cljs.core.cst$kw$java2d,(p5.prototype["JAVA2D"]),cljs.core.cst$kw$p2d,(p5.prototype["P2D"]),cljs.core.cst$kw$p3d,(p5.prototype["P3D"]),cljs.core.cst$kw$opengl,(p5.prototype["OPENGL"])], null);
quil.sketch.resolve_renderer = (function quil$sketch$resolve_renderer(mode){
if(cljs.core._EQ_.cljs$core$IFn$_invoke$arity$2(cljs.core.cst$kw$p3d,mode)){
return (p5.prototype["WEBGL"]);
} else {
return quil.util.resolve_constant_key(mode,quil.sketch.rendering_modes);
}
});
quil.sketch.set_size = (function quil$sketch$set_size(applet,width,height){
var temp__5735__auto__ = applet.quil_canvas;
if(cljs.core.truth_(temp__5735__auto__)){
var el = temp__5735__auto__;
var inner_canvas = el.querySelector("canvas");
applet.resizeCanvas(width,height);
inner_canvas.setAttribute("width",width);
inner_canvas.setAttribute("height",height);
(inner_canvas.style["width"] = [cljs.core.str.cljs$core$IFn$_invoke$arity$1(width),"px"].join(''));
(inner_canvas.style["height"] = [cljs.core.str.cljs$core$IFn$_invoke$arity$1(height),"px"].join(''));
applet.width = window.parseInt(goog.style.getComputedStyle(inner_canvas,"width"));
return applet.height = window.parseInt(goog.style.getComputedStyle(inner_canvas,"height"));
} else {
return null;
}
});
quil.sketch.size = (function quil$sketch$size(var_args){
var G__5592 = arguments.length;
switch (G__5592) {
case 2:
return quil.sketch.size.cljs$core$IFn$_invoke$arity$2((arguments[(0)]),(arguments[(1)]));
break;
case 3:
return quil.sketch.size.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('')));
}
});
quil.sketch.size.cljs$core$IFn$_invoke$arity$2 = (function (width,height){
return quil.sketch.current_applet().createCanvas((width | (0)),(height | (0)));
});
quil.sketch.size.cljs$core$IFn$_invoke$arity$3 = (function (width,height,mode){
return quil.sketch.current_applet().createCanvas((width | (0)),(height | (0)),quil.sketch.resolve_renderer(mode));
});
quil.sketch.size.cljs$lang$maxFixedArity = 3;
quil.sketch.bind_handlers = (function quil$sketch$bind_handlers(prc,opts){
var seq__5594 = cljs.core.seq(cljs.core.PersistentHashMap.fromArrays([cljs.core.cst$kw$keyPressed,cljs.core.cst$kw$mouseOut,cljs.core.cst$kw$mouseDragged,cljs.core.cst$kw$setup,cljs.core.cst$kw$mouseWheel,cljs.core.cst$kw$keyReleased,cljs.core.cst$kw$mouseClicked,cljs.core.cst$kw$mouseReleased,cljs.core.cst$kw$mousePressed,cljs.core.cst$kw$mouseMoved,cljs.core.cst$kw$mouseOver,cljs.core.cst$kw$keyTyped,cljs.core.cst$kw$draw],[cljs.core.cst$kw$key_DASH_pressed,cljs.core.cst$kw$mouse_DASH_exited,cljs.core.cst$kw$mouse_DASH_dragged,cljs.core.cst$kw$setup,cljs.core.cst$kw$mouse_DASH_wheel,cljs.core.cst$kw$key_DASH_released,cljs.core.cst$kw$mouse_DASH_clicked,cljs.core.cst$kw$mouse_DASH_released,cljs.core.cst$kw$mouse_DASH_pressed,cljs.core.cst$kw$mouse_DASH_moved,cljs.core.cst$kw$mouse_DASH_entered,cljs.core.cst$kw$key_DASH_typed,cljs.core.cst$kw$draw]));
var chunk__5595 = null;
var count__5596 = (0);
var i__5597 = (0);
while(true){
if((i__5597 < count__5596)){
var vec__5608 = chunk__5595.cljs$core$IIndexed$_nth$arity$2(null,i__5597);
var processing_name = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__5608,(0),null);
var quil_name = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__5608,(1),null);
var temp__5735__auto___5618 = (opts.cljs$core$IFn$_invoke$arity$1 ? opts.cljs$core$IFn$_invoke$arity$1(quil_name) : opts.call(null,quil_name));
if(cljs.core.truth_(temp__5735__auto___5618)){
var handler_5619 = temp__5735__auto___5618;
(prc[cljs.core.name(processing_name)] = ((function (seq__5594,chunk__5595,count__5596,i__5597,handler_5619,temp__5735__auto___5618,vec__5608,processing_name,quil_name){
return (function() {
var G__5620__delegate = function (args){
var _STAR_applet_STAR__orig_val__5611 = quil.sketch._STAR_applet_STAR_;
var _STAR_applet_STAR__temp_val__5612 = prc;
quil.sketch._STAR_applet_STAR_ = _STAR_applet_STAR__temp_val__5612;
try{return cljs.core.apply.cljs$core$IFn$_invoke$arity$2(handler_5619,args);
}finally {quil.sketch._STAR_applet_STAR_ = _STAR_applet_STAR__orig_val__5611;
}};
var G__5620 = function (var_args){
var args = null;
if (arguments.length > 0) {
var G__5621__i = 0, G__5621__a = new Array(arguments.length - 0);
while (G__5621__i < G__5621__a.length) {G__5621__a[G__5621__i] = arguments[G__5621__i + 0]; ++G__5621__i;}
args = new cljs.core.IndexedSeq(G__5621__a,0,null);
}
return G__5620__delegate.call(this,args);};
G__5620.cljs$lang$maxFixedArity = 0;
G__5620.cljs$lang$applyTo = (function (arglist__5622){
var args = cljs.core.seq(arglist__5622);
return G__5620__delegate(args);
});
G__5620.cljs$core$IFn$_invoke$arity$variadic = G__5620__delegate;
return G__5620;
})()
;})(seq__5594,chunk__5595,count__5596,i__5597,handler_5619,temp__5735__auto___5618,vec__5608,processing_name,quil_name))
);
} else {
}
var G__5623 = seq__5594;
var G__5624 = chunk__5595;
var G__5625 = count__5596;
var G__5626 = (i__5597 + (1));
seq__5594 = G__5623;
chunk__5595 = G__5624;
count__5596 = G__5625;
i__5597 = G__5626;
continue;
} else {
var temp__5735__auto__ = cljs.core.seq(seq__5594);
if(temp__5735__auto__){
var seq__5594__$1 = temp__5735__auto__;
if(cljs.core.chunked_seq_QMARK_(seq__5594__$1)){
var c__4550__auto__ = cljs.core.chunk_first(seq__5594__$1);
var G__5627 = cljs.core.chunk_rest(seq__5594__$1);
var G__5628 = c__4550__auto__;
var G__5629 = cljs.core.count(c__4550__auto__);
var G__5630 = (0);
seq__5594 = G__5627;
chunk__5595 = G__5628;
count__5596 = G__5629;
i__5597 = G__5630;
continue;
} else {
var vec__5613 = cljs.core.first(seq__5594__$1);
var processing_name = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__5613,(0),null);
var quil_name = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__5613,(1),null);
var temp__5735__auto___5631__$1 = (opts.cljs$core$IFn$_invoke$arity$1 ? opts.cljs$core$IFn$_invoke$arity$1(quil_name) : opts.call(null,quil_name));
if(cljs.core.truth_(temp__5735__auto___5631__$1)){
var handler_5632 = temp__5735__auto___5631__$1;
(prc[cljs.core.name(processing_name)] = ((function (seq__5594,chunk__5595,count__5596,i__5597,handler_5632,temp__5735__auto___5631__$1,vec__5613,processing_name,quil_name,seq__5594__$1,temp__5735__auto__){
return (function() {
var G__5633__delegate = function (args){
var _STAR_applet_STAR__orig_val__5616 = quil.sketch._STAR_applet_STAR_;
var _STAR_applet_STAR__temp_val__5617 = prc;
quil.sketch._STAR_applet_STAR_ = _STAR_applet_STAR__temp_val__5617;
try{return cljs.core.apply.cljs$core$IFn$_invoke$arity$2(handler_5632,args);
}finally {quil.sketch._STAR_applet_STAR_ = _STAR_applet_STAR__orig_val__5616;
}};
var G__5633 = function (var_args){
var args = null;
if (arguments.length > 0) {
var G__5634__i = 0, G__5634__a = new Array(arguments.length - 0);
while (G__5634__i < G__5634__a.length) {G__5634__a[G__5634__i] = arguments[G__5634__i + 0]; ++G__5634__i;}
args = new cljs.core.IndexedSeq(G__5634__a,0,null);
}
return G__5633__delegate.call(this,args);};
G__5633.cljs$lang$maxFixedArity = 0;
G__5633.cljs$lang$applyTo = (function (arglist__5635){
var args = cljs.core.seq(arglist__5635);
return G__5633__delegate(args);
});
G__5633.cljs$core$IFn$_invoke$arity$variadic = G__5633__delegate;
return G__5633;
})()
;})(seq__5594,chunk__5595,count__5596,i__5597,handler_5632,temp__5735__auto___5631__$1,vec__5613,processing_name,quil_name,seq__5594__$1,temp__5735__auto__))
);
} else {
}
var G__5636 = cljs.core.next(seq__5594__$1);
var G__5637 = null;
var G__5638 = (0);
var G__5639 = (0);
seq__5594 = G__5636;
chunk__5595 = G__5637;
count__5596 = G__5638;
i__5597 = G__5639;
continue;
}
} else {
return null;
}
}
break;
}
});
quil.sketch.in_fullscreen_QMARK_ = (function quil$sketch$in_fullscreen_QMARK_(){
var or__4131__auto__ = document.fullscreenElement;
if(cljs.core.truth_(or__4131__auto__)){
return or__4131__auto__;
} else {
return document.mozFullScreenElement;
}
});
/**
* Adds fullscreen support for the provided `p5` object.
* Fullscreen is enabled when the user presses `F11`. We turn
* the sketch `<canvas>` element to fullscreen storing the old size
* in an `atom`. When the user cancels fullscreen (`F11` or `Esc`)
* we resize the sketch back to the old size.
*/
quil.sketch.add_fullscreen_support = (function quil$sketch$add_fullscreen_support(applet){
var old_size = cljs.core.atom.cljs$core$IFn$_invoke$arity$1(null);
var adjust_canvas_size = ((function (old_size){
return (function (){
if(cljs.core.truth_(quil.sketch.in_fullscreen_QMARK_())){
cljs.core.reset_BANG_(old_size,new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, [applet.width,applet.height], null));
return quil.sketch.set_size(applet,window.screen.width,window.screen.height);
} else {
return cljs.core.apply.cljs$core$IFn$_invoke$arity$3(quil.sketch.set_size,applet,cljs.core.deref(old_size));
}
});})(old_size))
;
var G__5641_5647 = window;
var G__5642_5648 = goog.events.EventType.KEYDOWN;
var G__5643_5649 = ((function (G__5641_5647,G__5642_5648,old_size,adjust_canvas_size){
return (function (event){
if(((cljs.core._EQ_.cljs$core$IFn$_invoke$arity$2(event.key,"F11")) && (cljs.core.not(quil.sketch.in_fullscreen_QMARK_())))){
event.preventDefault();
var canvas = applet.quil_canvas;
if(cljs.core.truth_(canvas.requestFullscreen)){
return canvas.requestFullscreen();
} else {
if(cljs.core.truth_(canvas.mozRequestFullScreen)){
return canvas.mozRequestFullScreen();
} else {
return console.warn("Fullscreen mode is not supported in current browser.");
}
}
} else {
return null;
}
});})(G__5641_5647,G__5642_5648,old_size,adjust_canvas_size))
;
goog.events.listen(G__5641_5647,G__5642_5648,G__5643_5649);
goog.events.listen(document,"fullscreenchange",adjust_canvas_size);
goog.events.listen(document,"mozfullscreenchange",adjust_canvas_size);
var G__5644 = document;
var G__5645 = "fullscreenerror";
var G__5646 = ((function (G__5644,G__5645,old_size,adjust_canvas_size){
return (function (p1__5640_SHARP_){
return console.error("Error while switching to/from fullscreen: ",p1__5640_SHARP_);
});})(G__5644,G__5645,old_size,adjust_canvas_size))
;
return goog.events.listen(G__5644,G__5645,G__5646);
});
quil.sketch.make_sketch = (function quil$sketch$make_sketch(options){
var opts = cljs.core.merge.cljs$core$IFn$_invoke$arity$variadic(cljs.core.prim_seq.cljs$core$IFn$_invoke$arity$2([new cljs.core.PersistentArrayMap(null, 1, [cljs.core.cst$kw$size,new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, [(500),(300)], null)], null),(function (){var G__5652 = cljs.core.apply.cljs$core$IFn$_invoke$arity$2(cljs.core.comp,cljs.core.cons(quil.middlewares.deprecated_options.deprecated_options,cljs.core.cst$kw$middleware.cljs$core$IFn$_invoke$arity$2(options,cljs.core.PersistentVector.EMPTY)));
var fexpr__5651 = ((function (G__5652){
return (function (p1__5650_SHARP_){
return (p1__5650_SHARP_.cljs$core$IFn$_invoke$arity$1 ? p1__5650_SHARP_.cljs$core$IFn$_invoke$arity$1(options) : p1__5650_SHARP_.call(null,options));
});})(G__5652))
;
return fexpr__5651(G__5652);
})()], 0));
var sketch_size = cljs.core.cst$kw$size.cljs$core$IFn$_invoke$arity$1(opts);
var renderer = cljs.core.cst$kw$renderer.cljs$core$IFn$_invoke$arity$1(opts);
var features = cljs.core.set(cljs.core.cst$kw$features.cljs$core$IFn$_invoke$arity$1(opts));
var setup = ((function (opts,sketch_size,renderer,features){
return (function (){
cljs.core.apply.cljs$core$IFn$_invoke$arity$2(quil.sketch.size,cljs.core.concat.cljs$core$IFn$_invoke$arity$2(sketch_size,(cljs.core.truth_(renderer)?new cljs.core.PersistentVector(null, 1, 5, cljs.core.PersistentVector.EMPTY_NODE, [renderer], null):cljs.core.PersistentVector.EMPTY)));
if(cljs.core.truth_(cljs.core.cst$kw$settings.cljs$core$IFn$_invoke$arity$1(opts))){
var fexpr__5653_5656 = cljs.core.cst$kw$settings.cljs$core$IFn$_invoke$arity$1(opts);
(fexpr__5653_5656.cljs$core$IFn$_invoke$arity$0 ? fexpr__5653_5656.cljs$core$IFn$_invoke$arity$0() : fexpr__5653_5656.call(null));
} else {
}
if(cljs.core.truth_(cljs.core.cst$kw$setup.cljs$core$IFn$_invoke$arity$1(opts))){
var fexpr__5654 = cljs.core.cst$kw$setup.cljs$core$IFn$_invoke$arity$1(opts);
return (fexpr__5654.cljs$core$IFn$_invoke$arity$0 ? fexpr__5654.cljs$core$IFn$_invoke$arity$0() : fexpr__5654.call(null));
} else {
return null;
}
});})(opts,sketch_size,renderer,features))
;
var mouse_wheel = (function (){var temp__5735__auto__ = cljs.core.cst$kw$mouse_DASH_wheel.cljs$core$IFn$_invoke$arity$1(opts);
if(cljs.core.truth_(temp__5735__auto__)){
var wheel_handler = temp__5735__auto__;
return ((function (wheel_handler,temp__5735__auto__,opts,sketch_size,renderer,features,setup){
return (function (evt){
var G__5655 = goog.object.get(evt,"delta");
return (wheel_handler.cljs$core$IFn$_invoke$arity$1 ? wheel_handler.cljs$core$IFn$_invoke$arity$1(G__5655) : wheel_handler.call(null,G__5655));
});
;})(wheel_handler,temp__5735__auto__,opts,sketch_size,renderer,features,setup))
} else {
return null;
}
})();
var opts__$1 = cljs.core.assoc.cljs$core$IFn$_invoke$arity$variadic(opts,cljs.core.cst$kw$setup,setup,cljs.core.prim_seq.cljs$core$IFn$_invoke$arity$2([cljs.core.cst$kw$mouse_DASH_wheel,mouse_wheel], 0));
var sketch = ((function (opts,sketch_size,renderer,features,setup,mouse_wheel,opts__$1){
return (function (prc){
quil.sketch.bind_handlers(prc,opts__$1);
prc.quil = cljs.core.atom.cljs$core$IFn$_invoke$arity$1(null);
return prc.quil_internal_state = cljs.core.atom.cljs$core$IFn$_invoke$arity$1(quil.util.initial_internal_state);
});})(opts,sketch_size,renderer,features,setup,mouse_wheel,opts__$1))
;
return sketch;
});
quil.sketch.destroy_previous_sketch = (function quil$sketch$destroy_previous_sketch(host_elem){
var temp__5735__auto__ = host_elem.processing_obj;
if(cljs.core.truth_(temp__5735__auto__)){
var proc_obj = temp__5735__auto__;
return proc_obj.remove();
} else {
return null;
}
});
quil.sketch.sketch = (function quil$sketch$sketch(var_args){
var args__4736__auto__ = [];
var len__4730__auto___5658 = arguments.length;
var i__4731__auto___5659 = (0);
while(true){
if((i__4731__auto___5659 < len__4730__auto___5658)){
args__4736__auto__.push((arguments[i__4731__auto___5659]));
var G__5660 = (i__4731__auto___5659 + (1));
i__4731__auto___5659 = G__5660;
continue;
} else {
}
break;
}
var argseq__4737__auto__ = ((((0) < args__4736__auto__.length))?(new cljs.core.IndexedSeq(args__4736__auto__.slice((0)),(0),null)):null);
return quil.sketch.sketch.cljs$core$IFn$_invoke$arity$variadic(argseq__4737__auto__);
});
quil.sketch.sketch.cljs$core$IFn$_invoke$arity$variadic = (function (opts){
var opts_map = cljs.core.apply.cljs$core$IFn$_invoke$arity$2(cljs.core.hash_map,opts);
var host_elem = cljs.core.cst$kw$host.cljs$core$IFn$_invoke$arity$1(opts_map);
var renderer = (function (){var or__4131__auto__ = cljs.core.cst$kw$renderer.cljs$core$IFn$_invoke$arity$1(opts_map);
if(cljs.core.truth_(or__4131__auto__)){
return or__4131__auto__;
} else {
return cljs.core.cst$kw$p2d;
}
})();
var host_elem__$1 = ((typeof host_elem === 'string')?document.getElementById(host_elem):host_elem);
if(cljs.core.truth_(host_elem__$1)){
if(cljs.core.truth_(host_elem__$1.processing_context)){
if(cljs.core._EQ_.cljs$core$IFn$_invoke$arity$2(renderer,host_elem__$1.processing_context)){
} else {
console.warn("WARNING: Using different context on one canvas!");
}
} else {
host_elem__$1.processing_context = renderer;
}
quil.sketch.destroy_previous_sketch(host_elem__$1);
var proc_obj = (new p5(quil.sketch.make_sketch(opts_map),host_elem__$1));
host_elem__$1.processing_obj = proc_obj;
proc_obj.quil_canvas = host_elem__$1;
quil.sketch.add_fullscreen_support(proc_obj);
return proc_obj;
} else {
return console.error((cljs.core.truth_(cljs.core.cst$kw$host.cljs$core$IFn$_invoke$arity$1(opts_map))?["ERROR: Cannot find host element: ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(cljs.core.cst$kw$host.cljs$core$IFn$_invoke$arity$1(opts_map))].join(''):"ERROR: Cannot create sketch. :host is not specified or element not found."));
}
});
quil.sketch.sketch.cljs$lang$maxFixedArity = (0);
/** @this {Function} */
quil.sketch.sketch.cljs$lang$applyTo = (function (seq5657){
var self__4718__auto__ = this;
return self__4718__auto__.cljs$core$IFn$_invoke$arity$variadic(cljs.core.seq(seq5657));
});
quil.sketch.sketch_init_list = cljs.core.atom.cljs$core$IFn$_invoke$arity$1(cljs.core.List.EMPTY);
quil.sketch.empty_body_QMARK_ = (function quil$sketch$empty_body_QMARK_(){
var child = document.body.childNodes;
return (child.length <= (1));
});
quil.sketch.add_canvas = (function quil$sketch$add_canvas(canvas_id){
var canvas = document.createElement("canvas");
canvas.setAttribute("id",canvas_id);
return document.body.appendChild(canvas);
});
quil.sketch.init_sketches = (function quil$sketch$init_sketches(){
var add_elem_QMARK__5669 = quil.sketch.empty_body_QMARK_();
var seq__5661_5670 = cljs.core.seq(cljs.core.deref(quil.sketch.sketch_init_list));
var chunk__5662_5671 = null;
var count__5663_5672 = (0);
var i__5664_5673 = (0);
while(true){
if((i__5664_5673 < count__5663_5672)){
var sk_5674 = chunk__5662_5671.cljs$core$IIndexed$_nth$arity$2(null,i__5664_5673);
if(add_elem_QMARK__5669){
quil.sketch.add_canvas(cljs.core.cst$kw$host_DASH_id.cljs$core$IFn$_invoke$arity$1(sk_5674));
} else {
}
var fexpr__5667_5675 = cljs.core.cst$kw$fn.cljs$core$IFn$_invoke$arity$1(sk_5674);
(fexpr__5667_5675.cljs$core$IFn$_invoke$arity$0 ? fexpr__5667_5675.cljs$core$IFn$_invoke$arity$0() : fexpr__5667_5675.call(null));
var G__5676 = seq__5661_5670;
var G__5677 = chunk__5662_5671;
var G__5678 = count__5663_5672;
var G__5679 = (i__5664_5673 + (1));
seq__5661_5670 = G__5676;
chunk__5662_5671 = G__5677;
count__5663_5672 = G__5678;
i__5664_5673 = G__5679;
continue;
} else {
var temp__5735__auto___5680 = cljs.core.seq(seq__5661_5670);
if(temp__5735__auto___5680){
var seq__5661_5681__$1 = temp__5735__auto___5680;
if(cljs.core.chunked_seq_QMARK_(seq__5661_5681__$1)){
var c__4550__auto___5682 = cljs.core.chunk_first(seq__5661_5681__$1);
var G__5683 = cljs.core.chunk_rest(seq__5661_5681__$1);
var G__5684 = c__4550__auto___5682;
var G__5685 = cljs.core.count(c__4550__auto___5682);
var G__5686 = (0);
seq__5661_5670 = G__5683;
chunk__5662_5671 = G__5684;
count__5663_5672 = G__5685;
i__5664_5673 = G__5686;
continue;
} else {
var sk_5687 = cljs.core.first(seq__5661_5681__$1);
if(add_elem_QMARK__5669){
quil.sketch.add_canvas(cljs.core.cst$kw$host_DASH_id.cljs$core$IFn$_invoke$arity$1(sk_5687));
} else {
}
var fexpr__5668_5688 = cljs.core.cst$kw$fn.cljs$core$IFn$_invoke$arity$1(sk_5687);
(fexpr__5668_5688.cljs$core$IFn$_invoke$arity$0 ? fexpr__5668_5688.cljs$core$IFn$_invoke$arity$0() : fexpr__5668_5688.call(null));
var G__5689 = cljs.core.next(seq__5661_5681__$1);
var G__5690 = null;
var G__5691 = (0);
var G__5692 = (0);
seq__5661_5670 = G__5689;
chunk__5662_5671 = G__5690;
count__5663_5672 = G__5691;
i__5664_5673 = G__5692;
continue;
}
} else {
}
}
break;
}
return cljs.core.reset_BANG_(quil.sketch.sketch_init_list,cljs.core.PersistentVector.EMPTY);
});
quil.sketch.add_sketch_to_init_list = (function quil$sketch$add_sketch_to_init_list(sk){
cljs.core.swap_BANG_.cljs$core$IFn$_invoke$arity$3(quil.sketch.sketch_init_list,cljs.core.conj,sk);
if(cljs.core._EQ_.cljs$core$IFn$_invoke$arity$2(document.readyState,"complete")){
return quil.sketch.init_sketches();
} else {
return null;
}
});
goog.events.listenOnce(window,goog.events.EventType.LOAD,quil.sketch.init_sketches);

View File

@ -0,0 +1,131 @@
(ns ^{:doc "Utility fns"}
quil.util
(:require [clojure.string :as cstr]))
(defn no-fn
"Function that does nothing."
[])
(def initial-internal-state
"Internal state map used to initiate all sketches."
{:frame-rate 60
:looping? true})
#?(:clj
(defn callable? [value]
(or (fn? value)
(var? value))))
#?(:clj
(defn absolute-path [path]
(-> (str path)
(java.io.File.)
(.getAbsolutePath))))
#?(:clj
(defn int-like?
[val]
(let [t (type val)]
(or (= java.lang.Long t)
(= java.lang.Integer t)))))
(defn resolve-constant-key
"Returns the val associated with `key` in `mappings` or `key` directly if it
is one of the vals in `mappings`. Otherwise throws an exception."
[key mappings]
(cond
(get mappings key) (get mappings key)
(some #{key} (vals mappings)) key
:else (throw (#?(:clj Exception.
:cljs js/Error.)
(str "Expecting a keyword, got: " key ". Expected one of: " (vec (sort (keys mappings))))))))
(defn- length-of-longest-key
"Returns the length of the longest key of map `m`. Assumes `m`'s keys are strings
and returns 0 if map is empty:
Examples:
```
(length-of-longest-key {\"foo\" 1 \"barr\" 2 \"bazzz\" 3}) ;=> 5
(length-of-longest-key {}) ;=> 0
```"
[m]
(or (last (sort (map #(.length %) (keys m))))
0))
(defn- gen-padding
"Generates a padding string with `s` concatenated with `len` times `pad`.
May be called without starting string `s` in which case it defaults to the
empty string and also without `pad` in which case it defaults to a single
space.
Examples:
```
(gen-padding \"asdf\" 5 \"b\") ;=> \"asdfbbbbb\"
(gen-padding 3 \"b\") ;=> \"bbb\"
(gen-padding 3) ;=> \" \"
```
"
([len] (gen-padding "" len " "))
([len pad] (gen-padding "" len pad))
([s len pad]
(if (> len 0)
(gen-padding (str s pad) (dec len) pad)
s)))
(defn print-definition-list
[definitions]
(let [longest-key (length-of-longest-key definitions)]
(dorun
(map (fn [[k v]]
(let [len (.length k)
diff (- longest-key len)
pad (gen-padding diff)]
(println k pad "- " v)))
definitions))))
(defn clj-compilation? []
#?(:clj
(not
(boolean
(when-let [n (find-ns 'cljs.analyzer)]
(when-let [v (ns-resolve n '*cljs-file*)]
@v))))
:cljs false))
(defn prepare-quil-name [const-keyword]
(cstr/replace
(cstr/upper-case (name const-keyword))
#"-" "_"))
(defn prepare-quil-clj-constants [constants]
(into {}
(map
#(vector % (symbol (str "PConstants/" (prepare-quil-name %))))
constants)))
(defn prepare-quil-cljs-constants [constants]
(into {}
(map
#(vector % `(aget js/p5.prototype ~(prepare-quil-name %)))
constants)))
(defn make-quil-constant-map [target const-map-name const-map]
`(def ^{:private true}
~const-map-name
~(if (= target :clj)
(prepare-quil-clj-constants const-map)
(prepare-quil-cljs-constants const-map))))
(defmacro generate-quil-constants [target & opts]
`(do
~@(map
#(make-quil-constant-map target (first %) (second %))
(partition 2 opts))))
(defn clj-unchecked-int
"In clojure it does `unchecked-int` and does nothing in cljs.
Needed to handle colors in hex form in clojure."
[v]
#?(:clj (unchecked-int v) :cljs v))

View File

@ -0,0 +1,190 @@
// Compiled by ClojureScript 1.10.520 {:static-fns true, :optimize-constants true}
goog.provide('quil.util');
goog.require('cljs.core');
goog.require('cljs.core.constants');
goog.require('clojure.string');
/**
* Function that does nothing.
*/
quil.util.no_fn = (function quil$util$no_fn(){
return null;
});
/**
* Internal state map used to initiate all sketches.
*/
quil.util.initial_internal_state = new cljs.core.PersistentArrayMap(null, 2, [cljs.core.cst$kw$frame_DASH_rate,(60),cljs.core.cst$kw$looping_QMARK_,true], null);
/**
* Returns the val associated with `key` in `mappings` or `key` directly if it
* is one of the vals in `mappings`. Otherwise throws an exception.
*/
quil.util.resolve_constant_key = (function quil$util$resolve_constant_key(key,mappings){
if(cljs.core.truth_(cljs.core.get.cljs$core$IFn$_invoke$arity$2(mappings,key))){
return cljs.core.get.cljs$core$IFn$_invoke$arity$2(mappings,key);
} else {
if(cljs.core.truth_(cljs.core.some(cljs.core.PersistentHashSet.createAsIfByAssoc([key]),cljs.core.vals(mappings)))){
return key;
} else {
throw (new Error(["Expecting a keyword, got: ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(key),". Expected one of: ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(cljs.core.vec(cljs.core.sort.cljs$core$IFn$_invoke$arity$1(cljs.core.keys(mappings))))].join('')));
}
}
});
/**
* Returns the length of the longest key of map `m`. Assumes `m`'s keys are strings
* and returns 0 if map is empty:
*
* Examples:
* ```
* (length-of-longest-key {"foo" 1 "barr" 2 "bazzz" 3}) ;=> 5
* (length-of-longest-key {}) ;=> 0
* ```
*/
quil.util.length_of_longest_key = (function quil$util$length_of_longest_key(m){
var or__4131__auto__ = cljs.core.last(cljs.core.sort.cljs$core$IFn$_invoke$arity$1(cljs.core.map.cljs$core$IFn$_invoke$arity$2((function (p1__5570_SHARP_){
return p1__5570_SHARP_.length();
}),cljs.core.keys(m))));
if(cljs.core.truth_(or__4131__auto__)){
return or__4131__auto__;
} else {
return (0);
}
});
/**
* Generates a padding string with `s` concatenated with `len` times `pad`.
* May be called without starting string `s` in which case it defaults to the
* empty string and also without `pad` in which case it defaults to a single
* space.
*
* Examples:
* ```
* (gen-padding "asdf" 5 "b") ;=> "asdfbbbbb"
* (gen-padding 3 "b") ;=> "bbb"
* (gen-padding 3) ;=> " "
* ```
*
*/
quil.util.gen_padding = (function quil$util$gen_padding(var_args){
var G__5572 = arguments.length;
switch (G__5572) {
case 1:
return quil.util.gen_padding.cljs$core$IFn$_invoke$arity$1((arguments[(0)]));
break;
case 2:
return quil.util.gen_padding.cljs$core$IFn$_invoke$arity$2((arguments[(0)]),(arguments[(1)]));
break;
case 3:
return quil.util.gen_padding.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('')));
}
});
quil.util.gen_padding.cljs$core$IFn$_invoke$arity$1 = (function (len){
return quil.util.gen_padding.cljs$core$IFn$_invoke$arity$3("",len," ");
});
quil.util.gen_padding.cljs$core$IFn$_invoke$arity$2 = (function (len,pad){
return quil.util.gen_padding.cljs$core$IFn$_invoke$arity$3("",len,pad);
});
quil.util.gen_padding.cljs$core$IFn$_invoke$arity$3 = (function (s,len,pad){
if((len > (0))){
return quil.util.gen_padding.cljs$core$IFn$_invoke$arity$3([cljs.core.str.cljs$core$IFn$_invoke$arity$1(s),cljs.core.str.cljs$core$IFn$_invoke$arity$1(pad)].join(''),(len - (1)),pad);
} else {
return s;
}
});
quil.util.gen_padding.cljs$lang$maxFixedArity = 3;
quil.util.print_definition_list = (function quil$util$print_definition_list(definitions){
var longest_key = quil.util.length_of_longest_key(definitions);
return cljs.core.dorun.cljs$core$IFn$_invoke$arity$1(cljs.core.map.cljs$core$IFn$_invoke$arity$2(((function (longest_key){
return (function (p__5574){
var vec__5575 = p__5574;
var k = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__5575,(0),null);
var v = cljs.core.nth.cljs$core$IFn$_invoke$arity$3(vec__5575,(1),null);
var len = k.length();
var diff = (longest_key - len);
var pad = quil.util.gen_padding.cljs$core$IFn$_invoke$arity$1(diff);
return cljs.core.println.cljs$core$IFn$_invoke$arity$variadic(cljs.core.prim_seq.cljs$core$IFn$_invoke$arity$2([k,pad,"- ",v], 0));
});})(longest_key))
,definitions));
});
quil.util.clj_compilation_QMARK_ = (function quil$util$clj_compilation_QMARK_(){
return false;
});
quil.util.prepare_quil_name = (function quil$util$prepare_quil_name(const_keyword){
return clojure.string.replace(clojure.string.upper_case(cljs.core.name(const_keyword)),/-/,"_");
});
quil.util.prepare_quil_clj_constants = (function quil$util$prepare_quil_clj_constants(constants){
return cljs.core.into.cljs$core$IFn$_invoke$arity$2(cljs.core.PersistentArrayMap.EMPTY,cljs.core.map.cljs$core$IFn$_invoke$arity$2((function (p1__5578_SHARP_){
return (new cljs.core.PersistentVector(null,2,(5),cljs.core.PersistentVector.EMPTY_NODE,[p1__5578_SHARP_,cljs.core.symbol.cljs$core$IFn$_invoke$arity$1(["PConstants/",cljs.core.str.cljs$core$IFn$_invoke$arity$1(quil.util.prepare_quil_name(p1__5578_SHARP_))].join(''))],null));
}),constants));
});
quil.util.prepare_quil_cljs_constants = (function quil$util$prepare_quil_cljs_constants(constants){
return cljs.core.into.cljs$core$IFn$_invoke$arity$2(cljs.core.PersistentArrayMap.EMPTY,cljs.core.map.cljs$core$IFn$_invoke$arity$2((function (p1__5579_SHARP_){
return (new cljs.core.PersistentVector(null,2,(5),cljs.core.PersistentVector.EMPTY_NODE,[p1__5579_SHARP_,cljs.core.sequence.cljs$core$IFn$_invoke$arity$1(cljs.core.seq(cljs.core.concat.cljs$core$IFn$_invoke$arity$variadic((new cljs.core.List(null,cljs.core.cst$sym$cljs$core_SLASH_aget,null,(1),null)),(new cljs.core.List(null,cljs.core.cst$sym$js_SLASH_p5$prototype,null,(1),null)),cljs.core.prim_seq.cljs$core$IFn$_invoke$arity$2([(new cljs.core.List(null,quil.util.prepare_quil_name(p1__5579_SHARP_),null,(1),null))], 0))))],null));
}),constants));
});
quil.util.make_quil_constant_map = (function quil$util$make_quil_constant_map(target,const_map_name,const_map){
return cljs.core.sequence.cljs$core$IFn$_invoke$arity$1(cljs.core.seq(cljs.core.concat.cljs$core$IFn$_invoke$arity$variadic((new cljs.core.List(null,cljs.core.cst$sym$def,null,(1),null)),(new cljs.core.List(null,const_map_name,null,(1),null)),cljs.core.prim_seq.cljs$core$IFn$_invoke$arity$2([(new cljs.core.List(null,((cljs.core._EQ_.cljs$core$IFn$_invoke$arity$2(target,cljs.core.cst$kw$clj))?quil.util.prepare_quil_clj_constants(const_map):quil.util.prepare_quil_cljs_constants(const_map)),null,(1),null))], 0))));
});
var ret__4776__auto___5585 = (function (){
quil.util.generate_quil_constants = (function quil$util$generate_quil_constants(var_args){
var args__4736__auto__ = [];
var len__4730__auto___5586 = arguments.length;
var i__4731__auto___5587 = (0);
while(true){
if((i__4731__auto___5587 < len__4730__auto___5586)){
args__4736__auto__.push((arguments[i__4731__auto___5587]));
var G__5588 = (i__4731__auto___5587 + (1));
i__4731__auto___5587 = G__5588;
continue;
} else {
}
break;
}
var argseq__4737__auto__ = ((((3) < args__4736__auto__.length))?(new cljs.core.IndexedSeq(args__4736__auto__.slice((3)),(0),null)):null);
return quil.util.generate_quil_constants.cljs$core$IFn$_invoke$arity$variadic((arguments[(0)]),(arguments[(1)]),(arguments[(2)]),argseq__4737__auto__);
});
quil.util.generate_quil_constants.cljs$core$IFn$_invoke$arity$variadic = (function (_AMPERSAND_form,_AMPERSAND_env,target,opts){
return cljs.core.sequence.cljs$core$IFn$_invoke$arity$1(cljs.core.seq(cljs.core.concat.cljs$core$IFn$_invoke$arity$2((new cljs.core.List(null,cljs.core.cst$sym$do,null,(1),null)),cljs.core.map.cljs$core$IFn$_invoke$arity$2((function (p1__5580_SHARP_){
return quil.util.make_quil_constant_map(target,cljs.core.first(p1__5580_SHARP_),cljs.core.second(p1__5580_SHARP_));
}),cljs.core.partition.cljs$core$IFn$_invoke$arity$2((2),opts)))));
});
quil.util.generate_quil_constants.cljs$lang$maxFixedArity = (3);
/** @this {Function} */
quil.util.generate_quil_constants.cljs$lang$applyTo = (function (seq5581){
var G__5582 = cljs.core.first(seq5581);
var seq5581__$1 = cljs.core.next(seq5581);
var G__5583 = cljs.core.first(seq5581__$1);
var seq5581__$2 = cljs.core.next(seq5581__$1);
var G__5584 = cljs.core.first(seq5581__$2);
var seq5581__$3 = cljs.core.next(seq5581__$2);
var self__4717__auto__ = this;
return self__4717__auto__.cljs$core$IFn$_invoke$arity$variadic(G__5582,G__5583,G__5584,seq5581__$3);
});
return null;
})()
;
quil.util.generate_quil_constants.cljs$lang$macro = true;
/**
* In clojure it does `unchecked-int` and does nothing in cljs.
* Needed to handle colors in hex form in clojure.
*/
quil.util.clj_unchecked_int = (function quil$util$clj_unchecked_int(v){
return v;
});