├── .gitignore ├── .rubocop.yml ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── Screenshot.png └── src ├── html ├── step04.html ├── step05.html ├── step06.html └── step07.html ├── images ├── 1.pdf ├── 1.svg ├── 10.pdf ├── 10.svg ├── 2.pdf ├── 2.svg ├── 3.pdf ├── 3.svg ├── 4.pdf ├── 4.svg ├── 5.pdf ├── 5.svg ├── 6.pdf ├── 6.svg ├── 7.pdf ├── 7.svg ├── 8.pdf ├── 8.svg ├── 9.pdf ├── 9.svg └── transparent.png ├── step01.rb ├── step02.rb ├── step03.rb ├── step04.rb ├── step05.rb ├── step06.rb ├── step07.rb ├── tutorial.rb └── vendor ├── modus ├── bootstrap.bundle.min.js ├── jquery.js ├── modus.css └── modus.min.css └── vue.js /.gitignore: -------------------------------------------------------------------------------- 1 | /src/images/preview.png 2 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | Exclude: 3 | - '**/*.rb' 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ruby.rubocop.onSave": false, 3 | "ruby.lint": { 4 | "ruby": false, 5 | "rubocop": false, 6 | "ruby-lint": false 7 | }, 8 | "dimmer.enabled": false, 9 | "dimmer.opacity": 20, 10 | "cSpell.enabled": false 11 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Trimble Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `UI::HtmlDialog` Examples 2 | 3 | These examples were originally presented at SketchUp DevCamp 2017 in Leeds, UK. 4 | 5 | They have later been updated and tweaked. 6 | 7 | The examples are grouped into three parts: 8 | * [HtmlDialog class](#part-1-htmldialog-class) (key difference from WebDialog). 9 | * [Communication between Ruby and JS.](#part-2-communication-between-ruby-and-js) HTML content synchronization. 10 | * [Styling.](#part-3-styling) 11 | 12 | ![](Screenshot.png) 13 | 14 | Below are some of the notes for each example: 15 | 16 | ## Part 1: HtmlDialog Class 17 | 18 | ### Example 1 19 | 20 | * `UI::HtmlDialog` added in SU2017. 21 | * Key benefit is predictable web-engine. 22 | * Same Chromium version across platform for each SketchUp version. 23 | * Basic "Hello World". 24 | * Many similar methods from `UI::WebDialog`. 25 | * Some difference in behaviour. 26 | * Some extra visual options. 27 | * Bye skp actions - hello `sketchup` object. 28 | * `get_element_value` is gone - due to Chromium async nature. 29 | 30 | ### Example 2 31 | 32 | * Typical window behaviour - reuse window. 33 | * Bring to front if already visible. 34 | * Note: Different from WebDialog, html and action callbacks doesn't work reused. 35 | * Reason is related to Chromium being in another process. Keeping 36 | the registered callbacks turned difficult. 37 | 38 | ### Example 3 39 | 40 | * Another pattern for reusing window. 41 | * Register callbacks before showing dialog. Every time. 42 | * If using `set_html`, also do that before showing dialog. 43 | 44 | ## Part 2: Communication between Ruby and JS. 45 | 46 | ### Example 4 47 | 48 | * Syncing data with Ruby, JS and HTML. 49 | * Use frameworks like Vue, React, etc. to bind data. 50 | * Avoids DOM handling. 51 | * Vue is just one of many frameworks, React etc is similar. 52 | * In the template we can display data using `{{ }}`. When the data updates the 53 | HTML updates automatically. 54 | * For form elements use `v-model` or `v-bind` to bind data properties to the 55 | template. User interactions is synchronized back to `data`. 56 | * Notice `say_something` make consecutive callbacks and aren't lost as oppose to the old skp-actions. 57 | 58 | ### Example 5 59 | 60 | * Lets create something more realistic. 61 | * Material edit dialog. 62 | * Select entity, display material. 63 | * Pushing data to dialog when it's ready... 64 | * With Vue, use the `mounted` event. 65 | * With jQuery, use the `ready` callback. 66 | * When Ruby get `ready` callback, push data back to dialog. 67 | * We push data by calling JavaScript functions. 68 | * Recommend using JSON for object structures. 69 | * `self.material_to_hash` convert `Sketchup::Material` to a hash with its properties. 70 | * `v-if` conditionally control what to display based on data. 71 | * Opacity is special - data from API is 0.0-1.0, UI use 0-100. 72 | * Computed properties can be used for custom display of data. 73 | 74 | ## Part 3: Styling 75 | 76 | ### Example 6 77 | 78 | * Styling webdialogs. 79 | * Look at UI frameworks; Bootstrap, Trimble Modus. 80 | * Add reference to Trimble Modus CSS and JS libs. 81 | * Few Ruby change, we mostly add some HTML classes to our elements. 82 | 83 | ### Example 7 84 | 85 | * Tweaking layout. 86 | * Using grid system - two eight wide columns (in 16 columns max). 87 | * Some manual CSS adjustments. 88 | * Some padding around content. 89 | * Positioning the footer at the bottom. 90 | * Adjusting the input widths. 91 | * Custom element for color and texture preview. 92 | -------------------------------------------------------------------------------- /Screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SketchUp/htmldialog-examples/130205067a7e64e8c32b4bad5bb78d1f425cb036/Screenshot.png -------------------------------------------------------------------------------- /src/html/step04.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |

10 |

11 |

12 |

13 |
14 | 15 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/html/step05.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 73 | 77 | 78 | 83 | 84 |
85 | 86 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /src/html/step06.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 93 | 97 | 98 |

99 | 100 | 101 | 102 |

103 | 104 |
105 | 106 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /src/html/step07.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 64 | 65 | 66 |
67 | 165 | 169 | 170 | 181 |
182 | 183 | 249 | 250 | 251 | -------------------------------------------------------------------------------- /src/images/1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SketchUp/htmldialog-examples/130205067a7e64e8c32b4bad5bb78d1f425cb036/src/images/1.pdf -------------------------------------------------------------------------------- /src/images/1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/images/10.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SketchUp/htmldialog-examples/130205067a7e64e8c32b4bad5bb78d1f425cb036/src/images/10.pdf -------------------------------------------------------------------------------- /src/images/10.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 8 | 14 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /src/images/2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SketchUp/htmldialog-examples/130205067a7e64e8c32b4bad5bb78d1f425cb036/src/images/2.pdf -------------------------------------------------------------------------------- /src/images/2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/images/3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SketchUp/htmldialog-examples/130205067a7e64e8c32b4bad5bb78d1f425cb036/src/images/3.pdf -------------------------------------------------------------------------------- /src/images/3.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /src/images/4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SketchUp/htmldialog-examples/130205067a7e64e8c32b4bad5bb78d1f425cb036/src/images/4.pdf -------------------------------------------------------------------------------- /src/images/4.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/images/5.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SketchUp/htmldialog-examples/130205067a7e64e8c32b4bad5bb78d1f425cb036/src/images/5.pdf -------------------------------------------------------------------------------- /src/images/5.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/images/6.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SketchUp/htmldialog-examples/130205067a7e64e8c32b4bad5bb78d1f425cb036/src/images/6.pdf -------------------------------------------------------------------------------- /src/images/6.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/images/7.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SketchUp/htmldialog-examples/130205067a7e64e8c32b4bad5bb78d1f425cb036/src/images/7.pdf -------------------------------------------------------------------------------- /src/images/7.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/images/8.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SketchUp/htmldialog-examples/130205067a7e64e8c32b4bad5bb78d1f425cb036/src/images/8.pdf -------------------------------------------------------------------------------- /src/images/8.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/images/9.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SketchUp/htmldialog-examples/130205067a7e64e8c32b4bad5bb78d1f425cb036/src/images/9.pdf -------------------------------------------------------------------------------- /src/images/9.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/images/transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SketchUp/htmldialog-examples/130205067a7e64e8c32b4bad5bb78d1f425cb036/src/images/transparent.png -------------------------------------------------------------------------------- /src/step01.rb: -------------------------------------------------------------------------------- 1 | module Examples 2 | module MaterialInspector 3 | module Step01 4 | 5 | # Basic Hello World. 6 | # Creates new window every time. 7 | 8 | def self.show_dialog 9 | html = <<-EOT 10 |

Hello World

11 |

12 | EOT 13 | options = { 14 | :dialog_title => "Material", 15 | :preferences_key => "example.htmldialog.materialinspector", 16 | :style => UI::HtmlDialog::STYLE_DIALOG # New feature! 17 | } 18 | dialog = UI::HtmlDialog.new(options) 19 | dialog.set_html(html) 20 | dialog.center # New feature! 21 | dialog.add_action_callback('poke') { |action_context, name, num_pokes| 22 | # New feature: callback parameters support basic types. 23 | self.on_poke(name, num_pokes) # Delegate to method for easier debugging. 24 | nil 25 | } 26 | dialog 27 | dialog.show 28 | end 29 | 30 | def self.on_poke(name, num_pokes) 31 | num_pokes.times { 32 | puts "Poke #{name}!" 33 | } 34 | end 35 | 36 | end # Step01 37 | end # MaterialInspector 38 | end # Examples 39 | -------------------------------------------------------------------------------- /src/step02.rb: -------------------------------------------------------------------------------- 1 | module Examples 2 | module MaterialInspector 3 | module Step02 4 | 5 | # Reuse window, bring to front if already visible. 6 | # However, html and action callbacks not reusable. 7 | 8 | def self.create_dialog 9 | # The callbacks here only work the first time the dialog is shown and are 10 | # lost when it is closed. 11 | # Example 03 shows how to reattach the callbacks each time the dialog is 12 | # shown. 13 | html = <<-EOT 14 |

Hello World

15 |

16 | EOT 17 | options = { 18 | :dialog_title => "Material", 19 | :preferences_key => "example.htmldialog.materialinspector", 20 | :style => UI::HtmlDialog::STYLE_DIALOG 21 | } 22 | dialog = UI::HtmlDialog.new(options) 23 | dialog.set_html(html) 24 | dialog.center 25 | dialog.add_action_callback('poke') { |action_context, name, num_pokes| 26 | self.on_poke(name, num_pokes) 27 | nil 28 | } 29 | dialog 30 | end 31 | 32 | def self.show_dialog 33 | @dialog ||= self.create_dialog 34 | @dialog.visible? ? @dialog.bring_to_front : @dialog.show 35 | end 36 | 37 | def self.on_poke(name, num_pokes) 38 | num_pokes.times { 39 | puts "Poke #{name}!" 40 | } 41 | end 42 | 43 | end # Step02 44 | end # MaterialInspector 45 | end # Examples -------------------------------------------------------------------------------- /src/step03.rb: -------------------------------------------------------------------------------- 1 | module Examples 2 | module MaterialInspector 3 | module Step03 4 | 5 | # Window fully reusable. 6 | 7 | def self.create_dialog 8 | options = { 9 | :dialog_title => "Material", 10 | :preferences_key => "example.htmldialog.materialinspector", 11 | :style => UI::HtmlDialog::STYLE_DIALOG 12 | } 13 | dialog = UI::HtmlDialog.new(options) 14 | dialog.center 15 | dialog 16 | end 17 | 18 | def self.show_dialog 19 | if @dialog && @dialog.visible? 20 | @dialog.bring_to_front 21 | else 22 | # Attach content and callbacks when showing the dialog, 23 | # not when creating it, to be able to use the same dialog again. 24 | html = <<-EOT 25 |

Hello World

26 |

27 | EOT 28 | @dialog ||= self.create_dialog 29 | @dialog.add_action_callback('poke') { |action_context, name, num_pokes| 30 | self.on_poke(name, num_pokes) 31 | nil 32 | } 33 | @dialog.set_html(html) 34 | @dialog.show 35 | end 36 | end 37 | 38 | def self.on_poke(name, num_pokes) 39 | num_pokes.times { 40 | puts "Poke #{name}!" 41 | } 42 | end 43 | 44 | end # Step03 45 | end # MaterialInspector 46 | end # Examples -------------------------------------------------------------------------------- /src/step04.rb: -------------------------------------------------------------------------------- 1 | module Examples 2 | module MaterialInspector 3 | module Step04 4 | 5 | # Using Vue to bind data. 6 | # Multiple callbacks from JS is safe. 7 | 8 | def self.create_dialog 9 | html_file = File.join(__dir__, 'html', 'step04.html') # Use external HTML 10 | options = { 11 | :dialog_title => "Material", 12 | :preferences_key => "example.htmldialog.materialinspector", 13 | :style => UI::HtmlDialog::STYLE_DIALOG 14 | } 15 | dialog = UI::HtmlDialog.new(options) 16 | dialog.set_file(html_file) # Can be set here. 17 | dialog.center 18 | dialog 19 | end 20 | 21 | def self.show_dialog 22 | if @dialog && @dialog.visible? 23 | @dialog.bring_to_front 24 | else 25 | @dialog ||= self.create_dialog 26 | @dialog.add_action_callback('poke') { |action_context, name, num_pokes| 27 | self.on_poke(name, num_pokes) 28 | nil 29 | } 30 | @dialog.add_action_callback('say') { |action_context, string| 31 | puts string 32 | nil 33 | } 34 | @dialog.show 35 | end 36 | end 37 | 38 | def self.on_poke(name, num_pokes) 39 | puts 'Get ready...' 40 | num_pokes.times { 41 | puts "Poke #{name}!" 42 | } 43 | # More pokes next time! 44 | @dialog.execute_script("app.num_pokes = #{num_pokes + 1}") 45 | # Switch target - muhahaha! 46 | target = %w[Thom Chris Jin Jeremy].sample 47 | @dialog.execute_script("app.name = '#{target}'") 48 | end 49 | 50 | end # Step04 51 | end # MaterialInspector 52 | end # Examples 53 | -------------------------------------------------------------------------------- /src/step05.rb: -------------------------------------------------------------------------------- 1 | require 'sketchup.rb' 2 | require 'json' 3 | 4 | module Examples 5 | module MaterialInspector 6 | module Step05 7 | 8 | # More realistic example. 9 | # Material-Edit dialog. Just the data exchange logic with Vue. 10 | 11 | def self.create_dialog 12 | html_file = File.join(__dir__, 'html', 'step05.html') 13 | options = { 14 | :dialog_title => "Material", 15 | :preferences_key => "example.htmldialog.materialinspector", 16 | :style => UI::HtmlDialog::STYLE_DIALOG 17 | } 18 | dialog = UI::HtmlDialog.new(options) 19 | dialog.set_file(html_file) 20 | dialog.center 21 | dialog 22 | end 23 | 24 | def self.show_dialog 25 | @dialog ||= self.create_dialog 26 | @dialog.add_action_callback("ready") { |action_context| 27 | self.update_dialog 28 | nil 29 | } 30 | @dialog.add_action_callback("accept") { |action_context, value| 31 | self.update_material(value) 32 | @dialog.close 33 | nil 34 | } 35 | @dialog.add_action_callback("cancel") { |action_context, value| 36 | @dialog.close 37 | nil 38 | } 39 | @dialog.add_action_callback("apply") { |action_context, value| 40 | self.update_material(value) 41 | nil 42 | } 43 | @dialog.show 44 | end 45 | 46 | # Populate dialog with selected material. 47 | 48 | def self.update_dialog 49 | return if @dialog.nil? 50 | material_data = nil 51 | model = Sketchup.active_model 52 | if model.selection.size == 1 53 | material = self.selected_material 54 | material_data = self.material_to_hash(material) if material 55 | end 56 | json = material_data ? JSON.pretty_generate(material_data) : 'null' 57 | @dialog.execute_script("updateMaterial(#{json})") 58 | end 59 | 60 | # Edit a material. 61 | 62 | def self.update_material(data) 63 | model = Sketchup.active_model 64 | material = model.materials[data['name']] 65 | r = data['color']['red'] 66 | g = data['color']['green'] 67 | b = data['color']['blue'] 68 | color = Sketchup::Color.new(r, g, b) 69 | model.start_operation('Edit Material') 70 | material.color = color 71 | material.alpha = data['alpha'] 72 | material.colorize_type = data['colorize_type'] 73 | model.commit_operation 74 | end 75 | 76 | # Observer handling. 77 | 78 | def self.on_selection_change(selection) 79 | self.update_dialog 80 | end 81 | 82 | def self.on_material_change(material) 83 | if material == self.selected_material 84 | self.update_dialog 85 | end 86 | end 87 | 88 | # Collect model data. 89 | 90 | def self.selected_material 91 | material = nil 92 | model = Sketchup.active_model 93 | if model.selection.size == 1 94 | material = nil 95 | entity = model.selection.first 96 | if entity.respond_to?(:material) && entity.material 97 | material = entity.material 98 | elsif entity.respond_to?(:back_material) 99 | material = entity.back_material 100 | end 101 | end 102 | material 103 | end 104 | 105 | # Convert objects to Hashes. 106 | 107 | def self.material_to_hash(material) 108 | return nil if material.nil? 109 | { 110 | name: material.name, 111 | display_name: material.display_name, 112 | type: material.materialType, 113 | color: self.color_to_hash(material.color), 114 | alpha: material.alpha, 115 | texture: self.texture_to_hash(material.texture), 116 | colorize_type: material.colorize_type, 117 | colorize_deltas: material.colorize_deltas, 118 | } 119 | end 120 | 121 | def self.color_to_hash(color) 122 | { 123 | red: color.red, 124 | green: color.green, 125 | blue: color.blue, 126 | alpha: color.alpha, 127 | } 128 | end 129 | 130 | def self.texture_to_hash(texture) 131 | return nil if texture.nil? 132 | { 133 | filename: texture.filename, 134 | pixel_width: texture.image_width, 135 | pixel_height: texture.image_height, 136 | model_width: texture.width, 137 | model_height: texture.height, 138 | model_width_formatted: texture.width.to_l.to_s, 139 | model_height_formatted: texture.height.to_l.to_s, 140 | average_color: self.color_to_hash(texture.average_color) 141 | } 142 | end 143 | 144 | # Observe selected material. 145 | 146 | PLUGIN = self 147 | class SelectionChangeObserver < Sketchup::SelectionObserver 148 | def onSelectionAdded(selection, entity) 149 | PLUGIN.on_selection_change(selection) 150 | end 151 | def onSelectionBulkChange(selection) 152 | PLUGIN.on_selection_change(selection) 153 | end 154 | def onSelectionCleared(selection) 155 | PLUGIN.on_selection_change(selection) 156 | end 157 | def onSelectionRemoved(selection, entity) 158 | PLUGIN.on_selection_change(selection) 159 | end 160 | def onSelectedRemoved(selection, entity) 161 | PLUGIN.on_selection_change(selection) 162 | end 163 | end 164 | 165 | class MaterialChangeObserver < Sketchup::MaterialsObserver 166 | def onMaterialChange(materials, material) 167 | PLUGIN.on_material_change(material) 168 | end 169 | def onMaterialRemove(materials, material) 170 | PLUGIN.on_material_change(material) 171 | end 172 | end 173 | 174 | class AppObserver < Sketchup::AppObserver 175 | def onNewModel(model) 176 | observe_model(model) 177 | end 178 | def onOpenModel(model) 179 | observe_model(model) 180 | end 181 | def expectsStartupModelNotifications 182 | return true 183 | end 184 | private 185 | def observe_model(model) 186 | model.selection.add_observer(SelectionChangeObserver.new) 187 | model.materials.add_observer(MaterialChangeObserver.new) 188 | end 189 | end 190 | 191 | unless file_loaded?(__FILE__) 192 | Sketchup.add_observer(AppObserver.new) 193 | end 194 | 195 | file_loaded(__FILE__) 196 | 197 | end # Step05 198 | end # MaterialInspector 199 | end # Examples 200 | -------------------------------------------------------------------------------- /src/step06.rb: -------------------------------------------------------------------------------- 1 | require 'sketchup.rb' 2 | require 'json' 3 | 4 | module Examples 5 | module MaterialInspector 6 | module Step06 7 | 8 | # Adding UI frameworks for some style. 9 | 10 | def self.create_dialog 11 | html_file = File.join(__dir__, 'html', 'step06.html') 12 | options = { 13 | :dialog_title => "Material", 14 | :preferences_key => "example.htmldialog.materialinspector", 15 | :style => UI::HtmlDialog::STYLE_DIALOG 16 | } 17 | dialog = UI::HtmlDialog.new(options) 18 | dialog.set_file(html_file) 19 | dialog.center 20 | dialog 21 | end 22 | 23 | def self.show_dialog 24 | @dialog ||= self.create_dialog 25 | @dialog.add_action_callback("ready") { 26 | self.update_dialog 27 | nil 28 | } 29 | @dialog.add_action_callback("accept") { |_, value| 30 | self.update_material(value) 31 | @dialog.close 32 | nil 33 | } 34 | @dialog.add_action_callback("cancel") { |_, value| 35 | @dialog.close 36 | nil 37 | } 38 | @dialog.add_action_callback("apply") { |_, value| 39 | self.update_material(value) 40 | nil 41 | } 42 | @dialog.show 43 | end 44 | 45 | # Populate dialog with selected material. 46 | 47 | def self.update_dialog 48 | return if @dialog.nil? 49 | material_data = nil 50 | model = Sketchup.active_model 51 | if model.selection.size == 1 52 | material = self.selected_material 53 | material_data = self.material_to_hash(material) if material 54 | end 55 | json = material_data ? JSON.pretty_generate(material_data) : 'null' 56 | @dialog.execute_script("updateMaterial(#{json})") 57 | end 58 | 59 | # Edit a material. 60 | 61 | def self.update_material(data) 62 | model = Sketchup.active_model 63 | material = model.materials[data['name']] 64 | r = data['color']['red'] 65 | g = data['color']['green'] 66 | b = data['color']['blue'] 67 | color = Sketchup::Color.new(r, g, b) 68 | model.start_operation('Edit Material') 69 | material.color = color 70 | material.alpha = data['alpha'] 71 | material.colorize_type = data['colorize_type'] 72 | model.commit_operation 73 | end 74 | 75 | # Observer handling. 76 | 77 | def self.on_selection_change(selection) 78 | self.update_dialog 79 | end 80 | 81 | def self.on_material_change(material) 82 | if material == self.selected_material 83 | self.update_dialog 84 | end 85 | end 86 | 87 | # Collect model data. 88 | 89 | def self.selected_material 90 | material = nil 91 | model = Sketchup.active_model 92 | if model.selection.size == 1 93 | material = nil 94 | entity = model.selection.first 95 | if entity.respond_to?(:material) && entity.material 96 | material = entity.material 97 | elsif entity.respond_to?(:back_material) 98 | material = entity.back_material 99 | end 100 | end 101 | material 102 | end 103 | 104 | # Convert objects to Hashes. 105 | 106 | def self.material_to_hash(material) 107 | return nil if material.nil? 108 | { 109 | name: material.name, 110 | display_name: material.display_name, 111 | type: material.materialType, 112 | color: self.color_to_hash(material.color), 113 | alpha: material.alpha, 114 | texture: self.texture_to_hash(material.texture), 115 | colorize_type: material.colorize_type, 116 | colorize_deltas: material.colorize_deltas, 117 | } 118 | end 119 | 120 | def self.color_to_hash(color) 121 | { 122 | red: color.red, 123 | green: color.green, 124 | blue: color.blue, 125 | alpha: color.alpha, 126 | } 127 | end 128 | 129 | def self.texture_to_hash(texture) 130 | return nil if texture.nil? 131 | { 132 | filename: texture.filename, 133 | pixel_width: texture.image_width, 134 | pixel_height: texture.image_height, 135 | model_width: texture.width, 136 | model_height: texture.height, 137 | model_width_formatted: texture.width.to_l.to_s, 138 | model_height_formatted: texture.height.to_l.to_s, 139 | average_color: self.color_to_hash(texture.average_color) 140 | } 141 | end 142 | 143 | # Observe selected material. 144 | 145 | PLUGIN = self 146 | class SelectionChangeObserver < Sketchup::SelectionObserver 147 | def onSelectionAdded(selection, entity) 148 | PLUGIN.on_selection_change(selection) 149 | end 150 | def onSelectionBulkChange(selection) 151 | PLUGIN.on_selection_change(selection) 152 | end 153 | def onSelectionCleared(selection) 154 | PLUGIN.on_selection_change(selection) 155 | end 156 | def onSelectionRemoved(selection, entity) 157 | PLUGIN.on_selection_change(selection) 158 | end 159 | def onSelectedRemoved(selection, entity) 160 | PLUGIN.on_selection_change(selection) 161 | end 162 | end 163 | 164 | class MaterialChangeObserver < Sketchup::MaterialsObserver 165 | def onMaterialChange(materials, material) 166 | PLUGIN.on_material_change(material) 167 | end 168 | def onMaterialRemove(materials, material) 169 | PLUGIN.on_material_change(material) 170 | end 171 | end 172 | 173 | class AppObserver < Sketchup::AppObserver 174 | def onNewModel(model) 175 | observe_model(model) 176 | end 177 | def onOpenModel(model) 178 | observe_model(model) 179 | end 180 | def expectsStartupModelNotifications 181 | return true 182 | end 183 | private 184 | def observe_model(model) 185 | model.selection.add_observer(SelectionChangeObserver.new) 186 | model.materials.add_observer(MaterialChangeObserver.new) 187 | end 188 | end 189 | 190 | unless file_loaded?(__FILE__) 191 | Sketchup.add_observer(AppObserver.new) 192 | end 193 | 194 | file_loaded(__FILE__) 195 | 196 | end # Step06 197 | end # MaterialInspector 198 | end # Examples 199 | -------------------------------------------------------------------------------- /src/step07.rb: -------------------------------------------------------------------------------- 1 | require 'sketchup.rb' 2 | require 'json' 3 | 4 | module Examples 5 | module MaterialInspector 6 | module Step07 7 | 8 | # Fine tuning UI. 9 | 10 | def self.create_dialog 11 | html_file = File.join(__dir__, 'html', 'step07.html') 12 | # Define pixel perfect dimensions matching our content. 13 | width = 15 + 128 + 15 + 15 + 128 + 15 14 | height = 470 15 | # Add some extra space for older SU versions that don't support inner sizing. 16 | if Sketchup.version.to_f < 21.1 17 | width += 20 18 | height += 40 19 | end 20 | options = { 21 | :dialog_title => "Material", 22 | :preferences_key => "com.sketchup.example.htmldialog.materialinspector", 23 | :style => UI::HtmlDialog::STYLE_DIALOG, 24 | # Set a fixed size now that we know the content size. 25 | :resizable => false, 26 | :width => width, 27 | :height => height, 28 | :use_content_size => true 29 | } 30 | dialog = UI::HtmlDialog.new(options) 31 | dialog.set_file(html_file) 32 | dialog.center 33 | dialog 34 | end 35 | 36 | def self.show_dialog 37 | @dialog ||= self.create_dialog 38 | @dialog.add_action_callback("ready") { 39 | self.update_dialog 40 | nil 41 | } 42 | @dialog.add_action_callback("accept") { |_, value| 43 | self.update_material(value) 44 | @dialog.close 45 | nil 46 | } 47 | @dialog.add_action_callback("cancel") { |_, value| 48 | @dialog.close 49 | nil 50 | } 51 | @dialog.add_action_callback("apply") { |_, value| 52 | self.update_material(value) 53 | nil 54 | } 55 | @dialog.show 56 | end 57 | 58 | # Populate dialog with selected material. 59 | 60 | def self.update_dialog 61 | return if @dialog.nil? 62 | material_data = nil 63 | model = Sketchup.active_model 64 | if model.selection.size == 1 65 | material = self.selected_material 66 | if material 67 | material_data = self.material_to_hash(material) 68 | # Write out a material thumbnail. 69 | self.generate_texture_preview(material) 70 | end 71 | end 72 | json = material_data ? JSON.pretty_generate(material_data) : 'null' 73 | @dialog.execute_script("updateMaterial(#{json})") 74 | end 75 | 76 | # Edit a material. 77 | 78 | def self.update_material(data) 79 | model = Sketchup.active_model 80 | material = model.materials[data['name']] 81 | r = data['color']['red'] 82 | g = data['color']['green'] 83 | b = data['color']['blue'] 84 | color = Sketchup::Color.new(r, g, b) 85 | model.start_operation('Edit Material') 86 | material.color = color 87 | material.alpha = data['alpha'] 88 | material.colorize_type = data['colorize_type'] 89 | model.commit_operation 90 | end 91 | 92 | # Observer handling. 93 | 94 | def self.on_selection_change(selection) 95 | self.update_dialog 96 | end 97 | 98 | def self.on_material_change(material) 99 | if material == self.selected_material 100 | self.update_dialog 101 | end 102 | end 103 | 104 | # Collect model data. 105 | 106 | def self.selected_material 107 | material = nil 108 | model = Sketchup.active_model 109 | if model.selection.size == 1 110 | material = nil 111 | entity = model.selection.first 112 | if entity.respond_to?(:material) && entity.material 113 | material = entity.material 114 | elsif entity.respond_to?(:back_material) 115 | material = entity.back_material 116 | end 117 | end 118 | material 119 | end 120 | 121 | def self.generate_texture_preview(material) 122 | return unless material && material.texture 123 | preview_file = File.join(__dir__, 'images', 'preview.png') 124 | unless material.write_thumbnail(preview_file, 128) 125 | # .write_thumbnail fails can fail if dimensions are equal or smaller than 126 | # the actual size of the textures. 127 | material.texture.write(preview_file) 128 | end 129 | end 130 | 131 | # Convert objects to Hashes. 132 | 133 | def self.material_to_hash(material) 134 | return nil if material.nil? 135 | { 136 | name: material.name, 137 | display_name: material.display_name, 138 | type: material.materialType, 139 | color: self.color_to_hash(material.color), 140 | alpha: material.alpha, 141 | texture: self.texture_to_hash(material.texture), 142 | colorize_type: material.colorize_type, 143 | colorize_deltas: material.colorize_deltas, 144 | } 145 | end 146 | 147 | def self.color_to_hash(color) 148 | { 149 | red: color.red, 150 | green: color.green, 151 | blue: color.blue, 152 | alpha: color.alpha, 153 | } 154 | end 155 | 156 | def self.texture_to_hash(texture) 157 | return nil if texture.nil? 158 | { 159 | filename: texture.filename, 160 | pixel_width: texture.image_width, 161 | pixel_height: texture.image_height, 162 | model_width: texture.width, 163 | model_height: texture.height, 164 | model_width_formatted: texture.width.to_l.to_s, 165 | model_height_formatted: texture.height.to_l.to_s, 166 | average_color: self.color_to_hash(texture.average_color) 167 | } 168 | end 169 | 170 | # Observe selected material. 171 | 172 | PLUGIN = self 173 | class SelectionChangeObserver < Sketchup::SelectionObserver 174 | def onSelectionAdded(selection, entity) 175 | PLUGIN.on_selection_change(selection) 176 | end 177 | def onSelectionBulkChange(selection) 178 | PLUGIN.on_selection_change(selection) 179 | end 180 | def onSelectionCleared(selection) 181 | PLUGIN.on_selection_change(selection) 182 | end 183 | def onSelectionRemoved(selection, entity) 184 | PLUGIN.on_selection_change(selection) 185 | end 186 | def onSelectedRemoved(selection, entity) 187 | PLUGIN.on_selection_change(selection) 188 | end 189 | end 190 | 191 | class MaterialChangeObserver < Sketchup::MaterialsObserver 192 | def onMaterialChange(materials, material) 193 | PLUGIN.on_material_change(material) 194 | end 195 | def onMaterialRemove(materials, material) 196 | PLUGIN.on_material_change(material) 197 | end 198 | end 199 | 200 | class AppObserver < Sketchup::AppObserver 201 | def onNewModel(model) 202 | observe_model(model) 203 | end 204 | def onOpenModel(model) 205 | observe_model(model) 206 | end 207 | def expectsStartupModelNotifications 208 | return true 209 | end 210 | private 211 | def observe_model(model) 212 | model.selection.add_observer(SelectionChangeObserver.new) 213 | model.materials.add_observer(MaterialChangeObserver.new) 214 | end 215 | end 216 | 217 | unless file_loaded?(__FILE__) 218 | Sketchup.add_observer(AppObserver.new) 219 | end 220 | 221 | file_loaded(__FILE__) 222 | 223 | end # Step07 224 | end # MaterialInspector 225 | end # Examples 226 | -------------------------------------------------------------------------------- /src/tutorial.rb: -------------------------------------------------------------------------------- 1 | require_relative 'step01.rb' 2 | require_relative 'step02.rb' 3 | require_relative 'step03.rb' 4 | require_relative 'step04.rb' 5 | require_relative 'step05.rb' 6 | require_relative 'step06.rb' 7 | require_relative 'step07.rb' 8 | 9 | require 'sketchup.rb' 10 | 11 | module Examples 12 | module MaterialInspector 13 | TUTORIALS = self 14 | module TutorialController 15 | 16 | OSX = Sketchup.platform == :platform_osx 17 | 18 | def self.create_step(index, title, &block) 19 | cmd = UI::Command.new(title, &block) 20 | cmd.tooltip = title 21 | ext = OSX ? 'pdf' : 'svg' 22 | # https://www.flaticon.com/free-icons/numbers_931 23 | icon = File.join(__dir__, 'images', "#{index}.#{ext}") 24 | cmd.small_icon = icon 25 | cmd.large_icon = icon 26 | cmd 27 | end 28 | 29 | unless file_loaded?(__FILE__) 30 | step01 = self.create_step(1, 'Step 01 - Basic Usage') { 31 | TUTORIALS::Step01.show_dialog 32 | } 33 | 34 | step02 = self.create_step(2, 'Step 02 - (Partially) Reusable window') { 35 | TUTORIALS::Step02.show_dialog 36 | } 37 | 38 | step03 = self.create_step(3, 'Step 03 - (Fully) Reusable Window') { 39 | TUTORIALS::Step03.show_dialog 40 | } 41 | 42 | step04 = self.create_step(4, 'Step 04 - Adding JS Framework') { 43 | TUTORIALS::Step04.show_dialog 44 | } 45 | 46 | step05 = self.create_step(5, 'Step 05 - Realistic Example') { 47 | TUTORIALS::Step05.show_dialog 48 | } 49 | 50 | step06 = self.create_step(6, 'Step 06 - Adding UI Framework') { 51 | TUTORIALS::Step06.show_dialog 52 | } 53 | 54 | step07 = self.create_step(7, 'Step 07 - Tuning UI') { 55 | TUTORIALS::Step07.show_dialog 56 | } 57 | 58 | toolbar = UI::Toolbar.new('HtmlDialog Examples') 59 | toolbar.add_item(step01) 60 | toolbar.add_item(step02) 61 | toolbar.add_item(step03) 62 | toolbar.add_separator 63 | toolbar.add_item(step04) 64 | toolbar.add_item(step05) 65 | toolbar.add_separator 66 | toolbar.add_item(step06) 67 | toolbar.add_item(step07) 68 | toolbar.restore 69 | file_loaded(__FILE__) 70 | end 71 | 72 | end # TutorialController 73 | end # MaterialInspector 74 | end # Examples -------------------------------------------------------------------------------- /src/vendor/modus/bootstrap.bundle.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v4.4.1 (https://getbootstrap.com/) 3 | * Copyright 2011-2019 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("jquery")):"function"==typeof define&&define.amd?define(["exports","jquery"],t):t((e=e||self).bootstrap={},e.jQuery)}(this,function(e,p){"use strict";function i(e,t){for(var n=0;nthis._items.length-1||e<0))if(this._isSliding)p(this._element).one(V.SLID,function(){return t.to(e)});else{if(n===e)return this.pause(),void this.cycle();var i=n=i.clientWidth&&n>=i.clientHeight}),u=0l[e]&&!i.escapeWithReference&&(n=Math.min(h[t],l[e]-("right"===e?h.width:h.height))),Ye({},t,n)}};return c.forEach(function(e){var t=-1!==["left","top"].indexOf(e)?"primary":"secondary";h=ze({},h,u[t](e))}),e.offsets.popper=h,e},priority:["left","right","top","bottom"],padding:5,boundariesElement:"scrollParent"},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,n=t.popper,i=t.reference,o=e.placement.split("-")[0],r=Math.floor,s=-1!==["top","bottom"].indexOf(o),a=s?"right":"bottom",l=s?"left":"top",c=s?"width":"height";return n[a]r(i[a])&&(e.offsets.popper[l]=r(i[a])),e}},arrow:{order:500,enabled:!0,fn:function(e,t){var n;if(!gt(e.instance.modifiers,"arrow","keepTogether"))return e;var i=t.element;if("string"==typeof i){if(!(i=e.instance.popper.querySelector(i)))return e}else if(!e.instance.popper.contains(i))return console.warn("WARNING: `arrow.element` must be child of its popper element!"),e;var o=e.placement.split("-")[0],r=e.offsets,s=r.popper,a=r.reference,l=-1!==["left","right"].indexOf(o),c=l?"height":"width",h=l?"Top":"Left",u=h.toLowerCase(),f=l?"left":"top",d=l?"bottom":"right",p=nt(i)[c];a[d]-ps[d]&&(e.offsets.popper[u]+=a[u]+p-s[d]),e.offsets.popper=Xe(e.offsets.popper);var m=a[u]+a[c]/2-p/2,g=ke(e.instance.popper),_=parseFloat(g["margin"+h],10),v=parseFloat(g["border"+h+"Width"],10),y=m-e.offsets.popper[u]-_-v;return y=Math.max(Math.min(s[c]-p,y),0),e.arrowElement=i,e.offsets.arrow=(Ye(n={},u,Math.round(y)),Ye(n,f,""),n),e},element:"[x-arrow]"},flip:{order:600,enabled:!0,fn:function(m,g){if(at(m.instance.modifiers,"inner"))return m;if(m.flipped&&m.placement===m.originalPlacement)return m;var _=Ze(m.instance.popper,m.instance.reference,g.padding,g.boundariesElement,m.positionFixed),v=m.placement.split("-")[0],y=it(v),E=m.placement.split("-")[1]||"",b=[];switch(g.behavior){case Et:b=[v,y];break;case bt:b=yt(v);break;case wt:b=yt(v,!0);break;default:b=g.behavior}return b.forEach(function(e,t){if(v!==e||b.length===t+1)return m;v=m.placement.split("-")[0],y=it(v);var n=m.offsets.popper,i=m.offsets.reference,o=Math.floor,r="left"===v&&o(n.right)>o(i.left)||"right"===v&&o(n.left)o(i.top)||"bottom"===v&&o(n.top)o(_.right),l=o(n.top)o(_.bottom),h="left"===v&&s||"right"===v&&a||"top"===v&&l||"bottom"===v&&c,u=-1!==["top","bottom"].indexOf(v),f=!!g.flipVariations&&(u&&"start"===E&&s||u&&"end"===E&&a||!u&&"start"===E&&l||!u&&"end"===E&&c),d=!!g.flipVariationsByContent&&(u&&"start"===E&&a||u&&"end"===E&&s||!u&&"start"===E&&c||!u&&"end"===E&&l),p=f||d;(r||h||p)&&(m.flipped=!0,(r||h)&&(v=b[t+1]),p&&(E=function(e){return"end"===e?"start":"start"===e?"end":e}(E)),m.placement=v+(E?"-"+E:""),m.offsets.popper=ze({},m.offsets.popper,ot(m.instance.popper,m.offsets.reference,m.placement)),m=st(m.instance.modifiers,m,"flip"))}),m},behavior:"flip",padding:5,boundariesElement:"viewport",flipVariations:!1,flipVariationsByContent:!1},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,n=t.split("-")[0],i=e.offsets,o=i.popper,r=i.reference,s=-1!==["left","right"].indexOf(n),a=-1===["top","left"].indexOf(n);return o[s?"left":"top"]=r[n]-(a?o[s?"width":"height"]:0),e.placement=it(t),e.offsets.popper=Xe(o),e}},hide:{order:800,enabled:!0,fn:function(e){if(!gt(e.instance.modifiers,"hide","preventOverflow"))return e;var t=e.offsets.reference,n=rt(e.instance.modifiers,function(e){return"preventOverflow"===e.name}).boundaries;if(t.bottomn.right||t.top>n.bottom||t.rightdocument.documentElement.clientHeight;!this._isBodyOverflowing&&e&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!e&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},e._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},e._checkScrollbar=function(){var e=document.body.getBoundingClientRect();this._isBodyOverflowing=e.left+e.right
',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent",sanitize:!0,sanitizeFn:null,whiteList:Cn,popperConfig:null},Fn="show",Mn="out",Wn={HIDE:"hide"+Nn,HIDDEN:"hidden"+Nn,SHOW:"show"+Nn,SHOWN:"shown"+Nn,INSERTED:"inserted"+Nn,CLICK:"click"+Nn,FOCUSIN:"focusin"+Nn,FOCUSOUT:"focusout"+Nn,MOUSEENTER:"mouseenter"+Nn,MOUSELEAVE:"mouseleave"+Nn},Un="fade",Bn="show",qn=".tooltip-inner",Kn=".arrow",Qn="hover",Vn="focus",Yn="click",zn="manual",Xn=function(){function i(e,t){if("undefined"==typeof St)throw new TypeError("Bootstrap's tooltips require Popper.js (https://popper.js.org/)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=e,this.config=this._getConfig(t),this.tip=null,this._setListeners()}var e=i.prototype;return e.enable=function(){this._isEnabled=!0},e.disable=function(){this._isEnabled=!1},e.toggleEnabled=function(){this._isEnabled=!this._isEnabled},e.toggle=function(e){if(this._isEnabled)if(e){var t=this.constructor.DATA_KEY,n=p(e.currentTarget).data(t);n||(n=new this.constructor(e.currentTarget,this._getDelegateConfig()),p(e.currentTarget).data(t,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(p(this.getTipElement()).hasClass(Bn))return void this._leave(null,this);this._enter(null,this)}},e.dispose=function(){clearTimeout(this._timeout),p.removeData(this.element,this.constructor.DATA_KEY),p(this.element).off(this.constructor.EVENT_KEY),p(this.element).closest(".modal").off("hide.bs.modal",this._hideModalHandler),this.tip&&p(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,this._activeTrigger=null,this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},e.show=function(){var t=this;if("none"===p(this.element).css("display"))throw new Error("Please use show on visible elements");var e=p.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){p(this.element).trigger(e);var n=m.findShadowRoot(this.element),i=p.contains(null!==n?n:this.element.ownerDocument.documentElement,this.element);if(e.isDefaultPrevented()||!i)return;var o=this.getTipElement(),r=m.getUID(this.constructor.NAME);o.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&p(o).addClass(Un);var s="function"==typeof this.config.placement?this.config.placement.call(this,o,this.element):this.config.placement,a=this._getAttachment(s);this.addAttachmentClass(a);var l=this._getContainer();p(o).data(this.constructor.DATA_KEY,this),p.contains(this.element.ownerDocument.documentElement,this.tip)||p(o).appendTo(l),p(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new St(this.element,o,this._getPopperConfig(a)),p(o).addClass(Bn),"ontouchstart"in document.documentElement&&p(document.body).children().on("mouseover",null,p.noop);var c=function(){t.config.animation&&t._fixTransition();var e=t._hoverState;t._hoverState=null,p(t.element).trigger(t.constructor.Event.SHOWN),e===Mn&&t._leave(null,t)};if(p(this.tip).hasClass(Un)){var h=m.getTransitionDurationFromElement(this.tip);p(this.tip).one(m.TRANSITION_END,c).emulateTransitionEnd(h)}else c()}},e.hide=function(e){function t(){n._hoverState!==Fn&&i.parentNode&&i.parentNode.removeChild(i),n._cleanTipClass(),n.element.removeAttribute("aria-describedby"),p(n.element).trigger(n.constructor.Event.HIDDEN),null!==n._popper&&n._popper.destroy(),e&&e()}var n=this,i=this.getTipElement(),o=p.Event(this.constructor.Event.HIDE);if(p(this.element).trigger(o),!o.isDefaultPrevented()){if(p(i).removeClass(Bn),"ontouchstart"in document.documentElement&&p(document.body).children().off("mouseover",null,p.noop),this._activeTrigger[Yn]=!1,this._activeTrigger[Vn]=!1,this._activeTrigger[Qn]=!1,p(this.tip).hasClass(Un)){var r=m.getTransitionDurationFromElement(i);p(i).one(m.TRANSITION_END,t).emulateTransitionEnd(r)}else t();this._hoverState=""}},e.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},e.isWithContent=function(){return Boolean(this.getTitle())},e.addAttachmentClass=function(e){p(this.getTipElement()).addClass(Ln+"-"+e)},e.getTipElement=function(){return this.tip=this.tip||p(this.config.template)[0],this.tip},e.setContent=function(){var e=this.getTipElement();this.setElementContent(p(e.querySelectorAll(qn)),this.getTitle()),p(e).removeClass(Un+" "+Bn)},e.setElementContent=function(e,t){"object"!=typeof t||!t.nodeType&&!t.jquery?this.config.html?(this.config.sanitize&&(t=In(t,this.config.whiteList,this.config.sanitizeFn)),e.html(t)):e.text(t):this.config.html?p(t).parent().is(e)||e.empty().append(t):e.text(p(t).text())},e.getTitle=function(){var e=this.element.getAttribute("data-original-title");return e=e||("function"==typeof this.config.title?this.config.title.call(this.element):this.config.title)},e._getPopperConfig=function(e){var t=this;return l({},{placement:e,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:Kn},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(e){e.originalPlacement!==e.placement&&t._handlePopperPlacementChange(e)},onUpdate:function(e){return t._handlePopperPlacementChange(e)}},{},this.config.popperConfig)},e._getOffset=function(){var t=this,e={};return"function"==typeof this.config.offset?e.fn=function(e){return e.offsets=l({},e.offsets,{},t.config.offset(e.offsets,t.element)||{}),e}:e.offset=this.config.offset,e},e._getContainer=function(){return!1===this.config.container?document.body:m.isElement(this.config.container)?p(this.config.container):p(document).find(this.config.container)},e._getAttachment=function(e){return Hn[e.toUpperCase()]},e._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(e){if("click"===e)p(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(e){return i.toggle(e)});else if(e!==zn){var t=e===Qn?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=e===Qn?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;p(i.element).on(t,i.config.selector,function(e){return i._enter(e)}).on(n,i.config.selector,function(e){return i._leave(e)})}}),this._hideModalHandler=function(){i.element&&i.hide()},p(this.element).closest(".modal").on("hide.bs.modal",this._hideModalHandler),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},e._fixTitle=function(){var e=typeof this.element.getAttribute("data-original-title");!this.element.getAttribute("title")&&"string"==e||(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},e._enter=function(e,t){var n=this.constructor.DATA_KEY;(t=t||p(e.currentTarget).data(n))||(t=new this.constructor(e.currentTarget,this._getDelegateConfig()),p(e.currentTarget).data(n,t)),e&&(t._activeTrigger["focusin"===e.type?Vn:Qn]=!0),p(t.getTipElement()).hasClass(Bn)||t._hoverState===Fn?t._hoverState=Fn:(clearTimeout(t._timeout),t._hoverState=Fn,t.config.delay&&t.config.delay.show?t._timeout=setTimeout(function(){t._hoverState===Fn&&t.show()},t.config.delay.show):t.show())},e._leave=function(e,t){var n=this.constructor.DATA_KEY;(t=t||p(e.currentTarget).data(n))||(t=new this.constructor(e.currentTarget,this._getDelegateConfig()),p(e.currentTarget).data(n,t)),e&&(t._activeTrigger["focusout"===e.type?Vn:Qn]=!1),t._isWithActiveTrigger()||(clearTimeout(t._timeout),t._hoverState=Mn,t.config.delay&&t.config.delay.hide?t._timeout=setTimeout(function(){t._hoverState===Mn&&t.hide()},t.config.delay.hide):t.hide())},e._isWithActiveTrigger=function(){for(var e in this._activeTrigger)if(this._activeTrigger[e])return!0;return!1},e._getConfig=function(e){var t=p(this.element).data();return Object.keys(t).forEach(function(e){-1!==xn.indexOf(e)&&delete t[e]}),"number"==typeof(e=l({},this.constructor.Default,{},t,{},"object"==typeof e&&e?e:{})).delay&&(e.delay={show:e.delay,hide:e.delay}),"number"==typeof e.title&&(e.title=e.title.toString()),"number"==typeof e.content&&(e.content=e.content.toString()),m.typeCheckConfig(An,e,this.constructor.DefaultType),e.sanitize&&(e.template=In(e.template,e.whiteList,e.sanitizeFn)),e},e._getDelegateConfig=function(){var e={};if(this.config)for(var t in this.config)this.constructor.Default[t]!==this.config[t]&&(e[t]=this.config[t]);return e},e._cleanTipClass=function(){var e=p(this.getTipElement()),t=e.attr("class").match(Pn);null!==t&&t.length&&e.removeClass(t.join(""))},e._handlePopperPlacementChange=function(e){var t=e.instance;this.tip=t.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(e.placement))},e._fixTransition=function(){var e=this.getTipElement(),t=this.config.animation;null===e.getAttribute("x-placement")&&(p(e).removeClass(Un),this.config.animation=!1,this.hide(),this.show(),this.config.animation=t)},i._jQueryInterface=function(n){return this.each(function(){var e=p(this).data(On),t="object"==typeof n&&n;if((e||!/dispose|hide/.test(n))&&(e||(e=new i(this,t),p(this).data(On,e)),"string"==typeof n)){if("undefined"==typeof e[n])throw new TypeError('No method named "'+n+'"');e[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.4.1"}},{key:"Default",get:function(){return Rn}},{key:"NAME",get:function(){return An}},{key:"DATA_KEY",get:function(){return On}},{key:"Event",get:function(){return Wn}},{key:"EVENT_KEY",get:function(){return Nn}},{key:"DefaultType",get:function(){return jn}}]),i}();p.fn[An]=Xn._jQueryInterface,p.fn[An].Constructor=Xn,p.fn[An].noConflict=function(){return p.fn[An]=kn,Xn._jQueryInterface};var Gn="popover",$n="bs.popover",Jn="."+$n,Zn=p.fn[Gn],ei="bs-popover",ti=new RegExp("(^|\\s)"+ei+"\\S+","g"),ni=l({},Xn.Default,{placement:"right",trigger:"click",content:"",template:''}),ii=l({},Xn.DefaultType,{content:"(string|element|function)"}),oi="fade",ri="show",si=".popover-header",ai=".popover-body",li={HIDE:"hide"+Jn,HIDDEN:"hidden"+Jn,SHOW:"show"+Jn,SHOWN:"shown"+Jn,INSERTED:"inserted"+Jn,CLICK:"click"+Jn,FOCUSIN:"focusin"+Jn,FOCUSOUT:"focusout"+Jn,MOUSEENTER:"mouseenter"+Jn,MOUSELEAVE:"mouseleave"+Jn},ci=function(e){function i(){return e.apply(this,arguments)||this}!function(e,t){e.prototype=Object.create(t.prototype),(e.prototype.constructor=e).__proto__=t}(i,e);var t=i.prototype;return t.isWithContent=function(){return this.getTitle()||this._getContent()},t.addAttachmentClass=function(e){p(this.getTipElement()).addClass(ei+"-"+e)},t.getTipElement=function(){return this.tip=this.tip||p(this.config.template)[0],this.tip},t.setContent=function(){var e=p(this.getTipElement());this.setElementContent(e.find(si),this.getTitle());var t=this._getContent();"function"==typeof t&&(t=t.call(this.element)),this.setElementContent(e.find(ai),t),e.removeClass(oi+" "+ri)},t._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},t._cleanTipClass=function(){var e=p(this.getTipElement()),t=e.attr("class").match(ti);null!==t&&0=this._offsets[o]&&("undefined"==typeof this._offsets[o+1]||e+~]|"+R+")"+R+"*"),U=new RegExp(R+"|>"),V=new RegExp(W),X=new RegExp("^"+B+"$"),Q={ID:new RegExp("^#("+B+")"),CLASS:new RegExp("^\\.("+B+")"),TAG:new RegExp("^("+B+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+W),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+R+"*(even|odd|(([+-]|)(\\d*)n|)"+R+"*(?:([+-]|)"+R+"*(\\d+)|))"+R+"*\\)|)","i"),bool:new RegExp("^(?:"+I+")$","i"),needsContext:new RegExp("^"+R+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+R+"*((?:-\\d)?\\d*)"+R+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,G=/^(?:input|select|textarea|button)$/i,K=/^h\d$/i,J=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\([\\da-f]{1,6}"+R+"?|("+R+")|.)","ig"),ne=function(e,t,n){var r="0x"+t-65536;return r!=r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){C()},ae=xe(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{O.apply(t=P.call(m.childNodes),m.childNodes),t[m.childNodes.length].nodeType}catch(e){O={apply:t.length?function(e,t){q.apply(e,P.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,d=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==d&&9!==d&&11!==d)return n;if(!r&&((e?e.ownerDocument||e:m)!==T&&C(e),e=e||T,E)){if(11!==d&&(u=Z.exec(t)))if(i=u[1]){if(9===d){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return O.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&p.getElementsByClassName&&e.getElementsByClassName)return O.apply(n,e.getElementsByClassName(i)),n}if(p.qsa&&!S[t+" "]&&(!v||!v.test(t))&&(1!==d||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===d&&U.test(t)){(s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=N),o=(l=h(t)).length;while(o--)l[o]="#"+s+" "+be(l[o]);c=l.join(","),f=ee.test(t)&&ye(e.parentNode)||e}try{return O.apply(n,f.querySelectorAll(c)),n}catch(e){S(t,!0)}finally{s===N&&e.removeAttribute("id")}}}return g(t.replace(F,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>x.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[N]=!0,e}function ce(e){var t=T.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)x.attrHandle[n[r]]=t}function de(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function pe(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in p=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},C=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:m;return r!==T&&9===r.nodeType&&r.documentElement&&(a=(T=r).documentElement,E=!i(T),m!==T&&(n=T.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),p.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),p.getElementsByTagName=ce(function(e){return e.appendChild(T.createComment("")),!e.getElementsByTagName("*").length}),p.getElementsByClassName=J.test(T.getElementsByClassName),p.getById=ce(function(e){return a.appendChild(e).id=N,!T.getElementsByName||!T.getElementsByName(N).length}),p.getById?(x.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},x.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(x.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},x.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),x.find.TAG=p.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):p.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},x.find.CLASS=p.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(p.qsa=J.test(T.querySelectorAll))&&(ce(function(e){a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+R+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+R+"*(?:value|"+I+")"),e.querySelectorAll("[id~="+N+"-]").length||v.push("~="),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+N+"+*").length||v.push(".#.+[+~]")}),ce(function(e){e.innerHTML="";var t=T.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+R+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(p.matchesSelector=J.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){p.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",W)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=J.test(a.compareDocumentPosition),y=t||J.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!p.sortDetached&&t.compareDocumentPosition(e)===n?e===T||e.ownerDocument===m&&y(m,e)?-1:t===T||t.ownerDocument===m&&y(m,t)?1:u?H(u,e)-H(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===T?-1:t===T?1:i?-1:o?1:u?H(u,e)-H(u,t):0;if(i===o)return de(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?de(a[r],s[r]):a[r]===m?-1:s[r]===m?1:0}),T},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if((e.ownerDocument||e)!==T&&C(e),p.matchesSelector&&E&&!S[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||p.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){S(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&V.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=d[e+" "];return t||(t=new RegExp("(^|"+R+")"+e+"("+R+"|$)"))&&d(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function L(e,n,r){return x(n)?E.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?E.grep(e,function(e){return e===n!==r}):"string"!=typeof n?E.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(E.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof E?t[0]:t,E.merge(this,E.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:v,!0)),D.test(r[1])&&E.isPlainObject(t))for(r in t)x(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=v.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):x(e)?void 0!==n.ready?n.ready(e):e(E):E.makeArray(e,this)}).prototype=E.fn,j=E(v);var O=/^(?:parents|prev(?:Until|All))/,P={children:!0,contents:!0,next:!0,prev:!0};function H(e,t){while((e=e[t])&&1!==e.nodeType);return e}E.fn.extend({has:function(e){var t=E(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,pe=/^$|^module$|\/(?:java|ecma)script/i,he={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ge(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&S(e,t)?E.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;nx",b.noCloneChecked=!!ye.cloneNode(!0).lastChild.defaultValue;var we=/^key/,Ce=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Te=/^([^.]*)(?:\.(.+)|)/;function Ee(){return!0}function Ne(){return!1}function Ae(e,t){return e===function(){try{return v.activeElement}catch(e){}}()==("focus"===t)}function ke(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)ke(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Ne;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return E().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=E.guid++)),e.each(function(){E.event.add(this,t,i,r,n)})}function Se(e,i,o){o?(G.set(e,i,!1),E.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=G.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(E.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),G.set(this,i,r),t=o(this,i),this[i](),r!==(n=G.get(this,i))||t?G.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(G.set(this,i,{value:E.event.trigger(E.extend(r[0],E.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===G.get(e,i)&&E.event.add(e,i,Ee)}E.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,d,p,h,g,v=G.get(t);if(v){n.handler&&(n=(o=n).handler,i=o.selector),i&&E.find.matchesSelector(ie,i),n.guid||(n.guid=E.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof E&&E.event.triggered!==e.type?E.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(I)||[""]).length;while(l--)p=g=(s=Te.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),p&&(f=E.event.special[p]||{},p=(i?f.delegateType:f.bindType)||p,f=E.event.special[p]||{},c=E.extend({type:p,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&E.expr.match.needsContext.test(i),namespace:h.join(".")},o),(d=u[p])||((d=u[p]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(p,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?d.splice(d.delegateCount++,0,c):d.push(c),E.event.global[p]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,d,p,h,g,v=G.hasData(e)&&G.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(I)||[""]).length;while(l--)if(p=g=(s=Te.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),p){f=E.event.special[p]||{},d=u[p=(r?f.delegateType:f.bindType)||p]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=d.length;while(o--)c=d[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(d.splice(o,1),c.selector&&d.delegateCount--,f.remove&&f.remove.call(e,c));a&&!d.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||E.removeEvent(e,p,v.handle),delete u[p])}else for(p in u)E.event.remove(e,p+t[l],n,r,!0);E.isEmptyObject(u)&&G.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=E.event.fix(e),u=new Array(arguments.length),l=(G.get(this,"events")||{})[s.type]||[],c=E.event.special[s.type]||{};for(u[0]=s,t=1;t\x20\t\r\n\f]*)[^>]*)\/>/gi,Le=/\s*$/g;function Oe(e,t){return S(e,"table")&&S(11!==t.nodeType?t:t.firstChild,"tr")&&E(e).children("tbody")[0]||e}function Pe(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Ie(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(G.hasData(e)&&(o=G.access(e),a=G.set(t,o),l=o.events))for(i in delete a.handle,a.events={},l)for(n=0,r=l[i].length;n")},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=oe(e);if(!(b.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||E.isXMLDoc(e)))for(a=ge(c),r=0,i=(o=ge(e)).length;r
",2===pt.childNodes.length),E.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(b.createHTMLDocument?((r=(t=v.implementation.createHTMLDocument("")).createElement("base")).href=v.location.href,t.head.appendChild(r)):t=v),o=!n&&[],(i=D.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&E(o).remove(),E.merge([],i.childNodes)));var r,i,o},E.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=E.css(e,"position"),c=E(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=E.css(e,"top"),u=E.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),x(t)&&(t=t.call(e,n,E.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},E.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){E.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===E.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===E.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=E(e).offset()).top+=E.css(e,"borderTopWidth",!0),i.left+=E.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-E.css(r,"marginTop",!0),left:t.left-i.left-E.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===E.css(e,"position"))e=e.offsetParent;return e||ie})}}),E.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;E.fn[t]=function(e){return z(this,function(e,t,n){var r;if(w(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),E.each(["top","left"],function(e,n){E.cssHooks[n]=ze(b.pixelPosition,function(e,t){if(t)return t=Fe(e,n),Me.test(t)?E(e).position()[n]+"px":t})}),E.each({Height:"height",Width:"width"},function(a,s){E.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){E.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return z(this,function(e,t,n){var r;return w(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?E.css(e,t,i):E.style(e,t,n,i)},s,n?e:void 0,n)}})}),E.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){E.fn[n]=function(e,t){return 0