diff --git a/db/migrations/20140524160241_CreateEventSettings.sql b/db/migrations/20140524160241_CreateEventSettings.sql index 7f6b8f7..e8d2688 100644 --- a/db/migrations/20140524160241_CreateEventSettings.sql +++ b/db/migrations/20140524160241_CreateEventSettings.sql @@ -2,7 +2,12 @@ CREATE TABLE event_settings ( id INTEGER PRIMARY KEY, name VARCHAR(255), - code VARCHAR(16) + code VARCHAR(16), + displaybackgroundcolor VARCHAR(16), + numelimalliances int, + selectionround1order VARCHAR(1), + selectionround2order VARCHAR(1), + selectionround3order VARCHAR(1) ); -- +goose Down diff --git a/event_settings.go b/event_settings.go index dfda084..5e5b220 100644 --- a/event_settings.go +++ b/event_settings.go @@ -6,9 +6,14 @@ package main type EventSettings struct { - Id int - Name string - Code string + Id int + Name string + Code string + DisplayBackgroundColor string + NumElimAlliances int + SelectionRound1Order string + SelectionRound2Order string + SelectionRound3Order string } const eventSettingsId = 0 @@ -18,11 +23,17 @@ func (database *Database) GetEventSettings() (*EventSettings, error) { err := database.eventSettingsMap.Get(eventSettings, eventSettingsId) if err != nil { // Database record doesn't exist yet; create it now. + eventSettings.Name = "Untitled Event" + eventSettings.Code = "UE" + eventSettings.DisplayBackgroundColor = "#00ff00" + eventSettings.NumElimAlliances = 8 + eventSettings.SelectionRound1Order = "F" + eventSettings.SelectionRound2Order = "L" + eventSettings.SelectionRound3Order = "" err = database.eventSettingsMap.Insert(eventSettings) if err != nil { return nil, err } - return new(EventSettings), nil } return eventSettings, nil } diff --git a/event_settings_test.go b/event_settings_test.go index 212f9c2..1ed610a 100644 --- a/event_settings_test.go +++ b/event_settings_test.go @@ -17,10 +17,15 @@ func TestEventSettingsReadWrite(t *testing.T) { eventSettings, err := db.GetEventSettings() assert.Nil(t, err) - assert.Equal(t, *new(EventSettings), *eventSettings) + assert.Equal(t, EventSettings{0, "Untitled Event", "UE", "#00ff00", 8, "F", "L", ""}, *eventSettings) eventSettings.Name = "Chezy Champs" eventSettings.Code = "cc" + eventSettings.DisplayBackgroundColor = "#ff00ff" + eventSettings.NumElimAlliances = 6 + eventSettings.SelectionRound1Order = "F" + eventSettings.SelectionRound2Order = "F" + eventSettings.SelectionRound3Order = "L" err = db.SaveEventSettings(eventSettings) assert.Nil(t, err) eventSettings2, err := db.GetEventSettings() diff --git a/main.go b/main.go index e13cecb..cd03e81 100644 --- a/main.go +++ b/main.go @@ -10,10 +10,16 @@ import ( ) var db *Database +var eventSettings *EventSettings func main() { rand.Seed(time.Now().UnixNano()) - db, _ = OpenDatabase("test.db") + var err error + db, err = OpenDatabase("test.db") + checkErr(err) + eventSettings, err = db.GetEventSettings() + checkErr(err) + ServeWebInterface() } diff --git a/reports.go b/reports.go index 7108fa7..072663b 100644 --- a/reports.go +++ b/reports.go @@ -67,11 +67,6 @@ func RankingsPdfReportHandler(w http.ResponseWriter, r *http.Request) { handleWebErr(w, err) return } - eventSettings, err := db.GetEventSettings() - if err != nil { - handleWebErr(w, err) - return - } // The widths of the table columns in mm, stored here so that they can be referenced for each row. colWidths := map[string]float64{"Rank": 13, "Team": 23, "QS": 20, "Assist": 20, "Auto": 20, @@ -161,11 +156,6 @@ func SchedulePdfReportHandler(w http.ResponseWriter, r *http.Request) { if len(teams) > 0 { matchesPerTeam = len(matches) * teamsPerMatch / len(teams) } - eventSettings, err := db.GetEventSettings() - if err != nil { - handleWebErr(w, err) - return - } // The widths of the table columns in mm, stored here so that they can be referenced for each row. colWidths := map[string]float64{"Time": 35, "Type": 25, "Match": 15, "Team": 20} @@ -272,11 +262,6 @@ func TeamsPdfReportHandler(w http.ResponseWriter, r *http.Request) { handleWebErr(w, err) return } - eventSettings, err := db.GetEventSettings() - if err != nil { - handleWebErr(w, err) - return - } // The widths of the table columns in mm, stored here so that they can be referenced for each row. colWidths := map[string]float64{"Id": 12, "Name": 80, "Location": 80, "RookieYear": 23} diff --git a/reports_test.go b/reports_test.go index c97b38a..03c86ab 100644 --- a/reports_test.go +++ b/reports_test.go @@ -5,8 +5,6 @@ package main import ( "github.com/stretchr/testify/assert" - "net/http" - "net/http/httptest" "testing" "time" ) @@ -33,6 +31,7 @@ func TestRankingsPdfReport(t *testing.T) { clearDb() defer clearDb() db, _ = OpenDatabase(testDbPath) + eventSettings, _ = db.GetEventSettings() ranking1 := Ranking{1114, 2, 18, 1100, 625, 90, 554, 0.254, 9, 1, 0, 0, 10} ranking2 := Ranking{254, 1, 20, 1100, 625, 90, 554, 0.254, 10, 0, 0, 0, 10} db.CreateRanking(&ranking1) @@ -72,6 +71,7 @@ func TestSchedulePdfReport(t *testing.T) { clearDb() defer clearDb() db, _ = OpenDatabase(testDbPath) + eventSettings, _ = db.GetEventSettings() match := Match{Type: "practice", DisplayName: "1", Time: time.Unix(0, 0), Red1: 1, Red2: 2, Red3: 3, Blue1: 4, Blue2: 5, Blue3: 6, Blue1IsSurrogate: true, Blue2IsSurrogate: true, Blue3IsSurrogate: true} db.CreateMatch(&match) @@ -106,6 +106,7 @@ func TestTeamsPdfReport(t *testing.T) { clearDb() defer clearDb() db, _ = OpenDatabase(testDbPath) + eventSettings, _ = db.GetEventSettings() team := Team{254, "NASA", "The Cheesy Poofs", "San Jose", "CA", "USA", 1999, "Barrage"} db.CreateTeam(&team) @@ -114,10 +115,3 @@ func TestTeamsPdfReport(t *testing.T) { assert.Equal(t, 200, recorder.Code) assert.Equal(t, "application/pdf", recorder.HeaderMap["Content-Type"][0]) } - -func getHttpResponse(path string) *httptest.ResponseRecorder { - recorder := httptest.NewRecorder() - req, _ := http.NewRequest("GET", path, nil) - newHandler().ServeHTTP(recorder, req) - return recorder -} diff --git a/setup_settings.go b/setup_settings.go new file mode 100644 index 0000000..347c6db --- /dev/null +++ b/setup_settings.go @@ -0,0 +1,67 @@ +// Copyright 2014 Team 254. All Rights Reserved. +// Author: pat@patfairbank.com (Patrick Fairbank) +// +// Web routes for configuring the event settings. + +package main + +import ( + "html/template" + "net/http" + "regexp" + "strconv" +) + +// Shows the event settings editing page. +func SettingsGetHandler(w http.ResponseWriter, r *http.Request) { + renderSettings(w, r, "") +} + +// Saves the event settings. +func SettingsPostHandler(w http.ResponseWriter, r *http.Request) { + err := r.ParseForm() + if err != nil { + handleWebErr(w, err) + return + } + eventSettings.Name = r.PostFormValue("name") + eventSettings.Code = r.PostFormValue("code") + match, _ := regexp.MatchString("^#([0-9A-Fa-f]{3}){1,2}$", r.PostFormValue("displayBackgroundColor")) + if !match { + renderSettings(w, r, "Display background color must be a valid hex color value.") + return + } + eventSettings.DisplayBackgroundColor = r.PostFormValue("displayBackgroundColor") + numAlliances, _ := strconv.Atoi(r.PostFormValue("numElimAlliances")) + if numAlliances < 2 || numAlliances > 16 { + renderSettings(w, r, "Number of alliances must be between 2 and 16.") + return + } + eventSettings.NumElimAlliances = numAlliances + eventSettings.SelectionRound1Order = r.PostFormValue("selectionRound1Order") + eventSettings.SelectionRound2Order = r.PostFormValue("selectionRound2Order") + eventSettings.SelectionRound3Order = r.PostFormValue("selectionRound3Order") + err = db.SaveEventSettings(eventSettings) + if err != nil { + handleWebErr(w, err) + return + } + renderSettings(w, r, "") +} + +func renderSettings(w http.ResponseWriter, r *http.Request, errorMessage string) { + template, err := template.ParseFiles("templates/settings.html", "templates/base.html") + if err != nil { + handleWebErr(w, err) + return + } + data := struct { + *EventSettings + ErrorMessage string + }{eventSettings, errorMessage} + err = template.ExecuteTemplate(w, "base", data) + if err != nil { + handleWebErr(w, err) + return + } +} diff --git a/setup_settings_test.go b/setup_settings_test.go new file mode 100644 index 0000000..85760ba --- /dev/null +++ b/setup_settings_test.go @@ -0,0 +1,54 @@ +// Copyright 2014 Team 254. All Rights Reserved. +// Author: pat@patfairbank.com (Patrick Fairbank) + +package main + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestSetupSettings(t *testing.T) { + clearDb() + defer clearDb() + var err error + db, err = OpenDatabase(testDbPath) + assert.Nil(t, err) + defer db.Close() + eventSettings, _ = db.GetEventSettings() + + // Check the default setting values. + recorder := getHttpResponse("/setup/settings") + assert.Equal(t, 200, recorder.Code) + assert.Contains(t, recorder.Body.String(), "Untitled Event") + assert.Contains(t, recorder.Body.String(), "UE") + assert.Contains(t, recorder.Body.String(), "#00ff00") + assert.Contains(t, recorder.Body.String(), "8") + + // Change the settings and check the response. + recorder = postHttpResponse("/setup/settings", "name=Chezy Champs&code=CC&displayBackgroundColor=#ff00ff&"+ + "numElimAlliances=16") + assert.Equal(t, 200, recorder.Code) + assert.Contains(t, recorder.Body.String(), "Chezy Champs") + assert.Contains(t, recorder.Body.String(), "CC") + assert.Contains(t, recorder.Body.String(), "#ff00ff") + assert.Contains(t, recorder.Body.String(), "16") +} + +func TestSetupSettingsInvalidValues(t *testing.T) { + clearDb() + defer clearDb() + var err error + db, err = OpenDatabase(testDbPath) + assert.Nil(t, err) + defer db.Close() + eventSettings, _ = db.GetEventSettings() + + // Invalid color value. + recorder := postHttpResponse("/setup/settings", "numAlliances=8&displayBackgroundColor=blorpy") + assert.Contains(t, recorder.Body.String(), "must be a valid hex color value") + + // Invalid number of alliances. + recorder = postHttpResponse("/setup/settings", "numAlliances=1&displayBackgroundColor=#000") + assert.Contains(t, recorder.Body.String(), "must be between 2 and 16") +} diff --git a/setup_teams.go b/setup_teams.go index 0843a6d..6e3f499 100644 --- a/setup_teams.go +++ b/setup_teams.go @@ -96,7 +96,11 @@ func TeamEditGetHandler(w http.ResponseWriter, r *http.Request) { handleWebErr(w, err) return } - err = template.ExecuteTemplate(w, "base", team) + data := struct { + *EventSettings + *Team + }{eventSettings, team} + err = template.ExecuteTemplate(w, "base", data) if err != nil { handleWebErr(w, err) return @@ -127,8 +131,7 @@ func TeamEditPostHandler(w http.ResponseWriter, r *http.Request) { team.City = r.PostFormValue("city") team.StateProv = r.PostFormValue("stateProv") team.Country = r.PostFormValue("country") - rookieYear, _ := strconv.Atoi(r.PostFormValue("rookieYear")) - team.RookieYear = rookieYear + team.RookieYear, _ = strconv.Atoi(r.PostFormValue("rookieYear")) team.RobotName = r.PostFormValue("robotName") err = db.SaveTeam(team) if err != nil { @@ -177,9 +180,10 @@ func renderTeams(w http.ResponseWriter, r *http.Request, showErrorMessage bool) return } data := struct { + *EventSettings Teams []Team ShowErrorMessage bool - }{teams, showErrorMessage} + }{eventSettings, teams, showErrorMessage} err = template.ExecuteTemplate(w, "base", data) if err != nil { handleWebErr(w, err) diff --git a/setup_teams_test.go b/setup_teams_test.go index eb15aba..84a41b5 100644 --- a/setup_teams_test.go +++ b/setup_teams_test.go @@ -8,7 +8,6 @@ import ( "github.com/stretchr/testify/assert" "net/http" "net/http/httptest" - "strings" "testing" ) @@ -19,6 +18,7 @@ func TestSetupTeams(t *testing.T) { db, err = OpenDatabase(testDbPath) assert.Nil(t, err) defer db.Close() + eventSettings, _ = db.GetEventSettings() // Check that there are no teams to start. recorder := getHttpResponse("/setup/teams") @@ -74,6 +74,7 @@ func TestSetupTeamsDisallowModification(t *testing.T) { db, err = OpenDatabase(testDbPath) assert.Nil(t, err) defer db.Close() + eventSettings, _ = db.GetEventSettings() db.CreateTeam(&Team{Id: 254, Nickname: "The Cheesy Poofs"}) db.CreateMatch(&Match{Type: "qualification"}) @@ -120,11 +121,3 @@ func TestSetupTeamsBadReqest(t *testing.T) { assert.Equal(t, 400, recorder.Code) assert.Contains(t, recorder.Body.String(), "No such team") } - -func postHttpResponse(path string, body string) *httptest.ResponseRecorder { - recorder := httptest.NewRecorder() - req, _ := http.NewRequest("POST", path, strings.NewReader(body)) - req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value") - newHandler().ServeHTTP(recorder, req) - return recorder -} diff --git a/static/css/bootstrap-colorpicker.min.css b/static/css/bootstrap-colorpicker.min.css new file mode 100755 index 0000000..98df370 --- /dev/null +++ b/static/css/bootstrap-colorpicker.min.css @@ -0,0 +1,9 @@ +/*! + * Bootstrap Colorpicker + * http://mjolnic.github.io/bootstrap-colorpicker/ + * + * Originally written by (c) 2012 Stefan Petre + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + * + */.colorpicker-saturation{float:left;width:100px;height:100px;cursor:crosshair;background-image:url("../img/bootstrap-colorpicker/saturation.png")}.colorpicker-saturation i{position:absolute;top:0;left:0;display:block;width:5px;height:5px;margin:-4px 0 0 -4px;border:1px solid #000;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.colorpicker-saturation i b{display:block;width:5px;height:5px;border:1px solid #fff;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.colorpicker-hue,.colorpicker-alpha{float:left;width:15px;height:100px;margin-bottom:4px;margin-left:4px;cursor:row-resize}.colorpicker-hue i,.colorpicker-alpha i{position:absolute;top:0;left:0;display:block;width:100%;height:1px;margin-top:-1px;background:#000;border-top:1px solid #fff}.colorpicker-hue{background-image:url("../img/bootstrap-colorpicker/hue.png")}.colorpicker-alpha{display:none;background-image:url("../img/bootstrap-colorpicker/alpha.png")}.colorpicker{top:0;left:0;z-index:2500;min-width:130px;padding:4px;margin-top:1px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*zoom:1}.colorpicker:before,.colorpicker:after{display:table;line-height:0;content:""}.colorpicker:after{clear:both}.colorpicker:before{position:absolute;top:-7px;left:6px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-left:7px solid transparent;border-bottom-color:rgba(0,0,0,0.2);content:''}.colorpicker:after{position:absolute;top:-6px;left:7px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid #fff;border-left:6px solid transparent;content:''}.colorpicker div{position:relative}.colorpicker.colorpicker-with-alpha{min-width:140px}.colorpicker.colorpicker-with-alpha .colorpicker-alpha{display:block}.colorpicker-color{height:10px;margin-top:5px;clear:both;background-image:url("../img/bootstrap-colorpicker/alpha.png");background-position:0 100%}.colorpicker-color div{height:10px}.colorpicker-element .input-group-addon i,.colorpicker-element .add-on i{display:inline-block;width:16px;height:16px;vertical-align:text-top;cursor:pointer}.colorpicker.colorpicker-inline{position:relative;z-index:auto;display:inline-block;float:none}.colorpicker.colorpicker-horizontal{width:110px;height:auto;min-width:110px}.colorpicker.colorpicker-horizontal .colorpicker-saturation{margin-bottom:4px}.colorpicker.colorpicker-horizontal .colorpicker-color{width:100px}.colorpicker.colorpicker-horizontal .colorpicker-hue,.colorpicker.colorpicker-horizontal .colorpicker-alpha{float:left;width:100px;height:15px;margin-bottom:4px;margin-left:0;cursor:col-resize}.colorpicker.colorpicker-horizontal .colorpicker-hue i,.colorpicker.colorpicker-horizontal .colorpicker-alpha i{position:absolute;top:0;left:0;display:block;width:1px;height:15px;margin-top:0;background:#fff;border:0}.colorpicker.colorpicker-horizontal .colorpicker-hue{background-image:url("../img/bootstrap-colorpicker/hue-horizontal.png")}.colorpicker.colorpicker-horizontal .colorpicker-alpha{background-image:url("../img/bootstrap-colorpicker/alpha-horizontal.png")}.colorpicker.colorpicker-hidden{display:none}.colorpicker.colorpicker-visible{display:block}.colorpicker-inline.colorpicker-visible{display:inline-block} \ No newline at end of file diff --git a/static/img/bootstrap-colorpicker/alpha-horizontal.png b/static/img/bootstrap-colorpicker/alpha-horizontal.png new file mode 100755 index 0000000..d0a65c0 Binary files /dev/null and b/static/img/bootstrap-colorpicker/alpha-horizontal.png differ diff --git a/static/img/bootstrap-colorpicker/alpha.png b/static/img/bootstrap-colorpicker/alpha.png new file mode 100755 index 0000000..38043f1 Binary files /dev/null and b/static/img/bootstrap-colorpicker/alpha.png differ diff --git a/static/img/bootstrap-colorpicker/hue-horizontal.png b/static/img/bootstrap-colorpicker/hue-horizontal.png new file mode 100755 index 0000000..a0d9add Binary files /dev/null and b/static/img/bootstrap-colorpicker/hue-horizontal.png differ diff --git a/static/img/bootstrap-colorpicker/hue.png b/static/img/bootstrap-colorpicker/hue.png new file mode 100755 index 0000000..d89560e Binary files /dev/null and b/static/img/bootstrap-colorpicker/hue.png differ diff --git a/static/img/bootstrap-colorpicker/saturation.png b/static/img/bootstrap-colorpicker/saturation.png new file mode 100755 index 0000000..594ae50 Binary files /dev/null and b/static/img/bootstrap-colorpicker/saturation.png differ diff --git a/static/js/bootstrap-colorpicker.min.js b/static/js/bootstrap-colorpicker.min.js new file mode 100755 index 0000000..b9883e4 --- /dev/null +++ b/static/js/bootstrap-colorpicker.min.js @@ -0,0 +1 @@ +!function(a){"use strict";"function"==typeof define&&define.amd?define(["jquery"],a):window.jQuery&&!window.jQuery.fn.colorpicker&&a(window.jQuery)}(function(a){"use strict";var b=function(a){this.value={h:0,s:0,b:0,a:1},this.origFormat=null,a&&(void 0!==a.toLowerCase?this.setColor(a):void 0!==a.h&&(this.value=a))};b.prototype={constructor:b,colors:{aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgreen:"#006400",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",green:"#008000",greenyellow:"#adff2f",honeydew:"#f0fff0",hotpink:"#ff69b4","indianred ":"#cd5c5c","indigo ":"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgrey:"#d3d3d3",lightgreen:"#90ee90",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370d8",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#d87093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",snow:"#fffafa",springgreen:"#00ff7f",steelblue:"#4682b4",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",tomato:"#ff6347",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32"},_sanitizeNumber:function(a){return"number"==typeof a?a:isNaN(a)||null===a||""===a||void 0===a?1:void 0!==a.toLowerCase?parseFloat(a):1},setColor:function(a){a=a.toLowerCase(),this.value=this.stringToHSB(a)||{h:0,s:0,b:0,a:1}},stringToHSB:function(b){b=b.toLowerCase();var c=this,d=!1;return a.each(this.stringParsers,function(a,e){var f=e.re.exec(b),g=f&&e.parse.apply(c,[f]),h=e.format||"rgba";return g?(d=h.match(/hsla?/)?c.RGBtoHSB.apply(c,c.HSLtoRGB.apply(c,g)):c.RGBtoHSB.apply(c,g),c.origFormat=h,!1):!0}),d},setHue:function(a){this.value.h=1-a},setSaturation:function(a){this.value.s=a},setBrightness:function(a){this.value.b=1-a},setAlpha:function(a){this.value.a=parseInt(100*(1-a),10)/100},toRGB:function(a,b,c,d){a||(a=this.value.h,b=this.value.s,c=this.value.b),a*=360;var e,f,g,h,i;return a=a%360/60,i=c*b,h=i*(1-Math.abs(a%2-1)),e=f=g=c-i,a=~~a,e+=[i,h,0,0,h,i][a],f+=[h,i,i,h,0,0][a],g+=[0,0,h,i,i,h][a],{r:Math.round(255*e),g:Math.round(255*f),b:Math.round(255*g),a:d||this.value.a}},toHex:function(a,b,c,d){var e=this.toRGB(a,b,c,d);return"#"+(1<<24|parseInt(e.r)<<16|parseInt(e.g)<<8|parseInt(e.b)).toString(16).substr(1)},toHSL:function(a,b,c,d){a=a||this.value.h,b=b||this.value.s,c=c||this.value.b,d=d||this.value.a;var e=a,f=(2-b)*c,g=b*c;return g/=f>0&&1>=f?f:2-f,f/=2,g>1&&(g=1),{h:isNaN(e)?0:e,s:isNaN(g)?0:g,l:isNaN(f)?0:f,a:isNaN(d)?0:d}},toAlias:function(a,b,c,d){var e=this.toHex(a,b,c,d);for(var f in this.colors)if(this.colors[f]==e)return f;return!1},RGBtoHSB:function(a,b,c,d){a/=255,b/=255,c/=255;var e,f,g,h;return g=Math.max(a,b,c),h=g-Math.min(a,b,c),e=0===h?null:g===a?(b-c)/h:g===b?(c-a)/h+2:(a-b)/h+4,e=(e+360)%6*60/360,f=0===h?0:h/g,{h:this._sanitizeNumber(e),s:f,b:g,a:this._sanitizeNumber(d)}},HueToRGB:function(a,b,c){return 0>c?c+=1:c>1&&(c-=1),1>6*c?a+(b-a)*c*6:1>2*c?b:2>3*c?a+(b-a)*(2/3-c)*6:a},HSLtoRGB:function(a,b,c,d){0>b&&(b=0);var e;e=.5>=c?c*(1+b):c+b-c*b;var f=2*c-e,g=a+1/3,h=a,i=a-1/3,j=Math.round(255*this.HueToRGB(f,e,g)),k=Math.round(255*this.HueToRGB(f,e,h)),l=Math.round(255*this.HueToRGB(f,e,i));return[j,k,l,this._sanitizeNumber(d)]},toString:function(a){switch(a=a||"rgba"){case"rgb":var b=this.toRGB();return"rgb("+b.r+","+b.g+","+b.b+")";case"rgba":var b=this.toRGB();return"rgba("+b.r+","+b.g+","+b.b+","+b.a+")";case"hsl":var c=this.toHSL();return"hsl("+Math.round(360*c.h)+","+Math.round(100*c.s)+"%,"+Math.round(100*c.l)+"%)";case"hsla":var c=this.toHSL();return"hsla("+Math.round(360*c.h)+","+Math.round(100*c.s)+"%,"+Math.round(100*c.l)+"%,"+c.a+")";case"hex":return this.toHex();case"alias":return this.toAlias()||this.toHex();default:return!1}},stringParsers:[{re:/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/,format:"hex",parse:function(a){return[parseInt(a[1],16),parseInt(a[2],16),parseInt(a[3],16),1]}},{re:/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/,format:"hex",parse:function(a){return[parseInt(a[1]+a[1],16),parseInt(a[2]+a[2],16),parseInt(a[3]+a[3],16),1]}},{re:/rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*?\)/,format:"rgb",parse:function(a){return[a[1],a[2],a[3],1]}},{re:/rgb\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*?\)/,format:"rgb",parse:function(a){return[2.55*a[1],2.55*a[2],2.55*a[3],1]}},{re:/rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,format:"rgba",parse:function(a){return[a[1],a[2],a[3],a[4]]}},{re:/rgba\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,format:"rgba",parse:function(a){return[2.55*a[1],2.55*a[2],2.55*a[3],a[4]]}},{re:/hsl\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*?\)/,format:"hsl",parse:function(a){return[a[1]/360,a[2]/100,a[3]/100,a[4]]}},{re:/hsla\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,format:"hsla",parse:function(a){return[a[1]/360,a[2]/100,a[3]/100,a[4]]}},{re:/^([a-z]{3,})$/,format:"alias",parse:function(a){var b=this.colorNameToHex(a[0])||"#000000",c=this.stringParsers[0].re.exec(b),d=c&&this.stringParsers[0].parse.apply(this,[c]);return d}}],colorNameToHex:function(a){return"undefined"!=typeof this.colors[a.toLowerCase()]?this.colors[a.toLowerCase()]:!1}};var c={horizontal:!1,inline:!1,color:!1,format:!1,input:"input",container:!1,component:".add-on, .input-group-addon",sliders:{saturation:{maxLeft:100,maxTop:100,callLeft:"setSaturation",callTop:"setBrightness"},hue:{maxLeft:0,maxTop:100,callLeft:!1,callTop:"setHue"},alpha:{maxLeft:0,maxTop:100,callLeft:!1,callTop:"setAlpha"}},slidersHorz:{saturation:{maxLeft:100,maxTop:100,callLeft:"setSaturation",callTop:"setBrightness"},hue:{maxLeft:100,maxTop:0,callLeft:"setHue",callTop:!1},alpha:{maxLeft:100,maxTop:0,callLeft:"setAlpha",callTop:!1}},template:'