root/kouyou/kouyou.xhtml

Revision 418, 14.8 kB (checked in by eevee, 9 months ago)

Importing kouyou 0.2.

Line 
1<?xml version="1.0" encoding="utf-8"?>
2<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
4<head>
5<!--
6    "Kouyou", by the way, is a Japanese word that translates to various
7    phrases related to the changing of the leaves' color in the fall.
8-->
9<title>Kouyou</title>
10<!-- TODO drop jQuery source in here? -->
11<script type="text/javascript" src="http://veekun.com/script/jquery.js"/>
12<script type="text/javascript"><![CDATA[
13    // Version 0.2
14    // Licensed under the MIT license, found at the bottom of this document
15
16    // Color(red, green, blue)
17    // Constructor for a color object.
18    // Every channel is available as a property, although currently the HSL
19    // channels are not guaranteed to be live until after a call to
20    // this._recalc_hsl() -- this will be fixed with getters shortly.
21    // n.b.: Every value handled by this class is normalized to the [0, 1]
22    // range.  If you want 0-255, do it yourself and multiply by 255.
23    function Color(r, g, b) {
24        this.r = r;
25        this.g = g;
26        this.b = b;
27        this.dirty = true;
28    }
29    Color.prototype = {
30        colorspaces: [
31            { name: 'RGB', channels: ['r', 'g', 'b'] },
32            { name: 'HSL', channels: ['h', 's', 'l'] },
33        ],
34
35        // to_hex()
36        // Returns the color this object represents formatted as a typical
37        // HTML hex triple, i.e. #rrggbb.
38        to_hex: function() {
39            return '#' +
40                ('0' + Math.floor(this.r * 255 + 0.5).toString(16)).substr(-2) +
41                ('0' + Math.floor(this.g * 255 + 0.5).toString(16)).substr(-2) +
42                ('0' + Math.floor(this.b * 255 + 0.5).toString(16)).substr(-2);
43        },
44
45        // -- Setters --
46
47        // set_X(X)
48        // Sets the given channel (identified by its single letter) to the
49        // given value.
50        set_r: function(r) { this.r = r; this.dirty = true; },
51        set_g: function(g) { this.g = g; this.dirty = true; },
52        set_b: function(b) { this.b = b; this.dirty = true; },
53
54        set_h: function(h) { this._recalc_hsl(); this.from_hsl(h, this.s, this.l); },
55        set_s: function(s) { this._recalc_hsl(); this.from_hsl(this.h, s, this.l); },
56        set_l: function(l) { this._recalc_hsl(); this.from_hsl(this.h, this.s, l); },
57
58        // -- Calculations --
59
60        // _recalc_hsl()
61        // Recalculates HSL channels from the current RGB values.  If hue or
62        // saturation is undefined, its current value is kept.  If .dirty is
63        // false, this method is a no-op.
64        _recalc_hsl: function() {
65            if (! this.dirty) return;
66
67            var max = Math.max(this.r, this.g, this.b);
68            var min = Math.min(this.r, this.g, this.b);
69            var equal = max - min < 0.000001;  // lol floats
70            if (equal) {
71                if (this.h == undefined) this.h = 0;
72            } else if (max == this.r)
73                this.h = 1/6 * (this.g - this.b) / (max - min) + 1;
74            else if (max == this.g)
75                this.h = 1/6 * (this.b - this.r) / (max - min) + 1/3;
76            else if (max == this.b)
77                this.h = 1/6 * (this.r - this.g) / (max - min) + 2/3;
78
79            this.h = this.h % 1;
80
81            this.l = (min + max) / 2;
82
83            if (equal) {
84                if (this.s == undefined) this.s = 0;
85            } else if (this.l <= 1/2)
86                this.s = (max - min) / (max + min);
87            else
88                this.s = (max - min) / (2 - (max + min));
89
90            this.dirty = false;
91        },
92
93        // from_hsl(h, s, l)
94        // Does some crazy number-crunching to convert the given hue, sat, and
95        // lightness to RGB.
96        from_hsl: function(h, s, l) {
97            // Modified slightly from http://en.wikipedia.org/wiki/HSL_and_HSV
98            this.h = h;
99            this.s = s;
100            this.l = l;
101
102            var q;
103            if (l < 0.5)
104                q = l * (1 + s);
105            else
106                q = l + s - (l * s);
107
108            var p = 2 * l - q;
109
110            var t = {};
111            t.r = (h + 1/3) % 1;
112            t.g = h;
113            t.b = (h + 2/3) % 1;
114
115            for (ch in t) {
116                if (t[ch] < 1/6)
117                    this[ch] = p + ((q - p) * 6 * t[ch]);
118                else if (t[ch] < 1/2)
119                    this[ch] = q;
120                else if (t[ch] < 2/3)
121                    this[ch] = p + ((q - p) * 6 * (2/3 - t[ch]));
122                else
123                    this[ch] = p;
124            }
125
126            this.dirty = true;
127        },
128
129        // -- Utilities --
130
131        // clone()
132        // Returns a copy of this color.
133        clone: function() {
134            // Javascript has no real concept of classes; "new Foo()" really
135            // just returns a shallow copy of Foo.prototype.  Thus, to clone
136            // an object with only scalar properties, do the same thing
137            var other = {};
138            for (var key in this)
139                other[key] = this[key];
140            return other;
141        },
142
143        // assume(hash)
144        // Returns a color object identical to this one, except that the hash
145        // of channels => values provided are applied.  These are applied in
146        // arbitrary order, so supplying channels from multiple colorspaces
147        // is undefined.
148        assume: function(assumptions) {
149            var other = this.clone();
150            for (var channel in assumptions) {
151                other['set_' + channel]( assumptions[channel] );
152            }
153            return other;
154        },
155    };
156
157    // --- Utility functions ---
158
159    var current_color = new Color(0, 0, 0);
160
161    // update_color()
162    // Synchronizes the page contents with the current color.  Sliders,
163    // gradients, the current color box, and so forth are updated.
164    function update_color() {
165        current_color._recalc_hsl();
166
167        // Adjust background and label for current-color box.
168        var hex = current_color.to_hex();
169        $('#current-color').css('background-color', hex);
170        var color_color = 'white';
171        if (current_color.l > 0.5)
172            // Lighter colors need a darker label
173            color_color = 'black';
174        $('#current-color .color')
175            .text(hex)
176            .css('color', color_color);
177
178        // Iterate over each channel on the page
179        for (var cs in Color.prototype.colorspaces) {
180            var colorspace = Color.prototype.colorspaces[cs];
181            var cs_name = colorspace.name.toLowerCase();
182            for (var c in colorspace.channels) {
183                var channel = colorspace.channels[c];
184                var id = cs_name + '-' + channel;
185
186                // Update value and marker position
187                $('#' + id + ' .value').text(Math.round(current_color[channel] * 10000) / 100 + '%');
188                $('#' + id + ' .marker').css('left', current_color[channel] * 100 + '%');
189
190                // Iterate over stops and update their colors, using assume()
191                // to see what they *would* be if the slider were there.  Note
192                // that hue and lightness are special cases; hue runs through
193                // the rainbow and needs several stops, whereas lightness runs
194                // from black to a full color and then back to white, needing
195                // an extra stop in the middle.
196                var stops = $('#' + id + ' stop');
197                var assumption = {};
198                for (var i = 0; i < stops.length; i++) {
199                    assumption[channel] = i / (stops.length - 1);
200                    stops.get(i).setAttributeNS(null, 'stop-color', current_color.assume(assumption, cs_name).to_hex());
201                }
202            }
203        }
204    }
205
206    // --- Event handlers ---
207
208    // create_sliders()
209    // Creates the slider controls, including a header per colorspace and a
210    // label/slider/value for each channel.
211    function create_sliders() {
212        // Iterate over colorspaces, then channels..
213        for (var cs in Color.prototype.colorspaces) {
214            var colorspace = Color.prototype.colorspaces[cs];
215            var cs_name = colorspace.name.toLowerCase();
216
217            // Headers are fairly simple
218            $(document.body).append('<h1>' + colorspace.name + '</h1>');
219
220            for (var c in colorspace.channels) {
221                var channel = colorspace.channels[c];
222                var id = cs_name + '-' + channel;
223
224                // There's a lot of SVG stuff to insert and DOM manipulation
225                // is unreadable garbage, so just shove in an HTML fragment
226                // (n.b.: backslashes needed because ; in JS is optional)
227                $(document.body).append(' \
228<div class="slider-row" id="' + id + '"> \
229    <div class="label">' + channel.toUpperCase() + '</div> \
230    <div class="value">100%</div> \
231    <div class="slider"> \
232        <div class="marker"></div> \
233        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10" preserveAspectRatio="none"> \
234            <defs> \
235                <linearGradient id="grad-' + id + '"/> \
236            </defs> \
237            <rect x="0" y="0" width="10" height="10" fill="url(#grad-' + id + ')"/> \
238        </svg> \
239    </div> \
240</div> \
241                ');
242
243                // Create the stops for this channel, space them equally, and
244                // give them default values; hue and lightness are special
245                // cases, as mentioned above
246                var stop_ct = 2;
247                if (channel == 'h')
248                    stop_ct = 7;
249                else if (channel == 'l')
250                    stop_ct = 3;
251                var svg_doc = $(document.getElementById(id)).find('svg').get(0).ownerDocument;
252                for (var i = 1; i <= stop_ct; i++) {
253                    var stop = svg_doc.createElementNS('http://www.w3.org/2000/svg', 'stop');
254                    stop.setAttributeNS(null, 'offset', (i - 1) / (stop_ct - 1));
255                    stop.setAttributeNS(null, 'stop-color', '#999999');
256                    svg_doc.getElementById('grad-' + id).appendChild(stop);
257                }
258            }
259        }
260    }
261
262    // --- Dragging ---
263
264    // Remember what slider is currently being moved around
265    var current_slider = null;
266
267    // mousedown
268    // Registers the target slider as the current one.
269    function mousedown(event) {
270        // Use currentTarget to get the registered listener; actual click will
271        // hit a child element
272        var el = event.currentTarget;
273        if (el.className != 'slider') return;
274
275        current_slider = el;
276        event.preventDefault();
277        mousemove(event);
278    }
279
280    // mousemove
281    // Calculates how far along the slider the mouse is now, changes the color
282    // accordingly, and updates the screen.  Value is clipped to [0, 1].
283    function mousemove(event) {
284        if (! current_slider) return;
285
286        var pct = (event.pageX - current_slider.offsetLeft) / current_slider.offsetWidth;
287        if (pct < 0) pct = 0;
288        if (pct > 1) pct = 1;
289
290        var parts = current_slider.parentNode.id.split('-');
291        current_color['set_' + parts[1]](pct);
292        update_color();
293    }
294
295    // mouseup
296    // Blanks the currently registered slider.
297    function mouseup(event) {
298        current_slider = null;
299    }
300
301    // load
302    // Creates everything, updates the screen, and registers handlers.
303    $( function() {
304        create_sliders();
305        update_color();
306        $('.slider').mousedown(mousedown);
307        $(document).mousemove(mousemove);
308        $(document).mouseup(mouseup);
309    } );
310]]></script>
311<style type="text/css">
312/* Eric Meyer's Reset Reloaded */
313/* http://meyerweb.com/eric/thoughts/2007/05/01/reset-reloaded/ */
314
315/* Included here for independence and because I don't have it uploaded anywhere */
316
317html, body, div, span, applet, object, iframe,
318h1, h2, h3, h4, h5, h6, p, blockquote, pre,
319a, abbr, acronym, address, big, cite, code,
320del, dfn, em, font, img, ins, kbd, q, s, samp,
321small, strike, strong, sub, sup, tt, var,
322dl, dt, dd, ol, ul, li,
323fieldset, form, label, legend,
324table, caption, tbody, tfoot, thead, tr, th, td {
325    margin: 0;
326    padding: 0;
327    border: 0;
328    outline: 0;
329    font-weight: inherit;
330    font-style: inherit;
331    font-size: 100%;
332    font-family: inherit;
333    vertical-align: baseline;
334}
335/* remember to define focus styles! */
336:focus {
337    outline: 0;
338}
339body {
340    line-height: 1;
341    color: black;
342    background: white;
343}
344ol, ul {
345    list-style: none;
346}
347/* tables still need 'cellspacing="0"' in the markup */
348table {
349    border-collapse: separate;
350    border-spacing: 0;
351}
352caption, th, td {
353    text-align: left;
354    font-weight: normal;
355}
356blockquote:before, blockquote:after,
357q:before, q:after {
358    content: "";
359}
360blockquote, q {
361    quotes: "" "";
362}
363</style>
364<style type="text/css">
365#current-color { margin: 1em; /*dyn*/ background: red; height: 2em; }
366#current-color .label { float: left; background: white; padding: 0.5em; }
367#current-color .color { line-height: 2em; vertical-align: middle; padding: 0.5em; }
368
369h1 { margin: 1em; margin-top: 2em; border-bottom: 1px solid black; }
370
371.slider-row { margin: 1em; }
372.slider-row > * { height: 1.5em; line-height: 1.5em; }
373.slider-row:after { content: 'v'; display: block; height: 0; visibility: hidden; clear: both; }
374.slider-row .label { width: 2em; text-align: center; float: left; }
375.slider-row .value { width: 5em; text-align: center; float: right; }
376.slider-row .slider { position: relative; margin-left: 2em; margin-right: 5em; background-color: rgba(0%, 100%, 0%, 0.5); }
377.slider-row .slider svg { outline: 1px solid #999; }
378.slider-row .slider .marker { position: absolute; top: -0.5em; left: 0; margin-left: -0.5em; border-top: 0.5em solid #999; border-left: 0.5em solid transparent; border-right: 0.5em solid transparent; }
379</style>
380</head>
381
382
383<body>
384<div id="current-color">
385    <span class="label">Current color:</span>
386    <span class="color">hi</span>
387</div>
388</body>
389</html>
390
391<!--
392Copyright (c) 2007 Alex "Eevee" Munroe
393
394Permission is hereby granted, free of charge, to any person obtaining a copy
395of this software and associated documentation files (the "Software"), to deal
396in the Software without restriction, including without limitation the rights
397to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
398copies of the Software, and to permit persons to whom the Software is
399furnished to do so, subject to the following conditions:
400
401The above copyright notice and this permission notice shall be included in
402all copies or substantial portions of the Software.
403
404THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
405IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
406FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
407AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
408LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
409OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
410THE SOFTWARE.
411-->
Note: See TracBrowser for help on using the browser.