Commit 07a6a779854eb2c66930082f3fa2469b8cf58390

Authored by hitier
2 parents edd13fea 2d84dcc9

Status history pie chart

CHANGELOG.md
... ... @@ -24,6 +24,14 @@ or major refactoring improvments.
24 24  
25 25 ## Unreleased
26 26  
  27 +## [0.4.pre-3] - 2021-06-23 - Status Pie Chart
  28 +### New
  29 +Status Pie Chart
  30 +Status History table and ingest
  31 +
  32 +### Changed
  33 +D3js drawing scripts refactoring
  34 +
27 35 ## [0.4.pre-2] - 2021-05-31 - Edit/Delete Links
28 36 ### New
29 37 Excell export
... ...
VERSION.txt
1   -0.4.pre-2
  1 +0.4.pre-3
... ...
app/commands/commands.py
1 1 import csv
2 2 import os
3 3 import sys
  4 +from datetime import datetime
4 5  
5 6 import click
6 7 import random
... ... @@ -10,7 +11,7 @@ from sqlalchemy.exc import IntegrityError
10 11 from sqlalchemy.sql import func
11 12  
12 13 from app.models import db, Agent, Service, Project, Capacity, Period, Charge, AgentStatus, Company, AgentBap, \
13   - AgentGrade, Category, Label, ProjectLabel, CategoryLabel
  14 + AgentGrade, Category, Label, ProjectLabel, CategoryLabel, AgentHistory
14 15 # TODO: rename to methods and add get_roles()
15 16 from app.auth.models import User, _nameToRole, _roleToName
16 17  
... ... @@ -244,7 +245,7 @@ def feed_from_irap(csv_file_name):
244 245 # Feed categories and labels
245 246 #
246 247 for category, labels in categorie_labels.items():
247   - print(category)
  248 + print(f"Category: {category}")
248 249 n_c = Category.query.filter_by(name=category).one()
249 250 for label in labels:
250 251 print(label)
... ... @@ -293,10 +294,17 @@ def feed_from_irap(csv_file_name):
293 294 db.session.add(n_a)
294 295 db.session.commit()
295 296  
296   - # Feed agents from agents list previously filled
  297 + # Feed status history
  298 + # This is done for the current period only
  299 + # as this csv format doesnt hold any history information
297 300 #
  301 + for a in Agent.query.all():
  302 + _p = Period.query.filter(Period.name == datetime.today().year).one()
  303 + n_ap = AgentHistory(period_id=_p.id, agent_id=a.id, status_id=a.status_id)
  304 + db.session.add(n_ap)
  305 + db.session.commit()
298 306  
299   - # Now feed the charges.
  307 + # Now feed both charges and status history
300 308 #
301 309 # At least one for each csv row
302 310 # At most one for each year
... ... @@ -309,22 +317,30 @@ def feed_from_irap(csv_file_name):
309 317 # TODO: period names should come from db request
310 318 for period_name in range(2011, 2030):
311 319 t = Period.query.filter(Period.name == period_name).one()
312   - charge = r[f"{period_name}"]
  320 + charge_value = r[f"{period_name}"]
313 321 # Charge are stored as percent in db, but as fraction of ETP in irap csv
314 322 # we make the conversion here.
315 323 try:
316   - charge = int(100 * float(charge))
  324 + charge_value = int(100 * float(charge_value))
317 325 except ValueError:
318   - charge = 0
319   - if charge == 0:
  326 + charge_value = 0
  327 + if charge_value == 0:
320 328 continue
321 329 n_c = Charge(agent_id=a.id,
322 330 project_id=p.id,
323 331 service_id=s.id,
324 332 capacity_id=c.id,
325 333 period_id=t.id,
326   - charge_rate=charge)
327   - db.session.add(n_c)
  334 + charge_rate=charge_value)
  335 + status_value = r[status_key].strip().upper()
  336 + if not status_value:
  337 + continue
  338 + st = AgentStatus.query.filter(AgentStatus.name == status_value).one_or_none()
  339 + if st is None:
  340 + continue
  341 + print(f"Adding history p: {t.name}, a: {a.firstname}, s: {st.name}")
  342 + n_ap = AgentHistory(period_id=t.id, agent_id=a.id, status_id=st.id)
  343 + db.session.add_all([n_c, n_ap])
328 344 db.session.commit()
329 345  
330 346  
... ... @@ -337,7 +353,7 @@ def feed_from_lesia():
337 353 """
338 354 from .lesia_db import lesia_agent, lesia_session, lesia_service, lesia_project, \
339 355 lesia_fonction, lesia_periods, lesia_affectation, lesia_domains, lesia_poles, \
340   - lesia_domainprojects
  356 + lesia_domainprojects, lesia_agentsemestres, lesia_statutagent
341 357  
342 358 # Feed all lesia 'domaine' names and link to new category "Domaine"
343 359 #
... ... @@ -432,6 +448,18 @@ def feed_from_lesia():
432 448 db.session.add(n_c)
433 449 db.session.commit()
434 450  
  451 + for sa in lesia_session.query(lesia_statutagent):
  452 + n_as = AgentStatus(id=sa.IDstatut, name=sa.nomstatut)
  453 + print(f"Adding Status {sa.nomstatut}")
  454 + db.session.add(n_as)
  455 + db.session.commit()
  456 +
  457 + for las in lesia_session.query(lesia_agentsemestres):
  458 + _p = Period.query.filter(Period.name == las.semestre_id).one()
  459 + n_ap = AgentHistory(period_id=_p.id, agent_id=las.agent_id, status_id=las.statut_id)
  460 + db.session.add(n_ap)
  461 + db.session.commit()
  462 +
435 463  
436 464 @bp.cli.command("fake_lesia_names")
437 465 def fake_lesia_names():
... ...
app/commands/lesia_db.py
... ... @@ -31,3 +31,5 @@ lesia_periods = lesia_base.classes.gestit_semestres
31 31 lesia_domains = lesia_base.classes.gestit_domaines
32 32 lesia_poles = lesia_base.classes.poles
33 33 lesia_domainprojects = lesia_base.classes.gestit_domaine_projets
  34 +lesia_agentsemestres = lesia_base.classes.gestit_agent_semestres
  35 +lesia_statutagent = lesia_base.classes.statutagent
... ...
app/db_mgr.py
... ... @@ -166,11 +166,11 @@ def charges_by_project(project_id):
166 166 return nocomma_results
167 167  
168 168  
169   -def charges_by_project_stacked(project_id, category="service"):
  169 +def charges_by_project_stacked(project_id, sort_key="service"):
170 170 """
171 171 Build the list of charges for one project, period by period
172 172 :param project_id: the project's id we want to return data for
173   - :param category: what dict to build for each period, 'service' or 'capacity' ?
  173 + :param sort_key: what dict to build for each period, 'service' or 'capacity' ?
174 174 :return: a 2 dim table with header as first line and datas next, of the form
175 175 period, category_0, category_1, ....., category_n,
176 176 per_0, value_00, value_01, ....., value_0n,
... ... @@ -181,7 +181,7 @@ def charges_by_project_stacked(project_id, category="service"):
181 181  
182 182 TODO: common with charges_by_agent_stacked(agent_id): code to extrat
183 183 """
184   - if category == 'capacity':
  184 + if sort_key == 'capacity':
185 185 category_table = 'capacity'
186 186 sql_req = """
187 187 select sum(c.charge_rate)
... ... @@ -190,7 +190,7 @@ def charges_by_project_stacked(project_id, category="service"):
190 190 group by c1.id
191 191 order by c1.id
192 192 """
193   - elif category == 'service':
  193 + elif sort_key == 'service':
194 194 category_table = 'service'
195 195 sql_req = """
196 196 select sum(c.charge_rate)
... ... @@ -284,7 +284,8 @@ def charges_for_projects_stacked():
284 284 select p.name, IFNULL(s.tot_charge, 0) tot_charge
285 285 from project p
286 286 left join
287   - (select c.project_id, sum(c.charge_rate) as tot_charge from charge c where c.period_id = {} group by c.project_id) s
  287 + (select c.project_id, sum(c.charge_rate) as tot_charge
  288 + from charge c where c.period_id = {} group by c.project_id) s
288 289 on s.project_id = p.id
289 290 group by p.id
290 291 order by p.id;
... ... @@ -359,7 +360,7 @@ def charges_by_agent_tabled(agent_id):
359 360 for line in results:
360 361 nocomma_line = [str(cell) for cell in line]
361 362 nocomma_results.append(nocomma_line)
362   - headers = ["Periode", "Projet", "Service", "Fonction", "Charge"]
  363 + headers = ["Période", "Projet", "Service", "Fonction", "Charge"]
363 364 nocomma_results.insert(0, headers)
364 365 return nocomma_results
365 366  
... ... @@ -379,3 +380,18 @@ def charges_by_agent(agent_id):
379 380 else:
380 381 all_charges.append([p, 0])
381 382 return all_charges
  383 +
  384 +
  385 +def count_agents_by_status(period_id):
  386 + sql_txt = f"""
  387 + select s.name, IFNULL(h.status_count, 0) as status_count
  388 + from agent_status s
  389 + left join
  390 + (select status_id, count(*) as status_count
  391 + from agent_history where period_id = {period_id} group by status_id) as h
  392 + on s.id = h.status_id
  393 + order by s.id;
  394 + """
  395 + request = db.session.execute(sql_txt)
  396 + all_counts = [["Statut", "Effectif"]] + [[f'{s_n}', f'{a_c}'] for (s_n, a_c) in request.fetchall()]
  397 + return all_counts
... ...
app/main/routes.py
... ... @@ -75,6 +75,15 @@ def agents():
75 75 agents=all_agents)
76 76  
77 77  
  78 +@bp.route('/agents/stats')
  79 +@role_required('project')
  80 +def agents_stats():
  81 + num_agents = len(Agent.query.all())
  82 + all_periods = Period.query.order_by(Period.name).all()
  83 + return render_template('agents_stats.html', subtitle="Statistiques des agents ({})".format(num_agents),
  84 + periods=all_periods)
  85 +
  86 +
78 87 @bp.route('/capacities')
79 88 @login_required
80 89 def capacities():
... ... @@ -225,11 +234,11 @@ def label_edit(label_id=None):
225 234 if label_id:
226 235 # then update existing
227 236 this_label = Label.query.get(int(label_id))
228   - done_string = updated_message+"."
  237 + done_string = updated_message + "."
229 238 else:
230 239 # or create from scratch
231 240 this_label = Label()
232   - done_string = added_message+"."
  241 + done_string = added_message + "."
233 242 # fill orm with form and write to db
234 243 this_label.from_request(request)
235 244 db.session.add(this_label)
... ... @@ -259,7 +268,7 @@ def category_edit(category_id=None):
259 268 if category_id:
260 269 # then update existing
261 270 this_category = Category.query.get(int(category_id))
262   - done_string = updated_message+"."
  271 + done_string = updated_message + "."
263 272 else:
264 273 # or create from scratch
265 274 this_category = Category()
... ... @@ -294,7 +303,7 @@ def project_edit(project_id=None):
294 303 if project_id:
295 304 # then update existing
296 305 this_project = Project.query.get(int(project_id))
297   - done_string = updated_message+"."
  306 + done_string = updated_message + "."
298 307 else:
299 308 # check name doesnt exist yet
300 309 existing_project = Project.query.filter(Project.name == request.form.get('name')).one_or_none()
... ... @@ -303,7 +312,7 @@ def project_edit(project_id=None):
303 312 return redirect(url_for('main.project_edit'))
304 313 # or create from scratch
305 314 this_project = Project()
306   - done_string = added_message+"."
  315 + done_string = added_message + "."
307 316 # fill orm with form and write to db
308 317 this_project.from_request(request)
309 318 db.session.add(this_project)
... ... @@ -345,11 +354,11 @@ def agent_edit(agent_id=None):
345 354 if agent_id:
346 355 # then update existing
347 356 this_agent = Agent.query.get(int(agent_id))
348   - done_string = updated_message+"."
  357 + done_string = updated_message + "."
349 358 else:
350 359 # or create from scratch
351 360 this_agent = Agent()
352   - done_string = added_message+"."
  361 + done_string = added_message + "."
353 362 # fill orm with form and write to db
354 363 this_agent.from_request(request)
355 364 db.session.add(this_agent)
... ... @@ -446,11 +455,11 @@ def array_to_csv(array, sep=','):
446 455 return resp
447 456  
448 457  
449   -@bp.route('/charge/project/<project_id>/<category>/<any_format>')
450   -@bp.route('/charge/project/<project_id>/<category>')
  458 +@bp.route('/charge/project/<project_id>/<sort_key>/<any_format>')
  459 +@bp.route('/charge/project/<project_id>/<sort_key>')
451 460 @role_required('project')
452   -def rest_charge_project(project_id, category, any_format='csv'):
453   - return array_to_any(db_mgr.charges_by_project_stacked(project_id, category), any_format)
  461 +def rest_charge_project(project_id, sort_key, any_format='csv'):
  462 + return array_to_any(db_mgr.charges_by_project_stacked(project_id, sort_key), any_format)
454 463  
455 464  
456 465 @bp.route('/charge/agent/<agent_id>/<any_format>')
... ... @@ -472,3 +481,10 @@ def rest_labels_stats(category_id, any_format=&#39;csv&#39;):
472 481 @role_required('project')
473 482 def rest_projects_stats(any_format='csv'):
474 483 return array_to_any(db_mgr.charges_for_projects_stacked(), any_format)
  484 +
  485 +
  486 +@bp.route('/count/agents/by_status/<period_id>/<any_format>')
  487 +@bp.route('/count/agents/by_status/<period_id>')
  488 +@role_required('project')
  489 +def rest_agents_status_count(period_id, any_format='csv'):
  490 + return array_to_any(db_mgr.count_agents_by_status(period_id), any_format)
... ...
app/main/static/css/charges.css
... ... @@ -4,14 +4,17 @@
4 4 stroke-width: 2px;
5 5 z-index: -1;
6 6 }
  7 +
7 8 .total-circle {
8   - fill: orangered;
  9 + fill: orangered;
9 10 }
  11 +
10 12 .total-circle:hover {
11 13 -webkit-filter: brightness(2.0);
12 14 -moz-filter: brightness(2.0);
13 15 filter: brightness(2.0);
14 16 }
  17 +
15 18 .total-tooltip {
16 19 border: orangered;
17 20 }
... ... @@ -28,8 +31,9 @@
28 31 stroke-width: 0px;
29 32 }
30 33  
31   -#charge_table, .charge_chart{
32   - width: 100%;
  34 +#charge_table, .charge_chart {
  35 + /*width: 100%;*/
  36 + /*height: 100%;*/
33 37 }
34 38  
35 39 #charge_table,
... ... @@ -46,11 +50,23 @@
46 50 }
47 51  
48 52 .svg_chart {
49   - margin-bottom: 50px;
  53 + /*margin-bottom: 50px;*/
50 54 background-color: #fAfAfA;
51 55 border: 1pt solid black;
52 56 display: inline-block;
53 57 width: 100%;
  58 + /*height: 100%;*/
  59 +}
  60 +
  61 +.pie_border {
  62 + stroke: black;
  63 + stroke-width: 0.8pt;
  64 + fill: none;
  65 +}
  66 +
  67 +path.pie {
  68 + stroke: #fafafa;
  69 + stroke-width: 1pt;
54 70 }
55 71  
56 72 text.legend {
... ... @@ -94,7 +110,7 @@ rect.bar {
94 110 i.export {
95 111 padding: 7px;
96 112 position: absolute;
97   - margin-top:8px;
  113 + margin-top: 8px;
98 114 right: 3%;
99 115 }
100 116  
... ... @@ -109,7 +125,7 @@ i.export:hover {
109 125 right: 2%;
110 126 pointer-events: auto;
111 127 width: 150px;
112   - margin-top:15px;
  128 + margin-top: 15px;
113 129 padding: 0;
114 130 }
115 131  
... ... @@ -137,7 +153,7 @@ i.export:hover {
137 153 height: 150px;
138 154 position: absolute;
139 155 padding: 10px;
140   - top:0;
  156 + top: 0;
141 157 right: 0;
142 158 /*background-color: #0b2e13;*/
143 159 }
144 160 \ No newline at end of file
... ...
app/main/static/js/charges.js
... ... @@ -3,89 +3,151 @@ function roundToTwo(num) {
3 3 return +(Math.round(num + "e+2") + "e-2");
4 4 }
5 5  
6   -function build_chart(div_selector, data_url, entity_name, category_type) {
  6 +function make_svg(div_selector, data_url, width, height,
  7 + margin = {top: 10, right: 50, bottom: 10, left: 10}) {
7 8  
8   - const main_elt = document.getElementById("main")
9 9  
10   - var margin = {top: 60, right: 100, bottom: 200, left: 90},
11   - width = main_elt.offsetWidth * 0.95 - margin.left - margin.right,
12   - height = 600 - margin.top - margin.bottom;
  10 + d3.select(div_selector).selectAll("*").remove();
  11 +
  12 + let svg_height = height + margin.top + margin.bottom;
  13 + let svg_width = width + margin.left + margin.right;
  14 +
  15 + const svg = d3.select(div_selector).append("svg")
  16 + .attr("viewBox", [0, 0, svg_width, svg_height])
  17 + .attr("preserveAspectRatio", 'xMinYMin meet')
  18 + .attr("class", "svg_chart")
  19 + .attr("width", svg_width)
  20 + .attr("height", svg_height)
  21 + .append("g")
  22 + .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
  23 +
  24 + d3.select(div_selector).node().appendChild(make_hamburger(data_url).node());
  25 +
  26 + // Show svg.g start and border
  27 + //
  28 + // svg.append('circle')
  29 + // .attr('r', 5)
  30 + // .attr('stroke', 'red');
  31 + // svg.append('rect')
  32 + // .attr('width', width)
  33 + // .attr('height', height)
  34 + // .attr('stroke', 'red')
  35 + // .attr('fill', 'none');
  36 +
  37 + return svg;
  38 +}
13 39  
14   - const height_ratio = 1.2; // how murch room to give above chart for lisibility
15 40  
16   - const tooltip_offset = {dx: 0, dy: 100}
17   - const tooltip_offset_dot = {dx: 20, dy: 60}
  41 +function make_title(svg, parent_width, titles, offset = {'x': 0, 'y': 0}) {
  42 + let l1_h = 20;
  43 + let l2_h = 20;
  44 + // move to center of containing element
  45 + let title_g = svg.append('g')
  46 + .attr("transform", "translate(" + (offset.x + parent_width / 2) + "," + offset.y + ")");
  47 + // display part 1
  48 + title_g.append("text")
  49 + .attr("text-anchor", "middle")
  50 + .style("font-size", "16px")
  51 + .style("font-weight", "bold")
  52 + .attr("y", l1_h)
  53 + .text(titles[0]);
  54 + // part 2
  55 + title_g.append("text")
  56 + .attr("y", l1_h + l2_h)
  57 + .attr("text-anchor", "middle")
  58 + .style("font-size", "12px")
  59 + .text(titles[1]);
  60 + return l1_h + l2_h;
  61 +}
18 62  
19   - const y_ticks_num = 5 // dont show to many y ticks for lisibility
  63 +function make_legend(svg, offset, parent_width, color_scale, mouseleave_callback, mouseover_callback) {
20 64  
21 65 const legend_rect_size = 15; // size of rectangle in legend
22 66 const legend_height = 20;
23 67 const legend_max_length = 20;
24 68 const legend_spacing = 5;
  69 + const text_margin_left = legend_rect_size + legend_spacing * 1.5;
25 70  
26   - const dot_radius = 6; // size of total line dot
27   -
28   -
29   - var colorScale = d3.scaleOrdinal([]); // Will be really set later by category type
30   -
31   - //
32   - // Configure chart by the category given as arg
33   - //
34   - var chart_title = ""
35   - var category_title = ""
36   - var draw_areas = () => void 0;
37   - var draw_total_line = () => void 0;
38   - var draw_categories_bars = () => void 0;
  71 + // Get list of elements to display
  72 + let legend_keys = color_scale.domain();
39 73  
40   - var mouseoverlegend = category_mouseoverlegend;
41   - var mouseleavelegend = category_mouseleavelegend;
  74 + if (legend_keys.length === 0) {
  75 + return 0;
  76 + }
42 77  
  78 + // Truncate legend keys when too long
  79 + let short_legend_keys = legend_keys.map(function (l) {
  80 + const n = legend_max_length;
  81 + return (l.length > n) ? l.substr(0, n - 1) : l;
  82 + });
  83 +
  84 +
  85 + let legend_wrap = svg.append('g')
  86 + .attr('width', parent_width)
  87 + .attr('class', 'legendwrap')
  88 + .attr("transform", "translate(" + offset.x + "," + offset.y + ")")
  89 + ;
  90 +
  91 + let legend = legend_wrap.selectAll('.legend')
  92 + .data(legend_keys)
  93 + .enter()
  94 + .append('g')
  95 + .attr('class', (d,i) => short_legend_keys[i])
  96 + .on("mouseover", mouseover_callback)
  97 + .on("mouseleave", mouseleave_callback);
  98 +
  99 + legend.append('rect')
  100 + .attr('width', legend_rect_size)
  101 + .attr('height', legend_rect_size)
  102 + .attr('fill', function (d) {
  103 + return color_scale(d);
  104 + })
  105 + .attr('class', 'legend');
43 106  
44   - if (category_type == 'capacity') {
45   - chart_title = "Charge par fonction"
46   - category_title = "Fonction"
47   - //draw_total_line
48   - draw_categories_bars = draw_categories_grouped
49   - colorScale = d3.scaleOrdinal(d3.schemeSet3);
50   - } else if (category_type == 'service') {
51   - chart_title = "Charge par service"
52   - category_title = "Service"
53   - draw_total_line = draw_totalcharge_line
54   - draw_categories_bars = draw_categories_stacked
55   - colorScale = d3.scaleOrdinal(d3.schemeTableau10);
56   - } else if (category_type == 'project') {
57   - chart_title = "Charge par projet"
58   - category_title = "Projet"
59   - draw_categories_bars = draw_categories_stacked
60   - colorScale = d3.scaleOrdinal(d3.schemeTableau10);
61   - } else if (category_type == 'area') {
62   - margin.bottom = 400
63   - height = 1200 - margin.top - margin.bottom;
64   - chart_title = "Tous agents confondus"
65   - category_title = "Projet"
66   - draw_areas = draw_projects_areas
67   - colorScale = d3.scaleOrdinal(d3.schemeTableau10);
68   - mouseoverlegend = () => void 0;
69   - mouseleavelegend = () => void 0;
70   - } else {
71   - alert("ALERT ! Every body shall quit the boat, we are sinking ! ALERT !")
72   - }
  107 + legend.append('text')
  108 + .attr('class', 'legend')
  109 + .attr('x', text_margin_left)
  110 + .attr('y', 13)
  111 + .text( (d,i) => short_legend_keys[i]);
73 112  
  113 + // Make legend column length for later display
  114 + //
  115 + // - first make an array of the length of each legend element
  116 + const row_height = legend.selectAll('text.legend')._groups[0][0].getBBox().height + legend_spacing;
  117 + const text_length_list = legend.selectAll('text.legend')._groups.map(el => el[0].getComputedTextLength());
  118 + // - then extract the maximum of those lengths plus a margin including the colored rect length
  119 + const column_length = Math.max(...text_length_list) + text_margin_left + 20;
  120 +
  121 + const nb_cols = Math.floor(parent_width / column_length);
  122 + const nb_rows = Math.floor(legend_keys.length / nb_cols) + 1;
  123 + let ypos = 0,
  124 + xpos = 0;
  125 +
  126 +
  127 + // Now move each legend element to its position
  128 + legend.attr("transform", function (d, i) {
  129 + if (i > 0 && i % nb_rows == 0) {
  130 + xpos += column_length;
  131 + ypos = 0;
  132 + }
  133 + let translation = 'translate(' + xpos + ',' + ypos + ')';
  134 + ypos += row_height;
  135 + return translation;
  136 + });
74 137  
75   - const xScale = d3.scaleBand()
76   - .range([0, width])
77   - .padding(0.4);
  138 + // TODO: should be able to return the height
78 139  
79   - const yScale = d3.scaleLinear()
80   - .range([height, 0]);
  140 + return row_height * nb_rows;
  141 +}
81 142  
  143 +function make_hamburger(data_url) {
82 144  
83 145 // Configure the tooltip to export svg to png or csv
84   - var update_export_menu = function (e, d) {
  146 + let update_export_menu = function (e, d) {
85 147  
86 148 const tooltip_test = document.getElementsByClassName('tooltip_hamburger');
87 149  
88   - if (tooltip_test.length == 0) {
  150 + if (tooltip_test.length === 0) {
89 151 d3.select(".tooltip_hamburger").remove();
90 152  
91 153 const tooltip_hamburger = hamburger.append("div")
... ... @@ -93,75 +155,142 @@ function build_chart(div_selector, data_url, entity_name, category_type) {
93 155 .attr("class", "tooltip tooltip_hamburger");
94 156  
95 157 d3.select(this).transition()
96   - .duration(1)
  158 + .duration(1);
97 159 tooltip_hamburger
98 160 .transition()
99 161 .duration(200)
100   - .style("opacity", 1)
  162 + .style("opacity", 1);
101 163 tooltip_hamburger
102 164 .html("<span>Export </span>" +
103 165 "<li id='to_csv'>To CSV</li>" +
104 166 "<li id='to_xls'>To XLS</li>" +
105   - "<li id='to_png'>To PNG</li>")
  167 + "<li id='to_png'>To PNG</li>");
106 168 d3.select("#to_png").on("click", download_png);
107 169 d3.select("#to_csv").on("click", function () {
108   - download_any(this, data_url, format='csv');
  170 + download_any(this, data_url, format = 'csv');
109 171 });
110 172 d3.select("#to_xls").on("click", function () {
111   - download_any(this, data_url, format='xls');
  173 + download_any(this, data_url, format = 'xls');
112 174 });
113 175 } else {
114 176 d3.select(".tooltip_hamburger").remove();
115 177 }
116 178  
117   - }
  179 + };
118 180 // Create a download button inside the div contaning svg chart
119   - const hamburger = d3.select(div_selector).append('i')
  181 + const hamburger = d3.create('i')
120 182 .attr('class', 'fas fa-bars fa-lg export')
121 183 .on("click", update_export_menu);
122 184  
123   - const real_width = width + margin.left + margin.right;
124   - const real_height = height + margin.top + margin.bottom;
  185 + return hamburger;
  186 +}
125 187  
126   - const svg = d3.select(div_selector).append("svg")
127   - .attr("viewBox", [0, 0, real_width, real_height])
128   - .attr("class", "svg_chart")
129   - .attr("width", real_width)
130   - .attr("height", real_height)
131   - .append("g")
132   - .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
  188 +function build_chart(div_selector, data_url, entity_name, category_type) {
  189 + // TODO: split build_bar and build_area
  190 +
  191 +
  192 + // TODO: common code, find a way to factorize
  193 + let main_elt = document.querySelector(div_selector);
  194 + let width = main_elt.offsetWidth,
  195 + height = 400;
  196 +
  197 + const height_ratio = 1.2; // how murch room to give above chart for lisibility
  198 +
  199 + const tooltip_offset = {dx: 0, dy: 100};
  200 + const tooltip_offset_dot = {dx: 20, dy: 60};
  201 +
  202 + const y_ticks_num = 5; // dont show to many y ticks for lisibility
  203 +
  204 + const dot_radius = 6; // size of total line dot
  205 +
  206 +
  207 + let colorScale = d3.scaleOrdinal([]); // Will be really set later by category type
  208 +
  209 + //
  210 + // Configure chart by the category given as arg
  211 + //
  212 + let chart_title = "";
  213 + let category_title = "";
  214 + let draw_areas = () => void 0;
  215 + let draw_total_line = () => void 0;
  216 + let draw_categories_bars = () => void 0;
  217 +
  218 + let mouseoverlegend = category_mouseoverlegend;
  219 + let mouseleavelegend = category_mouseleavelegend;
  220 +
  221 + let margin = {top: 100, right: 100, bottom: 100, left: 100};
  222 +
  223 +
  224 + if (category_type === 'capacity') {
  225 + chart_title = "Charge par fonction";
  226 + category_title = "Fonction";
  227 + //draw_total_line
  228 + draw_categories_bars = draw_categories_grouped;
  229 + colorScale = d3.scaleOrdinal(d3.schemeSet3);
  230 + } else if (category_type === 'service') {
  231 + chart_title = "Charge par service";
  232 + category_title = "Service";
  233 + draw_total_line = draw_totalcharge_line;
  234 + draw_categories_bars = draw_categories_stacked;
  235 + colorScale = d3.scaleOrdinal(d3.schemeTableau10);
  236 + } else if (category_type === 'project') {
  237 + chart_title = "Charge par projet";
  238 + category_title = "Projet";
  239 + draw_categories_bars = draw_categories_stacked;
  240 + colorScale = d3.scaleOrdinal(d3.schemeTableau10);
  241 + } else if (category_type === 'area') {
  242 + // margin.bottom = 400;
  243 + // height = 1200 ;//- margin.top - margin.bottom;
  244 + chart_title = "Tous agents confondus";
  245 + category_title = "Projet";
  246 + draw_areas = draw_projects_areas;
  247 + colorScale = d3.scaleOrdinal(d3.schemeTableau10);
  248 + mouseoverlegend = () => void 0;
  249 + mouseleavelegend = () => void 0;
  250 + } else {
  251 + alert("ALERT ! Every body shall quit the boat, we are sinking ! ALERT !");
  252 + }
  253 +
  254 +
  255 + const xScale = d3.scaleBand()
  256 + .range([0, width])
  257 + .padding(0.4);
  258 +
  259 + const yScale = d3.scaleLinear()
  260 + .range([height, 0]);
  261 +
  262 + const svg = make_svg(div_selector, data_url, width, height, margin);
133 263  
134 264 const tooltip = d3.select('html')
135 265 .append("div")
136 266 .style("opacity", 0)
137   - .attr("class", "tooltip")
  267 + .attr("class", "tooltip");
138 268  
139   - var mousemove = function (e, d) {
  269 + let mousemove = function (e, d) {
140 270 tooltip
141 271 .style("left", (e.pageX - tooltip_offset.dx) + "px")
142   - .style("top", (e.pageY - tooltip_offset.dy) + "px")
143   - }
  272 + .style("top", (e.pageY - tooltip_offset.dy) + "px");
  273 + };
144 274  
145   - var mouseleave = function (d) {
  275 + let mouseleave = function (d) {
146 276 tooltip
147 277 .transition()
148 278 .duration(900)
149   - .style("opacity", 0)
150   - }
151   -
152   - var mouseover = function (e, d) {
153   - var category_name = d3.select(this.parentNode).datum().key
154   - var category_charge = d.data[category_name]
155   - show_tooltip(e, category_name, category_charge)
156   - console.log("HELLO")
157   - }
158   -
159   - var mouseovergrouped = function (e, d) {
160   - var category_name = d.key
161   - var category_charge = d.value
162   - show_tooltip(e, category_name, category_charge)
163   - }
164   - var show_tooltip = function (e, category_name, category_charge) {
  279 + .style("opacity", 0);
  280 + };
  281 +
  282 + let mouseover = function (e, d) {
  283 + let category_name = d3.select(this.parentNode).datum().key;
  284 + let category_charge = d.data[category_name];
  285 + show_tooltip(e, category_name, category_charge);
  286 + };
  287 +
  288 + let mouseovergrouped = function (e, d) {
  289 + let category_name = d.key;
  290 + let category_charge = d.value;
  291 + show_tooltip(e, category_name, category_charge);
  292 + };
  293 + let show_tooltip = function (e, category_name, category_charge) {
165 294 tooltip
166 295 .transition()
167 296 .duration(200)
... ... @@ -169,155 +298,90 @@ function build_chart(div_selector, data_url, entity_name, category_type) {
169 298 tooltip
170 299 .html("<b>" + category_title + ":</b> " + category_name + "<br>" + "<b>Charge:</b> " + category_charge + " ETP")
171 300 .style("left", (e.pageX - tooltip_offset.dx) + "px")
172   - .style("top", (e.pageY - tooltip_offset.dy) + "px")
173   - }
  301 + .style("top", (e.pageY - tooltip_offset.dy) + "px");
  302 + };
174 303  
175   - var mouseleavedot = function (e, d) {
  304 + let mouseleavedot = function (e, d) {
176 305 d3.select(this).transition()
177 306 .duration(1)
178 307 .attr("r", dot_radius);
179 308 mouseleave(d);
180   - }
  309 + };
181 310  
182   - var mouseoverdot = function (e, d) {
  311 + let mouseoverdot = function (e, d) {
183 312 d3.select(this).transition()
184 313 .duration(1)
185 314 .attr("r", dot_radius * 1.5);
186 315 tooltip
187 316 .transition()
188 317 .duration(200)
189   - .style("opacity", 1)
  318 + .style("opacity", 1);
190 319 tooltip
191 320 .html("<b>" + d.period + ": </b>" + d.total + " ETP")
192 321 .style("left", (e.pageX - tooltip_offset_dot.dx) + "px")
193   - .style("top", (e.pageY - tooltip_offset_dot.dy) + "px")
194   - }
  322 + .style("top", (e.pageY - tooltip_offset_dot.dy) + "px");
  323 + };
195 324  
196 325 function category_mouseoverlegend() {
197   - var legend_category = $(this).attr("class");
198   - var bar_category = document.getElementsByClassName(legend_category);
  326 + let legend_category = $(this).attr("class");
  327 + let bar_category = document.getElementsByClassName(legend_category);
199 328 if (bar_category[0].tagName === "g") {
200   - var rect_to_hover = bar_category[0].children;
  329 + let rect_to_hover = bar_category[0].children;
201 330 (bar_category[1].children[0]).classList.add("brillance");
202   - for (var i = 0; i < rect_to_hover.length; i++) {
  331 + for (let i = 0; i < rect_to_hover.length; i++) {
203 332 rect_to_hover[i].classList.add("brillance");
204 333 }
205 334 } else {
206   - for (var i = 0; i < bar_category.length; i++) {
  335 + for (let i = 0; i < bar_category.length; i++) {
207 336 bar_category[i].classList.add("brillance");
208 337 }
209 338 }
210 339 }
211 340  
212 341 function category_mouseleavelegend() {
213   - var legend_category = $(this).attr("class");
214   - var bar_category = document.getElementsByClassName(legend_category);
  342 + let legend_category = $(this).attr("class");
  343 + let bar_category = document.getElementsByClassName(legend_category);
215 344 if (bar_category[0].tagName === "g") {
216   - var rect_to_hover = bar_category[0].children;
  345 + let rect_to_hover = bar_category[0].children;
217 346 (bar_category[1].children[0]).classList.remove("brillance");
218   - for (var i = 0; i < rect_to_hover.length; i++) {
  347 + for (let i = 0; i < rect_to_hover.length; i++) {
219 348 rect_to_hover[i].classList.remove("brillance");
220 349 }
221 350 } else {
222   - var lenght_i = bar_category.length - 1;
223   - for (var i = lenght_i; i >= 0; i--) {
  351 + let lenght_i = bar_category.length - 1;
  352 + for (let i = lenght_i; i >= 0; i--) {
224 353 bar_category[i].classList.remove("brillance");
225 354 }
226 355 }
227 356 }
228 357  
229   - var addlegend = function (color_scale) {
230   -
231   - // add horizontal legend
232   - let legend_keys = color_scale.domain();
233   -
234   - // Truncate legend keys when too long
235   - legend_keys = legend_keys.map(function (l) {
236   - const n = legend_max_length;
237   - return (l.length > n) ? l.substr(0, n - 1) : l;
238   - });
239   -
240   -
241   - var legendWrap = svg.append('g')
242   - .attr('width', '100%')
243   - .attr('class', 'legendwrap');
244   -
245   - var legend = svg.select('.legendwrap').selectAll('.legend')
246   - .data(legend_keys)
247   - .enter()
248   - .append('g')
249   - .attr('class', d => d)
250   - .on("mouseover", mouseoverlegend)
251   - .on("mouseleave", mouseleavelegend);
252   -
253   - legend.append('rect')
254   - .attr('width', legend_rect_size)
255   - .attr('height', legend_rect_size)
256   - .style('fill', color_scale)
257   - .attr('class', 'legend');
258   -
259   - legend.append('text')
260   - .attr('class', 'legend')
261   - .attr('x', legend_rect_size + legend_spacing * 1.5)
262   - .attr('y', 13)
263   - .text(d => d);
264   -
265   - var ypos = 0,
266   - newxpos = 0,
267   - maxwidth = 0,
268   - xpos;
269   -
270   - // Get Max legend key length for later display
271   - const text_length_list = legend.selectAll('text.legend')._groups.map(el => el[0].getComputedTextLength())
272   - const keycol_length = Math.max(...text_length_list) + 40
273   -
274   - legend
275   - .attr("transform", function (d, i) {
276   - xpos = newxpos;
277   - if (width < xpos + keycol_length) {
278   - newxpos = xpos = 0;
279   - ypos += legend_height;
280   -
281   - }
282   - newxpos += keycol_length;
283   - if (newxpos > maxwidth) maxwidth = newxpos;
284   -
285   - return 'translate(' + xpos + ',' + ypos + ')';
286   -
287   - });
288   -
289   -
290   - legendWrap
291   - .attr("transform", function (d, i) {
292   - return "translate(0 ," + (height + 100) + ")";
293   - });
294   - }
295 358  
296 359 function draw_categories_grouped(data, categories) {
297 360  
298 361 // TODO: use ymax_from_stacked
299 362 // Get the max y to plot
300 363 // If no data at all, force to 0
301   - if (categories.length == 0) {
302   - y_max = 0
  364 + let y_max;
  365 + if (categories.length === 0) {
  366 + y_max = 0;
303 367 } else {
304   - var y_max = d3.max(data, function (d) {
  368 + y_max = d3.max(data, function (d) {
305 369 return d3.max(categories, function (c) {
306   - return +d[c]
307   - })
  370 + return +d[c];
  371 + });
308 372 });
309 373 }
310 374 // Force maximum in any cases
311   - if (y_max == 0) {
312   - y_max = 100
  375 + if (y_max === 0) {
  376 + y_max = 100;
313 377 }
314   - y_max = y_max * height_ratio
315   - yScale.domain([0, y_max])
  378 + y_max = y_max * height_ratio;
  379 + yScale.domain([0, y_max]);
316 380 // Another scale for subgroup position
317   - var xCategories = d3.scaleBand()
  381 + let xCategories = d3.scaleBand()
318 382 .domain(categories)
319 383 .range([0, xScale.bandwidth()])
320   - .padding([0.2])
  384 + .padding([0.2]);
321 385  
322 386 svg.append("g")
323 387 .selectAll("g")
... ... @@ -336,9 +400,7 @@ function build_chart(div_selector, data_url, entity_name, category_type) {
336 400 .attr("x", d => xCategories(d.key))
337 401 .attr("width", xCategories.bandwidth())
338 402 /* Transition part */
339   - .attr("y", d => {
340   - return height;
341   - })
  403 + .attr("y", height)
342 404 .attr("height", 0)
343 405 .transition()
344 406 .duration(750)
... ... @@ -362,30 +424,31 @@ function build_chart(div_selector, data_url, entity_name, category_type) {
362 424  
363 425 // Get the max y to plot
364 426 // If no data at all, force to 0
365   - if (stacked_data.length == 0) {
366   - y_max = 0
  427 + let y_max;
  428 + if (stacked_data.length === 0) {
  429 + y_max = 0;
367 430 } else {
368   - var y_max = d3.max(stacked_data[stacked_data.length - 1], d => d[1]);
  431 + y_max = d3.max(stacked_data[stacked_data.length - 1], d => d[1]);
369 432 }
370 433 // Force maximum in any cases
371   - if (y_max == 0) {
372   - y_max = 100
  434 + if (y_max === 0) {
  435 + y_max = 100;
373 436 }
374 437 // Enhance it by %ratio to leave room on the top of the graph
375   - y_max = y_max * height_ratio
  438 + y_max = y_max * height_ratio;
376 439  
377   - return y_max
  440 + return y_max;
378 441 }
379 442  
380 443 function draw_projects_areas(data, projects) {
381 444  
382 445 // Now build the stacked data for stacked bars
383 446 //
384   - var stack = d3.stack()
385   - .keys(projects)
  447 + let stack = d3.stack()
  448 + .keys(projects);
386 449 // .order(d3.stackOrderNone)
387 450 // .offset(d3.stackOffsetNone);
388   - var stacked_data = stack(data)
  451 + let stacked_data = stack(data);
389 452  
390 453 const y_max = ymax_from_stacked(stacked_data);
391 454 yScale.domain([0, y_max]);
... ... @@ -393,9 +456,9 @@ function build_chart(div_selector, data_url, entity_name, category_type) {
393 456 const area = d3.area()
394 457 .x(d => xScale(d.data.period))
395 458 .y0(d => yScale(d[0]))
396   - .y1(d => yScale(d[1]))
  459 + .y1(d => yScale(d[1]));
397 460  
398   - let paths = svg.append("g")
  461 + svg.append("g")
399 462 .selectAll("path")
400 463 .data(stacked_data)
401 464 .join("path")
... ... @@ -410,14 +473,13 @@ function build_chart(div_selector, data_url, entity_name, category_type) {
410 473  
411 474  
412 475 function draw_categories_stacked(data, categories) {
413   -
414 476 // Now build the stacked data for stacked bars
415 477 //
416   - var stack = d3.stack()
417   - .keys(categories)
  478 + let stack = d3.stack()
  479 + .keys(categories);
418 480 // .order(d3.stackOrderNone)
419 481 // .offset(d3.stackOffsetNone);
420   - var stacked_data = stack(data)
  482 + let stacked_data = stack(data);
421 483  
422 484 const y_max = ymax_from_stacked(stacked_data);
423 485 yScale.domain([0, y_max]);
... ... @@ -431,7 +493,7 @@ function build_chart(div_selector, data_url, entity_name, category_type) {
431 493 .style("fill", (d) => colorScale(d.key));
432 494  
433 495 // then draw each period/category bar
434   - let rect = groups.selectAll("rect")
  496 + groups.selectAll("rect")
435 497 .data(d => d)
436 498 .enter()
437 499 .append("rect")
... ... @@ -439,9 +501,7 @@ function build_chart(div_selector, data_url, entity_name, category_type) {
439 501 .attr("x", d => xScale(d.data.period))
440 502 .attr("width", xScale.bandwidth())
441 503 /* transition part */
442   - .attr("y", d => {
443   - return height;
444   - })
  504 + .attr("y", height)
445 505 .attr("height", 0)
446 506 .transition()
447 507 .duration(750)
... ... @@ -449,7 +509,7 @@ function build_chart(div_selector, data_url, entity_name, category_type) {
449 509 return i * 150;
450 510 })
451 511 .attr("y", d => yScale(d[1]))
452   - .attr("height", d => height - yScale(d[1] - d[0]))
  512 + .attr("height", d => height - yScale(d[1] - d[0]));
453 513  
454 514 groups.selectAll("rect")
455 515 .on("mouseover", mouseover)
... ... @@ -462,15 +522,15 @@ function build_chart(div_selector, data_url, entity_name, category_type) {
462 522 // Build the total charge by period;
463 523 // it will be used for the line drawing above bars.
464 524 //
465   - var periods_total_charge = []
  525 + let periods_total_charge = [];
466 526  
467 527 data.forEach(function (d) {
468 528 // get the list of values for all columns except the first one which is the period name
469   - var period_values = Object.values(d).slice(1)
470   - var row = {}
471   - row['period'] = d.period
472   - row['total'] = roundToTwo(d3.sum(period_values))
473   - periods_total_charge.push(row)
  529 + let period_values = Object.values(d).slice(1);
  530 + let row = {};
  531 + row['period'] = d.period;
  532 + row['total'] = roundToTwo(d3.sum(period_values));
  533 + periods_total_charge.push(row);
474 534 });
475 535  
476 536 // the line itselet
... ... @@ -494,7 +554,7 @@ function build_chart(div_selector, data_url, entity_name, category_type) {
494 554 .attr("class", "total-circle")
495 555 .attr("r", dot_radius)
496 556 .attr("cx", function (d) {
497   - return (xScale.bandwidth() / 2) + xScale(d.period)
  557 + return (xScale.bandwidth() / 2) + xScale(d.period);
498 558 })
499 559 .attr("cy", function (d) {
500 560 return yScale(d.total);
... ... @@ -507,11 +567,11 @@ function build_chart(div_selector, data_url, entity_name, category_type) {
507 567  
508 568 d3.csv(data_url).then(data => {
509 569 // we could get categories that way,
510   - // var categories = data.columns.slice(1)
  570 + // let categories = data.columns.slice(1)
511 571 // but instead we want to filter by non zero, see later
512 572  
513   - var periods = d3.map(data, d => d.period)
514   - xScale.domain(periods)
  573 + let periods = d3.map(data, d => d.period);
  574 + xScale.domain(periods);
515 575  
516 576 // Filter datas to only keep non zero categories
517 577 // TODO: should be done on server (python/flask) side
... ... @@ -520,57 +580,57 @@ function build_chart(div_selector, data_url, entity_name, category_type) {
520 580 // That will leave '0' to categories with no charge at all
521 581 // TODO: to be done with Object.keys, Object.values and d3 filtering methods
522 582 //
523   - var categories_total_charge = {}
  583 + let categories_total_charge = {};
524 584 data.forEach(function (d) {
525 585 Object.keys(d).forEach(function (k) {
526 586 if (k === 'period') {
527 587 return;
528 588 }
529 589 if (categories_total_charge.hasOwnProperty(k)) {
530   - categories_total_charge[k] += +d[k]
  590 + categories_total_charge[k] += +d[k];
531 591 } else {
532   - categories_total_charge[k] = +d[k]
  592 + categories_total_charge[k] = +d[k];
533 593 }
534   - categories_total_charge[k] = roundToTwo(categories_total_charge[k])
  594 + categories_total_charge[k] = roundToTwo(categories_total_charge[k]);
535 595 }
536   - )
537   - })
  596 + );
  597 + });
538 598  
539 599 // 2- Now, filter only categories that have a non-zero charge
540 600 // TODO: to be done with Object.keys, Object.values and d3 filtering methods
541 601 //
542   - var categories = []
543   - for (var key in categories_total_charge) {
  602 + let categories = [];
  603 + for (let key in categories_total_charge) {
544 604 if (categories_total_charge[key] > 0) {
545   - categories.push(key)
  605 + categories.push(key);
546 606 }
547 607 }
548 608  
549 609 //
550   - // Draw the areas, stacked.
  610 + // This former list we use as color_scale domain.
  611 + // And that allows us to build the legend
551 612 //
552   - draw_areas(data, categories)
  613 + colorScale.domain(categories);
  614 + make_legend(svg, {'x': 0, 'y': height + 90}, width, colorScale, mouseleavelegend, mouseoverlegend);
553 615  
554 616 //
555   - // Draw the bars, stacked or grouped
  617 + // Draw the areas, stacked.
556 618 //
557   - draw_categories_bars(data, categories)
  619 + draw_areas(data, categories);
558 620  
559 621 //
560   - // Draw the total charge line ( may be)
  622 + // Draw the bars, stacked or grouped
561 623 //
562   - draw_total_line(data, categories)
  624 + draw_categories_bars(data, categories);
563 625  
564 626 //
565   - // This former list we use as color_scale domain.
566   - // And that allows us to build the legend
  627 + // Draw the total charge line ( may be)
567 628 //
568   - colorScale.domain(categories)
569   - addlegend(colorScale)
  629 + draw_total_line(data, categories);
570 630  
571 631 // Xaxis
572 632 //
573   - const xAxis = d3.axisBottom(xScale)
  633 + const xAxis = d3.axisBottom(xScale);
574 634  
575 635 // Draw Xaxis
576 636 svg.append("g")
... ... @@ -586,19 +646,19 @@ function build_chart(div_selector, data_url, entity_name, category_type) {
586 646 // Yaxis
587 647 //
588 648 const yAxis = d3.axisLeft(yScale)
589   - .ticks(y_ticks_num)
  649 + .ticks(y_ticks_num);
590 650  
591 651 // Draw Yaxis
592 652 svg.append("g")
593 653 .attr("class", "y_axis")
594   - .call(yAxis)
  654 + .call(yAxis);
595 655  
596 656 // Draw horizontal lines
597 657 svg.selectAll("y axis")
598 658 .data(yScale.ticks(y_ticks_num))
599 659 .enter()
600 660 .append("line")
601   - .attr("class", d => (d == 0 ? "horizontalY0" : "horizontalY"))
  661 + .attr("class", d => (d === 0 ? "horizontalY0" : "horizontalY"))
602 662 .attr("x1", 0)
603 663 .attr("x2", width)
604 664 .attr("y1", d => yScale(d))
... ... @@ -607,32 +667,97 @@ function build_chart(div_selector, data_url, entity_name, category_type) {
607 667  
608 668 // Write Y axis title
609 669 svg.append("text")
610   - .attr("text-anchor", "end")
  670 + .attr("text-anchor", "middle")
611 671 .attr("transform", "rotate(-90)")
612   - .attr("y", -margin.left + 40)
613   - .attr("x", -margin.top - 70)
  672 + .attr("y", -margin.left / 2)
  673 + .attr("x", -height / 2)
614 674 .text("Charge en ETP");
615 675  
616 676 //
617 677 // Write chart Title
618 678 //
  679 + make_title(svg, width, [entity_name, chart_title], {'x': 0, 'y': -50});
619 680  
620   - // part 1
621   - svg.append("text")
622   - .attr("x", (width / 2))
623   - .attr("y", 0 - (margin.top / 2) - 10)
624   - .attr("text-anchor", "middle")
625   - .style("font-size", "16px")
626   - .text(entity_name);
627   - // part 2
628   - svg.append("text")
629   - .attr("x", (width / 2))
630   - .attr("y", 0 - (margin.top / 2) + 10)
631   - .attr("text-anchor", "middle")
632   - .style("font-size", "12px")
633   - .text(chart_title);
  681 + }); // end of d3.csv().then({})
634 682  
  683 +}
635 684  
636   - }); // end of d3.csv().then({})
  685 +function build_pie_chart(div_selector, _height, data_url, entity_name) {
  686 + // TODO: common code, find a way to factorize
  687 + let main_elt = document.querySelector(div_selector);
  688 + let width = main_elt.offsetWidth,
  689 + height = _height;
  690 +
  691 + let svg = make_svg(div_selector, data_url, width, height);
  692 +
  693 + let colorScale = d3.scaleOrdinal(d3.schemeTableau10);
  694 +
  695 + let pie = d3.pie().value(
  696 + function (d) {
  697 + // TODO: rename to d[1]
  698 + return d['Effectif'];
  699 + })
  700 + .sort(null);
  701 +
  702 + d3.csv(data_url).then(data => {
  703 + let pie_margin = {'top': 30, 'right': 0, 'bottom': 0, 'left': 0};
  704 +
  705 + // Append Title on top of chart
  706 + //
  707 + let title_height = make_title(svg, width, ["Répartion des agents par statut", "Période: " + entity_name]);
  708 +
  709 + // Get pie positions
  710 + //
  711 + let pie_height = height - title_height - (pie_margin.top + pie_margin.bottom);
  712 + let pie_radius = pie_height / 2;
  713 + let pie_offset = {'x': pie_radius, 'y': title_height + pie_margin.top + pie_radius};
  714 +
  715 + let inner_radius = Math.floor(pie_radius * 0.6);
  716 +
  717 + // TODO: rename to a[0]
  718 + let statuses = data.map(a => a['Statut']);
  719 + colorScale.domain(statuses);
  720 +
  721 + let arc = d3.arc()
  722 + .innerRadius(inner_radius)
  723 + .outerRadius(pie_radius);
  724 +
  725 + let arcs = svg
  726 + .append('g')
  727 + .attr('transform', 'translate(' + pie_offset.x + ',' + pie_offset.y + ')');
  728 +
  729 + arcs.selectAll('arc')
  730 + .data(pie(data))
  731 + .enter()
  732 + .append('g')
  733 + .attr('class', 'arc')
  734 + .append('path')
  735 + .attr('class', 'pie')
  736 + .attr('fill', d => colorScale(d.data.Statut))
  737 + .attr('d', arc);
  738 +
  739 + // Draw borders to pie
  740 + //
  741 + arcs.append("circle")
  742 + .attr('class', 'pie_border')
  743 + .attr('r', pie_radius);
  744 + arcs.append("circle")
  745 + .attr('class', 'pie_border')
  746 + .attr('r', inner_radius);
  747 +
  748 + // Add Legend
  749 + let legend_offset = {'x': 2 * pie_radius + 50, 'y': title_height + pie_margin.top};
  750 + let legend_height = make_legend(svg, legend_offset, 2 * pie_radius, colorScale,
  751 + () => void 0, () => void 0);
  752 + });
637 753  
638 754 }
  755 +
  756 +function build_line_chart(div_selector, _height, data_url, entity_name) {
  757 + // TODO: common code, find a way to factorize
  758 + let main_elt = document.querySelector(div_selector);
  759 + let width = main_elt.offsetWidth,
  760 + height = _height;
  761 + let svg = make_svg(div_selector, data_url, width, height);
  762 + let title_height = make_title(svg, width, ["Default Line Chart", "default subtitle"]);
  763 +}
639 764 \ No newline at end of file
... ...
app/main/static/js/svg_to_any.js
1   -let download_any = function (node, base_rest_url, format='csv') {
  1 +let download_any = function (node, base_rest_url, format = 'csv') {
2 2 let chart_div = node.parentNode.parentNode.parentNode;
3 3 let chart_title = chart_div.id;
4 4 let content_type;
5 5 let extension;
6 6 let rest_url;
7   - if (format === 'csv'){
  7 + if (format === 'csv') {
8 8 content_type = 'text/csv';
9 9 extension = '.csv';
10   - rest_url = base_rest_url+'/csv'
  10 + rest_url = base_rest_url + '/csv';
11 11 } else if (format == 'xls') {
12 12 content_type = 'application/vnd.ms-excel';
13 13 extension = '.xslx';
14   - rest_url = base_rest_url+'/xls'
  14 + rest_url = base_rest_url + '/xls';
15 15 }
16 16 fetch(rest_url)
17 17 .then(function (r) {
18   - r.headers.set('content-type', content_type )
19   - return r.blob()
  18 + r.headers.set('content-type', content_type);
  19 + return r.blob();
20 20 })
21 21 .then((blob) => {
22 22 saveAs(blob, chart_title + extension); // FileSaver.js function
23   - })
24   -}
  23 + });
  24 +};
25 25  
26 26 var download_png = function () {
27 27 // This callback is supposed to be called on the click event of a button child of the div containing the svg.
28 28 // We then get the parent of this btn to guess the chart's title, width and heigth
29   - var chart_div = this.parentNode.parentNode.parentNode;
30   - var chart_title = chart_div.id;
31   - width = chart_div.offsetWidth;
32   - height = chart_div.offsetHeight;
  29 + let chart_div = this.parentNode.parentNode.parentNode;
  30 + let chart_title = chart_div.id;
  31 + let width = chart_div.offsetWidth;
  32 + let height = chart_div.offsetHeight;
33 33  
34 34 // Then we can access the svg contained in that parent
35   - svg = chart_div.getElementsByTagName('svg')[0]
  35 + svg = chart_div.getElementsByTagName('svg')[0];
36 36  
37 37 // Export to string and save
38 38 var svgString = getSVGString(svg);
39 39 svgString2Image(svgString, 2 * width, 2 * height, 'png', save); // passes Blob and filesize String to the callback
40 40  
41 41 function save(dataBlob, filesize) {
42   - console.log(dataBlob)
  42 + console.log(dataBlob);
43 43 saveAs(dataBlob, chart_title); // FileSaver.js function
44 44 }
45   -}
  45 +};
46 46  
47 47  
48 48 // Below are the functions that handle actual exporting:
... ... @@ -98,11 +98,11 @@ function getSVGString(svgNode) {
98 98  
99 99 var cssRules = s.cssRules;
100 100 for (var r = 0; r < cssRules.length; r++) {
101   - var cssRule = cssRules[r]
  101 + var cssRule = cssRules[r];
102 102 if (typeof cssRule.selectorText === 'undefined') {
103 103 continue;
104 104 }
105   - var classFromSelector = '.' + cssRule.selectorText.split('.')[1]
  105 + var classFromSelector = '.' + cssRule.selectorText.split('.')[1];
106 106 if (contains(classFromSelector, selectorTextArr))
107 107 extractedCSSText += cssRule.cssText;
108 108 }
... ...
app/main/templates/agent.html
... ... @@ -8,7 +8,7 @@
8 8 {% block content %}
9 9  
10 10 <!-- Invisible span to definte wich ul and a in the navbar are actived -->
11   - <span id="nav_actived" style="display: none">agent,liste_agents</span>
  11 + <span id="nav_actived" style="display: none">agent,agents_list</span>
12 12  
13 13 <div class="card">
14 14 <div class="card-header">
... ... @@ -71,6 +71,7 @@
71 71 </div>
72 72  
73 73 <div class="charge_chart" id="projects_chart"></div>
  74 +
74 75 <table class="table_datatables table table-hover">
75 76 <thead>
76 77 <tr>
... ...
app/main/templates/agent_form.html
... ... @@ -10,7 +10,7 @@
10 10 {% block content %}
11 11  
12 12 <!-- Invisible span to define wich ul and a in the navbar are actived -->
13   - <span id="nav_actived" style="display: none">cds,edit_agent</span>
  13 + <span id="nav_actived" style="display: none">cds,agent_edit</span>
14 14  
15 15 <form id="agent_form" class="pdc-form" action="{{ url_for('main.agent_edit') }}" method="post">
16 16 {% if agent and agent['id'] not in ['', None] %}
... ...
app/main/templates/agents.html
... ... @@ -3,7 +3,7 @@
3 3 {% block content %}
4 4  
5 5 <!-- Invisible span to definte wich ul and a in the navbar are actived -->
6   - <span id="nav_actived" style="display: none">agent,liste_agents</span>
  6 + <span id="nav_actived" style="display: none">agent,agents_list</span>
7 7  
8 8 <table class="table_datatables table table-hover">
9 9 <thead>
... ...
app/main/templates/agents_stats.html 0 → 100644
... ... @@ -0,0 +1,99 @@
  1 +{% extends "base_page.html" %}
  2 +{% block more_heads %}
  3 + <link href="{{ url_for('main.static', filename='css/charges.css', version=config.VERSION) }}" rel="stylesheet"
  4 + type="text/css"/>
  5 +{% endblock %}
  6 +
  7 +{% block content %}
  8 +
  9 + <!-- Invisible span to definte wich ul and a in the navbar are actived -->
  10 + <span id="nav_actived" style="display: none">agent,agents_stats</span>
  11 +
  12 + <div class="pdc-controls">
  13 + <select title="Choisir la période" id="period_id_select" name="period_id_select">
  14 + {% for p in periods %}
  15 + <option value="{{ p.id }}" {{ "selected" if p.id == 1 }}>{{ p.name }}</option>
  16 + {% endfor %}
  17 + </select>
  18 + <button id='prev_period' title="Période précédente"><span data-feather="chevron-left"></span></button>
  19 + <button id='next_period' title="Période suivante"><span data-feather="chevron-right"></span></button>
  20 + </div>
  21 +
  22 + <h3 class="sub-header">Tout le laboratoire</h3>
  23 +
  24 + <div class="row">
  25 + <div class="col-4">
  26 + <div class="charge_chart" id="agents_by_status_chart"></div>
  27 + </div>
  28 + <div class="col-8">
  29 + <div class="charge_chart" id="charge_by_status_chart"></div>
  30 + </div>
  31 + </div>
  32 +
  33 + {# {% for c in categories %}#}
  34 + {# <h3 class="sub-header">Charge pour la catégorie {{ c.name }}</h3>#}
  35 + {# <div class="charge_chart" id="labels_stats_chart_{{ c.id }}"></div>#}
  36 + {# {% endfor %}#}
  37 +
  38 +{% endblock %}
  39 +
  40 +{% block more_scripts %}
  41 + {% include 'd3js-includes.html' %}
  42 + {% include 'charges-includes.html' %}
  43 +
  44 + <script>
  45 + $('#period_id_select').on('change', function () {
  46 + let period_id = $(this).find(':selected').val();
  47 + let period_name = $(this).find(':selected').text();
  48 + build_all(period_id, period_name);
  49 + });
  50 + document.getElementById('next_period').onclick = function () {
  51 + period_select_update(1);
  52 + };
  53 + document.getElementById('prev_period').onclick = function () {
  54 + period_select_update(-1);
  55 + };
  56 +
  57 + let period_select_update = function (step) {
  58 + let select_elemnt = $('#period_id_select');
  59 + let selectoptions_length = $('#period_id_select option').length;
  60 + let next_step = +select_elemnt.val() + step;
  61 + if (next_step <= 0 || next_step > selectoptions_length) {
  62 + return;
  63 + }
  64 + select_elemnt.val(next_step);
  65 + select_elemnt.trigger('change');
  66 + };
  67 +
  68 + let build_all = function (period_id, period_name) {
  69 + let agent_status_url = "{{url_for('main.rest_agents_status_count', period_id='PERIOD_ID')}}"
  70 + .replace("PERIOD_ID", period_id);
  71 + let height = 300;
  72 +
  73 + build_pie_chart("#agents_by_status_chart",
  74 + height,
  75 + agent_status_url,
  76 + period_name);
  77 +
  78 + build_line_chart("#charge_by_status_chart",
  79 + height,
  80 + agent_status_url,
  81 + "Charge des agents par statut");
  82 +
  83 + };
  84 +
  85 + {#TODO: get current period id from session#}
  86 +
  87 + let curr_p_id = $('#period_id_select').val();
  88 + let curr_p_name = $('#period_id_select').find(":selected").text();
  89 +
  90 + build_all(curr_p_id, curr_p_name);
  91 +
  92 + {# {% for c in categories %}#}
  93 + {# build_chart("#labels_stats_chart_{{ c.id }}",#}
  94 + {# "{{url_for('main.rest_labels_stats', category_id=c.id)}}",#}
  95 + {# "Charge pour la catégorie {{c.name}}",#}
  96 + {# "area");#}
  97 + {# {% endfor %}#}
  98 + </script>
  99 +{% endblock %}
... ...
app/main/templates/categories.html
... ... @@ -2,7 +2,7 @@
2 2 {% block content %}
3 3  
4 4 <!-- Invisible span to definte wich ul and a in the navbar are actived -->
5   - <span id="nav_actived" style="display: none">project,categories</span>
  5 + <span id="nav_actived" style="display: none">project,categories_list</span>
6 6  
7 7 <table class="table_datatables table table-hover">
8 8 <thead>
... ...
app/main/templates/category.html
... ... @@ -2,7 +2,7 @@
2 2 {% block content %}
3 3  
4 4 <!-- Invisible span to definte wich ul and a in the navbar are actived -->
5   - <span id="nav_actived" style="display: none">project,categories</span>
  5 + <span id="nav_actived" style="display: none">project,categories_list</span>
6 6  
7 7 <div class="card">
8 8 <div class="card-header">
... ...
app/main/templates/charge_form.html
... ... @@ -2,7 +2,7 @@
2 2 {% block content %}
3 3  
4 4 <!-- Invisible span to definte wich ul and a in the navbar are actived -->
5   - <span id="nav_actived" style="display: none">cds,add_charge</span>
  5 + <span id="nav_actived" style="display: none">cds,charge_add</span>
6 6  
7 7 <form id="charge_form" class="pdc-form" action="{{ url_for('main.charge_add') }}" method="post">
8 8 <div class="form-group">
... ...
app/main/templates/label.html
... ... @@ -2,7 +2,7 @@
2 2 {% block content %}
3 3  
4 4 <!-- Invisible span to definte wich ul and a in the navbar are actived -->
5   - <span id="nav_actived" style="display: none">project,labels</span>
  5 + <span id="nav_actived" style="display: none">project,labels_list</span>
6 6  
7 7 <div class="card">
8 8 <div class="card-header">
... ...
app/main/templates/labels.html
... ... @@ -2,7 +2,7 @@
2 2 {% block content %}
3 3  
4 4 <!-- Invisible span to definte wich ul and a in the navbar are actived -->
5   - <span id="nav_actived" style="display: none">project,labels</span>
  5 + <span id="nav_actived" style="display: none">project,labels_list</span>
6 6  
7 7 <table class="table_datatables table table-hover">
8 8 <thead>
... ...
app/main/templates/periods.html
... ... @@ -2,7 +2,7 @@
2 2 {% block content %}
3 3  
4 4 <!-- Invisible span to definte wich ul and a in the navbar are actived -->
5   - <span id="nav_actived" style="display: none">admin,liste_periodes</span>
  5 + <span id="nav_actived" style="display: none">admin,periods_list</span>
6 6  
7 7 <table class="table_datatables table table-hover">
8 8 <thead>
... ...
app/main/templates/project.html
... ... @@ -7,7 +7,7 @@
7 7  
8 8 {% block content %}
9 9 <!-- Invisible span to definte wich ul and a in the navbar are actived -->
10   - <span id="nav_actived" style="display: none">projet,liste_projets</span>
  10 + <span id="nav_actived" style="display: none">projet,projects_list</span>
11 11  
12 12 <div class="card">
13 13 <div class="card-header">
... ... @@ -68,11 +68,11 @@
68 68  
69 69 <script>
70 70 build_chart("#project_services_chart",
71   - "{{url_for('main.rest_charge_project', project_id=project.id, category='service')}}",
  71 + "{{url_for('main.rest_charge_project', project_id=project.id, sort_key='service')}}",
72 72 "{{project.name}}",
73 73 "service");
74 74 build_chart("#project_capacities_chart",
75   - "{{url_for('main.rest_charge_project', project_id=project.id, category='capacity')}}",
  75 + "{{url_for('main.rest_charge_project', project_id=project.id, sort_key='capacity')}}",
76 76 "{{project.name}}",
77 77 "capacity");
78 78 </script>
... ...
app/main/templates/projects.html
... ... @@ -3,7 +3,7 @@
3 3 {% block content %}
4 4  
5 5 <!-- Invisible span to definte wich ul and a in the navbar are actived -->
6   - <span id="nav_actived" style="display: none">project,projects</span>
  6 + <span id="nav_actived" style="display: none">project,projects_list</span>
7 7  
8 8 <table class="table_datatables table table-hover">
9 9 <thead>
... ...
app/main/templates/services.html
... ... @@ -2,7 +2,7 @@
2 2 {% block content %}
3 3  
4 4 <!-- Invisible span to definte wich ul and a in the navbar are actived -->
5   - <span id="nav_actived" style="display: none">service,liste_services</span>
  5 + <span id="nav_actived" style="display: none">service,services_list</span>
6 6  
7 7 <table class="table_datatables table table-hover">
8 8 <thead>
... ...
app/models.py
... ... @@ -207,6 +207,17 @@ class Category(db.Model, Formable):
207 207  
208 208  
209 209 #
  210 +# History
  211 +#
  212 +
  213 +class AgentHistory(db.Model):
  214 + id = db.Column(db.Integer, primary_key=True)
  215 + period_id = db.Column(db.Integer, db.ForeignKey('period.id'))
  216 + agent_id = db.Column(db.Integer, db.ForeignKey('agent.id'))
  217 + status_id = db.Column(db.Integer, db.ForeignKey('agent_status.id'))
  218 +
  219 +
  220 +#
210 221 # Agents
211 222 #
212 223  
... ...
app/static/css/style.css
... ... @@ -156,4 +156,8 @@ table.dataTable thead .sorting_desc_disabled{
156 156  
157 157 .dt-buttons {
158 158 float: right !important;
  159 +}
  160 +
  161 +.pdc-controls {
  162 + margin-bottom: 3em;
159 163 }
160 164 \ No newline at end of file
... ...
app/static/lib/datatables-1.10.24/js/jszip.min.js 0 → 100644
No preview for this file type
app/static/lib/feather-4.28.0/feather.min.js 0 → 100644
... ... @@ -0,0 +1,13 @@
  1 +!function(e,n){"object"==typeof exports&&"object"==typeof module?module.exports=n():"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?exports.feather=n():e.feather=n()}("undefined"!=typeof self?self:this,function(){return function(e){var n={};function i(t){if(n[t])return n[t].exports;var l=n[t]={i:t,l:!1,exports:{}};return e[t].call(l.exports,l,l.exports,i),l.l=!0,l.exports}return i.m=e,i.c=n,i.d=function(e,n,t){i.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:t})},i.r=function(e){Object.defineProperty(e,"__esModule",{value:!0})},i.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(n,"a",n),n},i.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},i.p="",i(i.s=80)}([function(e,n,i){(function(n){var i="object",t=function(e){return e&&e.Math==Math&&e};e.exports=t(typeof globalThis==i&&globalThis)||t(typeof window==i&&window)||t(typeof self==i&&self)||t(typeof n==i&&n)||Function("return this")()}).call(this,i(75))},function(e,n){var i={}.hasOwnProperty;e.exports=function(e,n){return i.call(e,n)}},function(e,n,i){var t=i(0),l=i(11),r=i(33),o=i(62),a=t.Symbol,c=l("wks");e.exports=function(e){return c[e]||(c[e]=o&&a[e]||(o?a:r)("Symbol."+e))}},function(e,n,i){var t=i(6);e.exports=function(e){if(!t(e))throw TypeError(String(e)+" is not an object");return e}},function(e,n){e.exports=function(e){try{return!!e()}catch(e){return!0}}},function(e,n,i){var t=i(8),l=i(7),r=i(10);e.exports=t?function(e,n,i){return l.f(e,n,r(1,i))}:function(e,n,i){return e[n]=i,e}},function(e,n){e.exports=function(e){return"object"==typeof e?null!==e:"function"==typeof e}},function(e,n,i){var t=i(8),l=i(35),r=i(3),o=i(18),a=Object.defineProperty;n.f=t?a:function(e,n,i){if(r(e),n=o(n,!0),r(i),l)try{return a(e,n,i)}catch(e){}if("get"in i||"set"in i)throw TypeError("Accessors not supported");return"value"in i&&(e[n]=i.value),e}},function(e,n,i){var t=i(4);e.exports=!t(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(e,n){e.exports={}},function(e,n){e.exports=function(e,n){return{enumerable:!(1&e),configurable:!(2&e),writable:!(4&e),value:n}}},function(e,n,i){var t=i(0),l=i(19),r=i(17),o=t["__core-js_shared__"]||l("__core-js_shared__",{});(e.exports=function(e,n){return o[e]||(o[e]=void 0!==n?n:{})})("versions",[]).push({version:"3.1.3",mode:r?"pure":"global",copyright:"© 2019 Denis Pushkarev (zloirock.ru)"})},function(e,n,i){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var t=o(i(43)),l=o(i(41)),r=o(i(40));function o(e){return e&&e.__esModule?e:{default:e}}n.default=Object.keys(l.default).map(function(e){return new t.default(e,l.default[e],r.default[e])}).reduce(function(e,n){return e[n.name]=n,e},{})},function(e,n){e.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},function(e,n,i){var t=i(72),l=i(20);e.exports=function(e){return t(l(e))}},function(e,n){e.exports={}},function(e,n,i){var t=i(11),l=i(33),r=t("keys");e.exports=function(e){return r[e]||(r[e]=l(e))}},function(e,n){e.exports=!1},function(e,n,i){var t=i(6);e.exports=function(e,n){if(!t(e))return e;var i,l;if(n&&"function"==typeof(i=e.toString)&&!t(l=i.call(e)))return l;if("function"==typeof(i=e.valueOf)&&!t(l=i.call(e)))return l;if(!n&&"function"==typeof(i=e.toString)&&!t(l=i.call(e)))return l;throw TypeError("Can't convert object to primitive value")}},function(e,n,i){var t=i(0),l=i(5);e.exports=function(e,n){try{l(t,e,n)}catch(i){t[e]=n}return n}},function(e,n){e.exports=function(e){if(void 0==e)throw TypeError("Can't call method on "+e);return e}},function(e,n){var i=Math.ceil,t=Math.floor;e.exports=function(e){return isNaN(e=+e)?0:(e>0?t:i)(e)}},function(e,n,i){var t;
  2 +/*!
  3 + Copyright (c) 2016 Jed Watson.
  4 + Licensed under the MIT License (MIT), see
  5 + http://jedwatson.github.io/classnames
  6 +*/
  7 +/*!
  8 + Copyright (c) 2016 Jed Watson.
  9 + Licensed under the MIT License (MIT), see
  10 + http://jedwatson.github.io/classnames
  11 +*/
  12 +!function(){"use strict";var i=function(){function e(){}function n(e,n){for(var i=n.length,t=0;t<i;++t)l(e,n[t])}e.prototype=Object.create(null);var i={}.hasOwnProperty;var t=/\s+/;function l(e,l){if(l){var r=typeof l;"string"===r?function(e,n){for(var i=n.split(t),l=i.length,r=0;r<l;++r)e[i[r]]=!0}(e,l):Array.isArray(l)?n(e,l):"object"===r?function(e,n){for(var t in n)i.call(n,t)&&(e[t]=!!n[t])}(e,l):"number"===r&&function(e,n){e[n]=!0}(e,l)}}return function(){for(var i=arguments.length,t=Array(i),l=0;l<i;l++)t[l]=arguments[l];var r=new e;n(r,t);var o=[];for(var a in r)r[a]&&o.push(a);return o.join(" ")}}();void 0!==e&&e.exports?e.exports=i:void 0===(t=function(){return i}.apply(n,[]))||(e.exports=t)}()},function(e,n,i){var t=i(7).f,l=i(1),r=i(2)("toStringTag");e.exports=function(e,n,i){e&&!l(e=i?e:e.prototype,r)&&t(e,r,{configurable:!0,value:n})}},function(e,n,i){var t=i(20);e.exports=function(e){return Object(t(e))}},function(e,n,i){var t=i(1),l=i(24),r=i(16),o=i(63),a=r("IE_PROTO"),c=Object.prototype;e.exports=o?Object.getPrototypeOf:function(e){return e=l(e),t(e,a)?e[a]:"function"==typeof e.constructor&&e instanceof e.constructor?e.constructor.prototype:e instanceof Object?c:null}},function(e,n,i){"use strict";var t,l,r,o=i(25),a=i(5),c=i(1),p=i(2),y=i(17),h=p("iterator"),x=!1;[].keys&&("next"in(r=[].keys())?(l=o(o(r)))!==Object.prototype&&(t=l):x=!0),void 0==t&&(t={}),y||c(t,h)||a(t,h,function(){return this}),e.exports={IteratorPrototype:t,BUGGY_SAFARI_ITERATORS:x}},function(e,n,i){var t=i(21),l=Math.min;e.exports=function(e){return e>0?l(t(e),9007199254740991):0}},function(e,n,i){var t=i(1),l=i(14),r=i(68),o=i(15),a=r(!1);e.exports=function(e,n){var i,r=l(e),c=0,p=[];for(i in r)!t(o,i)&&t(r,i)&&p.push(i);for(;n.length>c;)t(r,i=n[c++])&&(~a(p,i)||p.push(i));return p}},function(e,n,i){var t=i(0),l=i(11),r=i(5),o=i(1),a=i(19),c=i(36),p=i(37),y=p.get,h=p.enforce,x=String(c).split("toString");l("inspectSource",function(e){return c.call(e)}),(e.exports=function(e,n,i,l){var c=!!l&&!!l.unsafe,p=!!l&&!!l.enumerable,y=!!l&&!!l.noTargetGet;"function"==typeof i&&("string"!=typeof n||o(i,"name")||r(i,"name",n),h(i).source=x.join("string"==typeof n?n:"")),e!==t?(c?!y&&e[n]&&(p=!0):delete e[n],p?e[n]=i:r(e,n,i)):p?e[n]=i:a(n,i)})(Function.prototype,"toString",function(){return"function"==typeof this&&y(this).source||c.call(this)})},function(e,n){var i={}.toString;e.exports=function(e){return i.call(e).slice(8,-1)}},function(e,n,i){var t=i(8),l=i(73),r=i(10),o=i(14),a=i(18),c=i(1),p=i(35),y=Object.getOwnPropertyDescriptor;n.f=t?y:function(e,n){if(e=o(e),n=a(n,!0),p)try{return y(e,n)}catch(e){}if(c(e,n))return r(!l.f.call(e,n),e[n])}},function(e,n,i){var t=i(0),l=i(31).f,r=i(5),o=i(29),a=i(19),c=i(71),p=i(65);e.exports=function(e,n){var i,y,h,x,s,u=e.target,d=e.global,f=e.stat;if(i=d?t:f?t[u]||a(u,{}):(t[u]||{}).prototype)for(y in n){if(x=n[y],h=e.noTargetGet?(s=l(i,y))&&s.value:i[y],!p(d?y:u+(f?".":"#")+y,e.forced)&&void 0!==h){if(typeof x==typeof h)continue;c(x,h)}(e.sham||h&&h.sham)&&r(x,"sham",!0),o(i,y,x,e)}}},function(e,n){var i=0,t=Math.random();e.exports=function(e){return"Symbol(".concat(void 0===e?"":e,")_",(++i+t).toString(36))}},function(e,n,i){var t=i(0),l=i(6),r=t.document,o=l(r)&&l(r.createElement);e.exports=function(e){return o?r.createElement(e):{}}},function(e,n,i){var t=i(8),l=i(4),r=i(34);e.exports=!t&&!l(function(){return 7!=Object.defineProperty(r("div"),"a",{get:function(){return 7}}).a})},function(e,n,i){var t=i(11);e.exports=t("native-function-to-string",Function.toString)},function(e,n,i){var t,l,r,o=i(76),a=i(0),c=i(6),p=i(5),y=i(1),h=i(16),x=i(15),s=a.WeakMap;if(o){var u=new s,d=u.get,f=u.has,g=u.set;t=function(e,n){return g.call(u,e,n),n},l=function(e){return d.call(u,e)||{}},r=function(e){return f.call(u,e)}}else{var v=h("state");x[v]=!0,t=function(e,n){return p(e,v,n),n},l=function(e){return y(e,v)?e[v]:{}},r=function(e){return y(e,v)}}e.exports={set:t,get:l,has:r,enforce:function(e){return r(e)?l(e):t(e,{})},getterFor:function(e){return function(n){var i;if(!c(n)||(i=l(n)).type!==e)throw TypeError("Incompatible receiver, "+e+" required");return i}}}},function(e,n,i){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var t=Object.assign||function(e){for(var n=1;n<arguments.length;n++){var i=arguments[n];for(var t in i)Object.prototype.hasOwnProperty.call(i,t)&&(e[t]=i[t])}return e},l=o(i(22)),r=o(i(12));function o(e){return e&&e.__esModule?e:{default:e}}n.default=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if("undefined"==typeof document)throw new Error("`feather.replace()` only works in a browser environment.");var n=document.querySelectorAll("[data-feather]");Array.from(n).forEach(function(n){return function(e){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},i=function(e){return Array.from(e.attributes).reduce(function(e,n){return e[n.name]=n.value,e},{})}(e),o=i["data-feather"];delete i["data-feather"];var a=r.default[o].toSvg(t({},n,i,{class:(0,l.default)(n.class,i.class)})),c=(new DOMParser).parseFromString(a,"image/svg+xml").querySelector("svg");e.parentNode.replaceChild(c,e)}(n,e)})}},function(e,n,i){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var t,l=i(12),r=(t=l)&&t.__esModule?t:{default:t};n.default=function(e){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(console.warn("feather.toSvg() is deprecated. Please use feather.icons[name].toSvg() instead."),!e)throw new Error("The required `key` (icon name) parameter is missing.");if(!r.default[e])throw new Error("No icon matching '"+e+"'. See the complete list of icons at https://feathericons.com");return r.default[e].toSvg(n)}},function(e){e.exports={activity:["pulse","health","action","motion"],airplay:["stream","cast","mirroring"],"alert-circle":["warning","alert","danger"],"alert-octagon":["warning","alert","danger"],"alert-triangle":["warning","alert","danger"],"align-center":["text alignment","center"],"align-justify":["text alignment","justified"],"align-left":["text alignment","left"],"align-right":["text alignment","right"],anchor:[],archive:["index","box"],"at-sign":["mention","at","email","message"],award:["achievement","badge"],aperture:["camera","photo"],"bar-chart":["statistics","diagram","graph"],"bar-chart-2":["statistics","diagram","graph"],battery:["power","electricity"],"battery-charging":["power","electricity"],bell:["alarm","notification","sound"],"bell-off":["alarm","notification","silent"],bluetooth:["wireless"],"book-open":["read","library"],book:["read","dictionary","booklet","magazine","library"],bookmark:["read","clip","marker","tag"],box:["cube"],briefcase:["work","bag","baggage","folder"],calendar:["date"],camera:["photo"],cast:["chromecast","airplay"],circle:["off","zero","record"],clipboard:["copy"],clock:["time","watch","alarm"],"cloud-drizzle":["weather","shower"],"cloud-lightning":["weather","bolt"],"cloud-rain":["weather"],"cloud-snow":["weather","blizzard"],cloud:["weather"],codepen:["logo"],codesandbox:["logo"],code:["source","programming"],coffee:["drink","cup","mug","tea","cafe","hot","beverage"],columns:["layout"],command:["keyboard","cmd","terminal","prompt"],compass:["navigation","safari","travel","direction"],copy:["clone","duplicate"],"corner-down-left":["arrow","return"],"corner-down-right":["arrow"],"corner-left-down":["arrow"],"corner-left-up":["arrow"],"corner-right-down":["arrow"],"corner-right-up":["arrow"],"corner-up-left":["arrow"],"corner-up-right":["arrow"],cpu:["processor","technology"],"credit-card":["purchase","payment","cc"],crop:["photo","image"],crosshair:["aim","target"],database:["storage","memory"],delete:["remove"],disc:["album","cd","dvd","music"],"dollar-sign":["currency","money","payment"],droplet:["water"],edit:["pencil","change"],"edit-2":["pencil","change"],"edit-3":["pencil","change"],eye:["view","watch"],"eye-off":["view","watch","hide","hidden"],"external-link":["outbound"],facebook:["logo","social"],"fast-forward":["music"],figma:["logo","design","tool"],"file-minus":["delete","remove","erase"],"file-plus":["add","create","new"],"file-text":["data","txt","pdf"],film:["movie","video"],filter:["funnel","hopper"],flag:["report"],"folder-minus":["directory"],"folder-plus":["directory"],folder:["directory"],framer:["logo","design","tool"],frown:["emoji","face","bad","sad","emotion"],gift:["present","box","birthday","party"],"git-branch":["code","version control"],"git-commit":["code","version control"],"git-merge":["code","version control"],"git-pull-request":["code","version control"],github:["logo","version control"],gitlab:["logo","version control"],globe:["world","browser","language","translate"],"hard-drive":["computer","server","memory","data"],hash:["hashtag","number","pound"],headphones:["music","audio","sound"],heart:["like","love","emotion"],"help-circle":["question mark"],hexagon:["shape","node.js","logo"],home:["house","living"],image:["picture"],inbox:["email"],instagram:["logo","camera"],key:["password","login","authentication","secure"],layers:["stack"],layout:["window","webpage"],"life-bouy":["help","life ring","support"],link:["chain","url"],"link-2":["chain","url"],linkedin:["logo","social media"],list:["options"],lock:["security","password","secure"],"log-in":["sign in","arrow","enter"],"log-out":["sign out","arrow","exit"],mail:["email","message"],"map-pin":["location","navigation","travel","marker"],map:["location","navigation","travel"],maximize:["fullscreen"],"maximize-2":["fullscreen","arrows","expand"],meh:["emoji","face","neutral","emotion"],menu:["bars","navigation","hamburger"],"message-circle":["comment","chat"],"message-square":["comment","chat"],"mic-off":["record","sound","mute"],mic:["record","sound","listen"],minimize:["exit fullscreen","close"],"minimize-2":["exit fullscreen","arrows","close"],minus:["subtract"],monitor:["tv","screen","display"],moon:["dark","night"],"more-horizontal":["ellipsis"],"more-vertical":["ellipsis"],"mouse-pointer":["arrow","cursor"],move:["arrows"],music:["note"],navigation:["location","travel"],"navigation-2":["location","travel"],octagon:["stop"],package:["box","container"],paperclip:["attachment"],pause:["music","stop"],"pause-circle":["music","audio","stop"],"pen-tool":["vector","drawing"],percent:["discount"],"phone-call":["ring"],"phone-forwarded":["call"],"phone-incoming":["call"],"phone-missed":["call"],"phone-off":["call","mute"],"phone-outgoing":["call"],phone:["call"],play:["music","start"],"pie-chart":["statistics","diagram"],"play-circle":["music","start"],plus:["add","new"],"plus-circle":["add","new"],"plus-square":["add","new"],pocket:["logo","save"],power:["on","off"],printer:["fax","office","device"],radio:["signal"],"refresh-cw":["synchronise","arrows"],"refresh-ccw":["arrows"],repeat:["loop","arrows"],rewind:["music"],"rotate-ccw":["arrow"],"rotate-cw":["arrow"],rss:["feed","subscribe"],save:["floppy disk"],scissors:["cut"],search:["find","magnifier","magnifying glass"],send:["message","mail","email","paper airplane","paper aeroplane"],settings:["cog","edit","gear","preferences"],"share-2":["network","connections"],shield:["security","secure"],"shield-off":["security","insecure"],"shopping-bag":["ecommerce","cart","purchase","store"],"shopping-cart":["ecommerce","cart","purchase","store"],shuffle:["music"],"skip-back":["music"],"skip-forward":["music"],slack:["logo"],slash:["ban","no"],sliders:["settings","controls"],smartphone:["cellphone","device"],smile:["emoji","face","happy","good","emotion"],speaker:["audio","music"],star:["bookmark","favorite","like"],"stop-circle":["media","music"],sun:["brightness","weather","light"],sunrise:["weather","time","morning","day"],sunset:["weather","time","evening","night"],tablet:["device"],tag:["label"],target:["logo","bullseye"],terminal:["code","command line","prompt"],thermometer:["temperature","celsius","fahrenheit","weather"],"thumbs-down":["dislike","bad","emotion"],"thumbs-up":["like","good","emotion"],"toggle-left":["on","off","switch"],"toggle-right":["on","off","switch"],tool:["settings","spanner"],trash:["garbage","delete","remove","bin"],"trash-2":["garbage","delete","remove","bin"],triangle:["delta"],truck:["delivery","van","shipping","transport","lorry"],tv:["television","stream"],twitch:["logo"],twitter:["logo","social"],type:["text"],umbrella:["rain","weather"],unlock:["security"],"user-check":["followed","subscribed"],"user-minus":["delete","remove","unfollow","unsubscribe"],"user-plus":["new","add","create","follow","subscribe"],"user-x":["delete","remove","unfollow","unsubscribe","unavailable"],user:["person","account"],users:["group"],"video-off":["camera","movie","film"],video:["camera","movie","film"],voicemail:["phone"],volume:["music","sound","mute"],"volume-1":["music","sound"],"volume-2":["music","sound"],"volume-x":["music","sound","mute"],watch:["clock","time"],"wifi-off":["disabled"],wifi:["connection","signal","wireless"],wind:["weather","air"],"x-circle":["cancel","close","delete","remove","times","clear"],"x-octagon":["delete","stop","alert","warning","times","clear"],"x-square":["cancel","close","delete","remove","times","clear"],x:["cancel","close","delete","remove","times","clear"],youtube:["logo","video","play"],"zap-off":["flash","camera","lightning"],zap:["flash","camera","lightning"],"zoom-in":["magnifying glass"],"zoom-out":["magnifying glass"]}},function(e){e.exports={activity:'<polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline>',airplay:'<path d="M5 17H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-1"></path><polygon points="12 15 17 21 7 21 12 15"></polygon>',"alert-circle":'<circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line>',"alert-octagon":'<polygon points="7.86 2 16.14 2 22 7.86 22 16.14 16.14 22 7.86 22 2 16.14 2 7.86 7.86 2"></polygon><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line>',"alert-triangle":'<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path><line x1="12" y1="9" x2="12" y2="13"></line><line x1="12" y1="17" x2="12.01" y2="17"></line>',"align-center":'<line x1="18" y1="10" x2="6" y2="10"></line><line x1="21" y1="6" x2="3" y2="6"></line><line x1="21" y1="14" x2="3" y2="14"></line><line x1="18" y1="18" x2="6" y2="18"></line>',"align-justify":'<line x1="21" y1="10" x2="3" y2="10"></line><line x1="21" y1="6" x2="3" y2="6"></line><line x1="21" y1="14" x2="3" y2="14"></line><line x1="21" y1="18" x2="3" y2="18"></line>',"align-left":'<line x1="17" y1="10" x2="3" y2="10"></line><line x1="21" y1="6" x2="3" y2="6"></line><line x1="21" y1="14" x2="3" y2="14"></line><line x1="17" y1="18" x2="3" y2="18"></line>',"align-right":'<line x1="21" y1="10" x2="7" y2="10"></line><line x1="21" y1="6" x2="3" y2="6"></line><line x1="21" y1="14" x2="3" y2="14"></line><line x1="21" y1="18" x2="7" y2="18"></line>',anchor:'<circle cx="12" cy="5" r="3"></circle><line x1="12" y1="22" x2="12" y2="8"></line><path d="M5 12H2a10 10 0 0 0 20 0h-3"></path>',aperture:'<circle cx="12" cy="12" r="10"></circle><line x1="14.31" y1="8" x2="20.05" y2="17.94"></line><line x1="9.69" y1="8" x2="21.17" y2="8"></line><line x1="7.38" y1="12" x2="13.12" y2="2.06"></line><line x1="9.69" y1="16" x2="3.95" y2="6.06"></line><line x1="14.31" y1="16" x2="2.83" y2="16"></line><line x1="16.62" y1="12" x2="10.88" y2="21.94"></line>',archive:'<polyline points="21 8 21 21 3 21 3 8"></polyline><rect x="1" y="3" width="22" height="5"></rect><line x1="10" y1="12" x2="14" y2="12"></line>',"arrow-down-circle":'<circle cx="12" cy="12" r="10"></circle><polyline points="8 12 12 16 16 12"></polyline><line x1="12" y1="8" x2="12" y2="16"></line>',"arrow-down-left":'<line x1="17" y1="7" x2="7" y2="17"></line><polyline points="17 17 7 17 7 7"></polyline>',"arrow-down-right":'<line x1="7" y1="7" x2="17" y2="17"></line><polyline points="17 7 17 17 7 17"></polyline>',"arrow-down":'<line x1="12" y1="5" x2="12" y2="19"></line><polyline points="19 12 12 19 5 12"></polyline>',"arrow-left-circle":'<circle cx="12" cy="12" r="10"></circle><polyline points="12 8 8 12 12 16"></polyline><line x1="16" y1="12" x2="8" y2="12"></line>',"arrow-left":'<line x1="19" y1="12" x2="5" y2="12"></line><polyline points="12 19 5 12 12 5"></polyline>',"arrow-right-circle":'<circle cx="12" cy="12" r="10"></circle><polyline points="12 16 16 12 12 8"></polyline><line x1="8" y1="12" x2="16" y2="12"></line>',"arrow-right":'<line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline>',"arrow-up-circle":'<circle cx="12" cy="12" r="10"></circle><polyline points="16 12 12 8 8 12"></polyline><line x1="12" y1="16" x2="12" y2="8"></line>',"arrow-up-left":'<line x1="17" y1="17" x2="7" y2="7"></line><polyline points="7 17 7 7 17 7"></polyline>',"arrow-up-right":'<line x1="7" y1="17" x2="17" y2="7"></line><polyline points="7 7 17 7 17 17"></polyline>',"arrow-up":'<line x1="12" y1="19" x2="12" y2="5"></line><polyline points="5 12 12 5 19 12"></polyline>',"at-sign":'<circle cx="12" cy="12" r="4"></circle><path d="M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-3.92 7.94"></path>',award:'<circle cx="12" cy="8" r="7"></circle><polyline points="8.21 13.89 7 23 12 20 17 23 15.79 13.88"></polyline>',"bar-chart-2":'<line x1="18" y1="20" x2="18" y2="10"></line><line x1="12" y1="20" x2="12" y2="4"></line><line x1="6" y1="20" x2="6" y2="14"></line>',"bar-chart":'<line x1="12" y1="20" x2="12" y2="10"></line><line x1="18" y1="20" x2="18" y2="4"></line><line x1="6" y1="20" x2="6" y2="16"></line>',"battery-charging":'<path d="M5 18H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h3.19M15 6h2a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-3.19"></path><line x1="23" y1="13" x2="23" y2="11"></line><polyline points="11 6 7 12 13 12 9 18"></polyline>',battery:'<rect x="1" y="6" width="18" height="12" rx="2" ry="2"></rect><line x1="23" y1="13" x2="23" y2="11"></line>',"bell-off":'<path d="M13.73 21a2 2 0 0 1-3.46 0"></path><path d="M18.63 13A17.89 17.89 0 0 1 18 8"></path><path d="M6.26 6.26A5.86 5.86 0 0 0 6 8c0 7-3 9-3 9h14"></path><path d="M18 8a6 6 0 0 0-9.33-5"></path><line x1="1" y1="1" x2="23" y2="23"></line>',bell:'<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"></path><path d="M13.73 21a2 2 0 0 1-3.46 0"></path>',bluetooth:'<polyline points="6.5 6.5 17.5 17.5 12 23 12 1 17.5 6.5 6.5 17.5"></polyline>',bold:'<path d="M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z"></path><path d="M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z"></path>',"book-open":'<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"></path><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"></path>',book:'<path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"></path><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"></path>',bookmark:'<path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"></path>',box:'<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"></path><polyline points="3.27 6.96 12 12.01 20.73 6.96"></polyline><line x1="12" y1="22.08" x2="12" y2="12"></line>',briefcase:'<rect x="2" y="7" width="20" height="14" rx="2" ry="2"></rect><path d="M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16"></path>',calendar:'<rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line>',"camera-off":'<line x1="1" y1="1" x2="23" y2="23"></line><path d="M21 21H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h3m3-3h6l2 3h4a2 2 0 0 1 2 2v9.34m-7.72-2.06a4 4 0 1 1-5.56-5.56"></path>',camera:'<path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"></path><circle cx="12" cy="13" r="4"></circle>',cast:'<path d="M2 16.1A5 5 0 0 1 5.9 20M2 12.05A9 9 0 0 1 9.95 20M2 8V6a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2h-6"></path><line x1="2" y1="20" x2="2.01" y2="20"></line>',"check-circle":'<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline>',"check-square":'<polyline points="9 11 12 14 22 4"></polyline><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"></path>',check:'<polyline points="20 6 9 17 4 12"></polyline>',"chevron-down":'<polyline points="6 9 12 15 18 9"></polyline>',"chevron-left":'<polyline points="15 18 9 12 15 6"></polyline>',"chevron-right":'<polyline points="9 18 15 12 9 6"></polyline>',"chevron-up":'<polyline points="18 15 12 9 6 15"></polyline>',"chevrons-down":'<polyline points="7 13 12 18 17 13"></polyline><polyline points="7 6 12 11 17 6"></polyline>',"chevrons-left":'<polyline points="11 17 6 12 11 7"></polyline><polyline points="18 17 13 12 18 7"></polyline>',"chevrons-right":'<polyline points="13 17 18 12 13 7"></polyline><polyline points="6 17 11 12 6 7"></polyline>',"chevrons-up":'<polyline points="17 11 12 6 7 11"></polyline><polyline points="17 18 12 13 7 18"></polyline>',chrome:'<circle cx="12" cy="12" r="10"></circle><circle cx="12" cy="12" r="4"></circle><line x1="21.17" y1="8" x2="12" y2="8"></line><line x1="3.95" y1="6.06" x2="8.54" y2="14"></line><line x1="10.88" y1="21.94" x2="15.46" y2="14"></line>',circle:'<circle cx="12" cy="12" r="10"></circle>',clipboard:'<path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"></path><rect x="8" y="2" width="8" height="4" rx="1" ry="1"></rect>',clock:'<circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline>',"cloud-drizzle":'<line x1="8" y1="19" x2="8" y2="21"></line><line x1="8" y1="13" x2="8" y2="15"></line><line x1="16" y1="19" x2="16" y2="21"></line><line x1="16" y1="13" x2="16" y2="15"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="12" y1="15" x2="12" y2="17"></line><path d="M20 16.58A5 5 0 0 0 18 7h-1.26A8 8 0 1 0 4 15.25"></path>',"cloud-lightning":'<path d="M19 16.9A5 5 0 0 0 18 7h-1.26a8 8 0 1 0-11.62 9"></path><polyline points="13 11 9 17 15 17 11 23"></polyline>',"cloud-off":'<path d="M22.61 16.95A5 5 0 0 0 18 10h-1.26a8 8 0 0 0-7.05-6M5 5a8 8 0 0 0 4 15h9a5 5 0 0 0 1.7-.3"></path><line x1="1" y1="1" x2="23" y2="23"></line>',"cloud-rain":'<line x1="16" y1="13" x2="16" y2="21"></line><line x1="8" y1="13" x2="8" y2="21"></line><line x1="12" y1="15" x2="12" y2="23"></line><path d="M20 16.58A5 5 0 0 0 18 7h-1.26A8 8 0 1 0 4 15.25"></path>',"cloud-snow":'<path d="M20 17.58A5 5 0 0 0 18 8h-1.26A8 8 0 1 0 4 16.25"></path><line x1="8" y1="16" x2="8.01" y2="16"></line><line x1="8" y1="20" x2="8.01" y2="20"></line><line x1="12" y1="18" x2="12.01" y2="18"></line><line x1="12" y1="22" x2="12.01" y2="22"></line><line x1="16" y1="16" x2="16.01" y2="16"></line><line x1="16" y1="20" x2="16.01" y2="20"></line>',cloud:'<path d="M18 10h-1.26A8 8 0 1 0 9 20h9a5 5 0 0 0 0-10z"></path>',code:'<polyline points="16 18 22 12 16 6"></polyline><polyline points="8 6 2 12 8 18"></polyline>',codepen:'<polygon points="12 2 22 8.5 22 15.5 12 22 2 15.5 2 8.5 12 2"></polygon><line x1="12" y1="22" x2="12" y2="15.5"></line><polyline points="22 8.5 12 15.5 2 8.5"></polyline><polyline points="2 15.5 12 8.5 22 15.5"></polyline><line x1="12" y1="2" x2="12" y2="8.5"></line>',codesandbox:'<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"></path><polyline points="7.5 4.21 12 6.81 16.5 4.21"></polyline><polyline points="7.5 19.79 7.5 14.6 3 12"></polyline><polyline points="21 12 16.5 14.6 16.5 19.79"></polyline><polyline points="3.27 6.96 12 12.01 20.73 6.96"></polyline><line x1="12" y1="22.08" x2="12" y2="12"></line>',coffee:'<path d="M18 8h1a4 4 0 0 1 0 8h-1"></path><path d="M2 8h16v9a4 4 0 0 1-4 4H6a4 4 0 0 1-4-4V8z"></path><line x1="6" y1="1" x2="6" y2="4"></line><line x1="10" y1="1" x2="10" y2="4"></line><line x1="14" y1="1" x2="14" y2="4"></line>',columns:'<path d="M12 3h7a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-7m0-18H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h7m0-18v18"></path>',command:'<path d="M18 3a3 3 0 0 0-3 3v12a3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0-3-3H6a3 3 0 0 0-3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3V6a3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3h12a3 3 0 0 0 3-3 3 3 0 0 0-3-3z"></path>',compass:'<circle cx="12" cy="12" r="10"></circle><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"></polygon>',copy:'<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>',"corner-down-left":'<polyline points="9 10 4 15 9 20"></polyline><path d="M20 4v7a4 4 0 0 1-4 4H4"></path>',"corner-down-right":'<polyline points="15 10 20 15 15 20"></polyline><path d="M4 4v7a4 4 0 0 0 4 4h12"></path>',"corner-left-down":'<polyline points="14 15 9 20 4 15"></polyline><path d="M20 4h-7a4 4 0 0 0-4 4v12"></path>',"corner-left-up":'<polyline points="14 9 9 4 4 9"></polyline><path d="M20 20h-7a4 4 0 0 1-4-4V4"></path>',"corner-right-down":'<polyline points="10 15 15 20 20 15"></polyline><path d="M4 4h7a4 4 0 0 1 4 4v12"></path>',"corner-right-up":'<polyline points="10 9 15 4 20 9"></polyline><path d="M4 20h7a4 4 0 0 0 4-4V4"></path>',"corner-up-left":'<polyline points="9 14 4 9 9 4"></polyline><path d="M20 20v-7a4 4 0 0 0-4-4H4"></path>',"corner-up-right":'<polyline points="15 14 20 9 15 4"></polyline><path d="M4 20v-7a4 4 0 0 1 4-4h12"></path>',cpu:'<rect x="4" y="4" width="16" height="16" rx="2" ry="2"></rect><rect x="9" y="9" width="6" height="6"></rect><line x1="9" y1="1" x2="9" y2="4"></line><line x1="15" y1="1" x2="15" y2="4"></line><line x1="9" y1="20" x2="9" y2="23"></line><line x1="15" y1="20" x2="15" y2="23"></line><line x1="20" y1="9" x2="23" y2="9"></line><line x1="20" y1="14" x2="23" y2="14"></line><line x1="1" y1="9" x2="4" y2="9"></line><line x1="1" y1="14" x2="4" y2="14"></line>',"credit-card":'<rect x="1" y="4" width="22" height="16" rx="2" ry="2"></rect><line x1="1" y1="10" x2="23" y2="10"></line>',crop:'<path d="M6.13 1L6 16a2 2 0 0 0 2 2h15"></path><path d="M1 6.13L16 6a2 2 0 0 1 2 2v15"></path>',crosshair:'<circle cx="12" cy="12" r="10"></circle><line x1="22" y1="12" x2="18" y2="12"></line><line x1="6" y1="12" x2="2" y2="12"></line><line x1="12" y1="6" x2="12" y2="2"></line><line x1="12" y1="22" x2="12" y2="18"></line>',database:'<ellipse cx="12" cy="5" rx="9" ry="3"></ellipse><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"></path><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"></path>',delete:'<path d="M21 4H8l-7 8 7 8h13a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2z"></path><line x1="18" y1="9" x2="12" y2="15"></line><line x1="12" y1="9" x2="18" y2="15"></line>',disc:'<circle cx="12" cy="12" r="10"></circle><circle cx="12" cy="12" r="3"></circle>',"divide-circle":'<line x1="8" y1="12" x2="16" y2="12"></line><line x1="12" y1="16" x2="12" y2="16"></line><line x1="12" y1="8" x2="12" y2="8"></line><circle cx="12" cy="12" r="10"></circle>',"divide-square":'<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="8" y1="12" x2="16" y2="12"></line><line x1="12" y1="16" x2="12" y2="16"></line><line x1="12" y1="8" x2="12" y2="8"></line>',divide:'<circle cx="12" cy="6" r="2"></circle><line x1="5" y1="12" x2="19" y2="12"></line><circle cx="12" cy="18" r="2"></circle>',"dollar-sign":'<line x1="12" y1="1" x2="12" y2="23"></line><path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"></path>',"download-cloud":'<polyline points="8 17 12 21 16 17"></polyline><line x1="12" y1="12" x2="12" y2="21"></line><path d="M20.88 18.09A5 5 0 0 0 18 9h-1.26A8 8 0 1 0 3 16.29"></path>',download:'<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line>',dribbble:'<circle cx="12" cy="12" r="10"></circle><path d="M8.56 2.75c4.37 6.03 6.02 9.42 8.03 17.72m2.54-15.38c-3.72 4.35-8.94 5.66-16.88 5.85m19.5 1.9c-3.5-.93-6.63-.82-8.94 0-2.58.92-5.01 2.86-7.44 6.32"></path>',droplet:'<path d="M12 2.69l5.66 5.66a8 8 0 1 1-11.31 0z"></path>',"edit-2":'<path d="M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z"></path>',"edit-3":'<path d="M12 20h9"></path><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"></path>',edit:'<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>',"external-link":'<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line>',"eye-off":'<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"></path><line x1="1" y1="1" x2="23" y2="23"></line>',eye:'<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path><circle cx="12" cy="12" r="3"></circle>',facebook:'<path d="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z"></path>',"fast-forward":'<polygon points="13 19 22 12 13 5 13 19"></polygon><polygon points="2 19 11 12 2 5 2 19"></polygon>',feather:'<path d="M20.24 12.24a6 6 0 0 0-8.49-8.49L5 10.5V19h8.5z"></path><line x1="16" y1="8" x2="2" y2="22"></line><line x1="17.5" y1="15" x2="9" y2="15"></line>',figma:'<path d="M5 5.5A3.5 3.5 0 0 1 8.5 2H12v7H8.5A3.5 3.5 0 0 1 5 5.5z"></path><path d="M12 2h3.5a3.5 3.5 0 1 1 0 7H12V2z"></path><path d="M12 12.5a3.5 3.5 0 1 1 7 0 3.5 3.5 0 1 1-7 0z"></path><path d="M5 19.5A3.5 3.5 0 0 1 8.5 16H12v3.5a3.5 3.5 0 1 1-7 0z"></path><path d="M5 12.5A3.5 3.5 0 0 1 8.5 9H12v7H8.5A3.5 3.5 0 0 1 5 12.5z"></path>',"file-minus":'<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="9" y1="15" x2="15" y2="15"></line>',"file-plus":'<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="12" y1="18" x2="12" y2="12"></line><line x1="9" y1="15" x2="15" y2="15"></line>',"file-text":'<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline>',file:'<path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"></path><polyline points="13 2 13 9 20 9"></polyline>',film:'<rect x="2" y="2" width="20" height="20" rx="2.18" ry="2.18"></rect><line x1="7" y1="2" x2="7" y2="22"></line><line x1="17" y1="2" x2="17" y2="22"></line><line x1="2" y1="12" x2="22" y2="12"></line><line x1="2" y1="7" x2="7" y2="7"></line><line x1="2" y1="17" x2="7" y2="17"></line><line x1="17" y1="17" x2="22" y2="17"></line><line x1="17" y1="7" x2="22" y2="7"></line>',filter:'<polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"></polygon>',flag:'<path d="M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z"></path><line x1="4" y1="22" x2="4" y2="15"></line>',"folder-minus":'<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path><line x1="9" y1="14" x2="15" y2="14"></line>',"folder-plus":'<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path><line x1="12" y1="11" x2="12" y2="17"></line><line x1="9" y1="14" x2="15" y2="14"></line>',folder:'<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path>',framer:'<path d="M5 16V9h14V2H5l14 14h-7m-7 0l7 7v-7m-7 0h7"></path>',frown:'<circle cx="12" cy="12" r="10"></circle><path d="M16 16s-1.5-2-4-2-4 2-4 2"></path><line x1="9" y1="9" x2="9.01" y2="9"></line><line x1="15" y1="9" x2="15.01" y2="9"></line>',gift:'<polyline points="20 12 20 22 4 22 4 12"></polyline><rect x="2" y="7" width="20" height="5"></rect><line x1="12" y1="22" x2="12" y2="7"></line><path d="M12 7H7.5a2.5 2.5 0 0 1 0-5C11 2 12 7 12 7z"></path><path d="M12 7h4.5a2.5 2.5 0 0 0 0-5C13 2 12 7 12 7z"></path>',"git-branch":'<line x1="6" y1="3" x2="6" y2="15"></line><circle cx="18" cy="6" r="3"></circle><circle cx="6" cy="18" r="3"></circle><path d="M18 9a9 9 0 0 1-9 9"></path>',"git-commit":'<circle cx="12" cy="12" r="4"></circle><line x1="1.05" y1="12" x2="7" y2="12"></line><line x1="17.01" y1="12" x2="22.96" y2="12"></line>',"git-merge":'<circle cx="18" cy="18" r="3"></circle><circle cx="6" cy="6" r="3"></circle><path d="M6 21V9a9 9 0 0 0 9 9"></path>',"git-pull-request":'<circle cx="18" cy="18" r="3"></circle><circle cx="6" cy="6" r="3"></circle><path d="M13 6h3a2 2 0 0 1 2 2v7"></path><line x1="6" y1="9" x2="6" y2="21"></line>',github:'<path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"></path>',gitlab:'<path d="M22.65 14.39L12 22.13 1.35 14.39a.84.84 0 0 1-.3-.94l1.22-3.78 2.44-7.51A.42.42 0 0 1 4.82 2a.43.43 0 0 1 .58 0 .42.42 0 0 1 .11.18l2.44 7.49h8.1l2.44-7.51A.42.42 0 0 1 18.6 2a.43.43 0 0 1 .58 0 .42.42 0 0 1 .11.18l2.44 7.51L23 13.45a.84.84 0 0 1-.35.94z"></path>',globe:'<circle cx="12" cy="12" r="10"></circle><line x1="2" y1="12" x2="22" y2="12"></line><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path>',grid:'<rect x="3" y="3" width="7" height="7"></rect><rect x="14" y="3" width="7" height="7"></rect><rect x="14" y="14" width="7" height="7"></rect><rect x="3" y="14" width="7" height="7"></rect>',"hard-drive":'<line x1="22" y1="12" x2="2" y2="12"></line><path d="M5.45 5.11L2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"></path><line x1="6" y1="16" x2="6.01" y2="16"></line><line x1="10" y1="16" x2="10.01" y2="16"></line>',hash:'<line x1="4" y1="9" x2="20" y2="9"></line><line x1="4" y1="15" x2="20" y2="15"></line><line x1="10" y1="3" x2="8" y2="21"></line><line x1="16" y1="3" x2="14" y2="21"></line>',headphones:'<path d="M3 18v-6a9 9 0 0 1 18 0v6"></path><path d="M21 19a2 2 0 0 1-2 2h-1a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h3zM3 19a2 2 0 0 0 2 2h1a2 2 0 0 0 2-2v-3a2 2 0 0 0-2-2H3z"></path>',heart:'<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path>',"help-circle":'<circle cx="12" cy="12" r="10"></circle><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path><line x1="12" y1="17" x2="12.01" y2="17"></line>',hexagon:'<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"></path>',home:'<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline>',image:'<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><circle cx="8.5" cy="8.5" r="1.5"></circle><polyline points="21 15 16 10 5 21"></polyline>',inbox:'<polyline points="22 12 16 12 14 15 10 15 8 12 2 12"></polyline><path d="M5.45 5.11L2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"></path>',info:'<circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line>',instagram:'<rect x="2" y="2" width="20" height="20" rx="5" ry="5"></rect><path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z"></path><line x1="17.5" y1="6.5" x2="17.51" y2="6.5"></line>',italic:'<line x1="19" y1="4" x2="10" y2="4"></line><line x1="14" y1="20" x2="5" y2="20"></line><line x1="15" y1="4" x2="9" y2="20"></line>',key:'<path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4"></path>',layers:'<polygon points="12 2 2 7 12 12 22 7 12 2"></polygon><polyline points="2 17 12 22 22 17"></polyline><polyline points="2 12 12 17 22 12"></polyline>',layout:'<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="3" y1="9" x2="21" y2="9"></line><line x1="9" y1="21" x2="9" y2="9"></line>',"life-buoy":'<circle cx="12" cy="12" r="10"></circle><circle cx="12" cy="12" r="4"></circle><line x1="4.93" y1="4.93" x2="9.17" y2="9.17"></line><line x1="14.83" y1="14.83" x2="19.07" y2="19.07"></line><line x1="14.83" y1="9.17" x2="19.07" y2="4.93"></line><line x1="14.83" y1="9.17" x2="18.36" y2="5.64"></line><line x1="4.93" y1="19.07" x2="9.17" y2="14.83"></line>',"link-2":'<path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3"></path><line x1="8" y1="12" x2="16" y2="12"></line>',link:'<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path>',linkedin:'<path d="M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-2-2 2 2 0 0 0-2 2v7h-4v-7a6 6 0 0 1 6-6z"></path><rect x="2" y="9" width="4" height="12"></rect><circle cx="4" cy="4" r="2"></circle>',list:'<line x1="8" y1="6" x2="21" y2="6"></line><line x1="8" y1="12" x2="21" y2="12"></line><line x1="8" y1="18" x2="21" y2="18"></line><line x1="3" y1="6" x2="3.01" y2="6"></line><line x1="3" y1="12" x2="3.01" y2="12"></line><line x1="3" y1="18" x2="3.01" y2="18"></line>',loader:'<line x1="12" y1="2" x2="12" y2="6"></line><line x1="12" y1="18" x2="12" y2="22"></line><line x1="4.93" y1="4.93" x2="7.76" y2="7.76"></line><line x1="16.24" y1="16.24" x2="19.07" y2="19.07"></line><line x1="2" y1="12" x2="6" y2="12"></line><line x1="18" y1="12" x2="22" y2="12"></line><line x1="4.93" y1="19.07" x2="7.76" y2="16.24"></line><line x1="16.24" y1="7.76" x2="19.07" y2="4.93"></line>',lock:'<rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path>',"log-in":'<path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"></path><polyline points="10 17 15 12 10 7"></polyline><line x1="15" y1="12" x2="3" y2="12"></line>',"log-out":'<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path><polyline points="16 17 21 12 16 7"></polyline><line x1="21" y1="12" x2="9" y2="12"></line>',mail:'<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path><polyline points="22,6 12,13 2,6"></polyline>',"map-pin":'<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path><circle cx="12" cy="10" r="3"></circle>',map:'<polygon points="1 6 1 22 8 18 16 22 23 18 23 2 16 6 8 2 1 6"></polygon><line x1="8" y1="2" x2="8" y2="18"></line><line x1="16" y1="6" x2="16" y2="22"></line>',"maximize-2":'<polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" y1="3" x2="14" y2="10"></line><line x1="3" y1="21" x2="10" y2="14"></line>',maximize:'<path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"></path>',meh:'<circle cx="12" cy="12" r="10"></circle><line x1="8" y1="15" x2="16" y2="15"></line><line x1="9" y1="9" x2="9.01" y2="9"></line><line x1="15" y1="9" x2="15.01" y2="9"></line>',menu:'<line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="18" x2="21" y2="18"></line>',"message-circle":'<path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"></path>',"message-square":'<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>',"mic-off":'<line x1="1" y1="1" x2="23" y2="23"></line><path d="M9 9v3a3 3 0 0 0 5.12 2.12M15 9.34V4a3 3 0 0 0-5.94-.6"></path><path d="M17 16.95A7 7 0 0 1 5 12v-2m14 0v2a7 7 0 0 1-.11 1.23"></path><line x1="12" y1="19" x2="12" y2="23"></line><line x1="8" y1="23" x2="16" y2="23"></line>',mic:'<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"></path><path d="M19 10v2a7 7 0 0 1-14 0v-2"></path><line x1="12" y1="19" x2="12" y2="23"></line><line x1="8" y1="23" x2="16" y2="23"></line>',"minimize-2":'<polyline points="4 14 10 14 10 20"></polyline><polyline points="20 10 14 10 14 4"></polyline><line x1="14" y1="10" x2="21" y2="3"></line><line x1="3" y1="21" x2="10" y2="14"></line>',minimize:'<path d="M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3"></path>',"minus-circle":'<circle cx="12" cy="12" r="10"></circle><line x1="8" y1="12" x2="16" y2="12"></line>',"minus-square":'<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="8" y1="12" x2="16" y2="12"></line>',minus:'<line x1="5" y1="12" x2="19" y2="12"></line>',monitor:'<rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect><line x1="8" y1="21" x2="16" y2="21"></line><line x1="12" y1="17" x2="12" y2="21"></line>',moon:'<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>',"more-horizontal":'<circle cx="12" cy="12" r="1"></circle><circle cx="19" cy="12" r="1"></circle><circle cx="5" cy="12" r="1"></circle>',"more-vertical":'<circle cx="12" cy="12" r="1"></circle><circle cx="12" cy="5" r="1"></circle><circle cx="12" cy="19" r="1"></circle>',"mouse-pointer":'<path d="M3 3l7.07 16.97 2.51-7.39 7.39-2.51L3 3z"></path><path d="M13 13l6 6"></path>',move:'<polyline points="5 9 2 12 5 15"></polyline><polyline points="9 5 12 2 15 5"></polyline><polyline points="15 19 12 22 9 19"></polyline><polyline points="19 9 22 12 19 15"></polyline><line x1="2" y1="12" x2="22" y2="12"></line><line x1="12" y1="2" x2="12" y2="22"></line>',music:'<path d="M9 18V5l12-2v13"></path><circle cx="6" cy="18" r="3"></circle><circle cx="18" cy="16" r="3"></circle>',"navigation-2":'<polygon points="12 2 19 21 12 17 5 21 12 2"></polygon>',navigation:'<polygon points="3 11 22 2 13 21 11 13 3 11"></polygon>',octagon:'<polygon points="7.86 2 16.14 2 22 7.86 22 16.14 16.14 22 7.86 22 2 16.14 2 7.86 7.86 2"></polygon>',package:'<line x1="16.5" y1="9.4" x2="7.5" y2="4.21"></line><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"></path><polyline points="3.27 6.96 12 12.01 20.73 6.96"></polyline><line x1="12" y1="22.08" x2="12" y2="12"></line>',paperclip:'<path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"></path>',"pause-circle":'<circle cx="12" cy="12" r="10"></circle><line x1="10" y1="15" x2="10" y2="9"></line><line x1="14" y1="15" x2="14" y2="9"></line>',pause:'<rect x="6" y="4" width="4" height="16"></rect><rect x="14" y="4" width="4" height="16"></rect>',"pen-tool":'<path d="M12 19l7-7 3 3-7 7-3-3z"></path><path d="M18 13l-1.5-7.5L2 2l3.5 14.5L13 18l5-5z"></path><path d="M2 2l7.586 7.586"></path><circle cx="11" cy="11" r="2"></circle>',percent:'<line x1="19" y1="5" x2="5" y2="19"></line><circle cx="6.5" cy="6.5" r="2.5"></circle><circle cx="17.5" cy="17.5" r="2.5"></circle>',"phone-call":'<path d="M15.05 5A5 5 0 0 1 19 8.95M15.05 1A9 9 0 0 1 23 8.94m-1 7.98v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"></path>',"phone-forwarded":'<polyline points="19 1 23 5 19 9"></polyline><line x1="15" y1="5" x2="23" y2="5"></line><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"></path>',"phone-incoming":'<polyline points="16 2 16 8 22 8"></polyline><line x1="23" y1="1" x2="16" y2="8"></line><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"></path>',"phone-missed":'<line x1="23" y1="1" x2="17" y2="7"></line><line x1="17" y1="1" x2="23" y2="7"></line><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"></path>',"phone-off":'<path d="M10.68 13.31a16 16 0 0 0 3.41 2.6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7 2 2 0 0 1 1.72 2v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.42 19.42 0 0 1-3.33-2.67m-2.67-3.34a19.79 19.79 0 0 1-3.07-8.63A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91"></path><line x1="23" y1="1" x2="1" y2="23"></line>',"phone-outgoing":'<polyline points="23 7 23 1 17 1"></polyline><line x1="16" y1="8" x2="23" y2="1"></line><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"></path>',phone:'<path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"></path>',"pie-chart":'<path d="M21.21 15.89A10 10 0 1 1 8 2.83"></path><path d="M22 12A10 10 0 0 0 12 2v10z"></path>',"play-circle":'<circle cx="12" cy="12" r="10"></circle><polygon points="10 8 16 12 10 16 10 8"></polygon>',play:'<polygon points="5 3 19 12 5 21 5 3"></polygon>',"plus-circle":'<circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="16"></line><line x1="8" y1="12" x2="16" y2="12"></line>',"plus-square":'<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="12" y1="8" x2="12" y2="16"></line><line x1="8" y1="12" x2="16" y2="12"></line>',plus:'<line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line>',pocket:'<path d="M4 3h16a2 2 0 0 1 2 2v6a10 10 0 0 1-10 10A10 10 0 0 1 2 11V5a2 2 0 0 1 2-2z"></path><polyline points="8 10 12 14 16 10"></polyline>',power:'<path d="M18.36 6.64a9 9 0 1 1-12.73 0"></path><line x1="12" y1="2" x2="12" y2="12"></line>',printer:'<polyline points="6 9 6 2 18 2 18 9"></polyline><path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"></path><rect x="6" y="14" width="12" height="8"></rect>',radio:'<circle cx="12" cy="12" r="2"></circle><path d="M16.24 7.76a6 6 0 0 1 0 8.49m-8.48-.01a6 6 0 0 1 0-8.49m11.31-2.82a10 10 0 0 1 0 14.14m-14.14 0a10 10 0 0 1 0-14.14"></path>',"refresh-ccw":'<polyline points="1 4 1 10 7 10"></polyline><polyline points="23 20 23 14 17 14"></polyline><path d="M20.49 9A9 9 0 0 0 5.64 5.64L1 10m22 4l-4.64 4.36A9 9 0 0 1 3.51 15"></path>',"refresh-cw":'<polyline points="23 4 23 10 17 10"></polyline><polyline points="1 20 1 14 7 14"></polyline><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path>',repeat:'<polyline points="17 1 21 5 17 9"></polyline><path d="M3 11V9a4 4 0 0 1 4-4h14"></path><polyline points="7 23 3 19 7 15"></polyline><path d="M21 13v2a4 4 0 0 1-4 4H3"></path>',rewind:'<polygon points="11 19 2 12 11 5 11 19"></polygon><polygon points="22 19 13 12 22 5 22 19"></polygon>',"rotate-ccw":'<polyline points="1 4 1 10 7 10"></polyline><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"></path>',"rotate-cw":'<polyline points="23 4 23 10 17 10"></polyline><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"></path>',rss:'<path d="M4 11a9 9 0 0 1 9 9"></path><path d="M4 4a16 16 0 0 1 16 16"></path><circle cx="5" cy="19" r="1"></circle>',save:'<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"></path><polyline points="17 21 17 13 7 13 7 21"></polyline><polyline points="7 3 7 8 15 8"></polyline>',scissors:'<circle cx="6" cy="6" r="3"></circle><circle cx="6" cy="18" r="3"></circle><line x1="20" y1="4" x2="8.12" y2="15.88"></line><line x1="14.47" y1="14.48" x2="20" y2="20"></line><line x1="8.12" y1="8.12" x2="12" y2="12"></line>',search:'<circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line>',send:'<line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>',server:'<rect x="2" y="2" width="20" height="8" rx="2" ry="2"></rect><rect x="2" y="14" width="20" height="8" rx="2" ry="2"></rect><line x1="6" y1="6" x2="6.01" y2="6"></line><line x1="6" y1="18" x2="6.01" y2="18"></line>',settings:'<circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path>',"share-2":'<circle cx="18" cy="5" r="3"></circle><circle cx="6" cy="12" r="3"></circle><circle cx="18" cy="19" r="3"></circle><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"></line><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"></line>',share:'<path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"></path><polyline points="16 6 12 2 8 6"></polyline><line x1="12" y1="2" x2="12" y2="15"></line>',"shield-off":'<path d="M19.69 14a6.9 6.9 0 0 0 .31-2V5l-8-3-3.16 1.18"></path><path d="M4.73 4.73L4 5v7c0 6 8 10 8 10a20.29 20.29 0 0 0 5.62-4.38"></path><line x1="1" y1="1" x2="23" y2="23"></line>',shield:'<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path>',"shopping-bag":'<path d="M6 2L3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"></path><line x1="3" y1="6" x2="21" y2="6"></line><path d="M16 10a4 4 0 0 1-8 0"></path>',"shopping-cart":'<circle cx="9" cy="21" r="1"></circle><circle cx="20" cy="21" r="1"></circle><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"></path>',shuffle:'<polyline points="16 3 21 3 21 8"></polyline><line x1="4" y1="20" x2="21" y2="3"></line><polyline points="21 16 21 21 16 21"></polyline><line x1="15" y1="15" x2="21" y2="21"></line><line x1="4" y1="4" x2="9" y2="9"></line>',sidebar:'<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="9" y1="3" x2="9" y2="21"></line>',"skip-back":'<polygon points="19 20 9 12 19 4 19 20"></polygon><line x1="5" y1="19" x2="5" y2="5"></line>',"skip-forward":'<polygon points="5 4 15 12 5 20 5 4"></polygon><line x1="19" y1="5" x2="19" y2="19"></line>',slack:'<path d="M14.5 10c-.83 0-1.5-.67-1.5-1.5v-5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5v5c0 .83-.67 1.5-1.5 1.5z"></path><path d="M20.5 10H19V8.5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"></path><path d="M9.5 14c.83 0 1.5.67 1.5 1.5v5c0 .83-.67 1.5-1.5 1.5S8 21.33 8 20.5v-5c0-.83.67-1.5 1.5-1.5z"></path><path d="M3.5 14H5v1.5c0 .83-.67 1.5-1.5 1.5S2 16.33 2 15.5 2.67 14 3.5 14z"></path><path d="M14 14.5c0-.83.67-1.5 1.5-1.5h5c.83 0 1.5.67 1.5 1.5s-.67 1.5-1.5 1.5h-5c-.83 0-1.5-.67-1.5-1.5z"></path><path d="M15.5 19H14v1.5c0 .83.67 1.5 1.5 1.5s1.5-.67 1.5-1.5-.67-1.5-1.5-1.5z"></path><path d="M10 9.5C10 8.67 9.33 8 8.5 8h-5C2.67 8 2 8.67 2 9.5S2.67 11 3.5 11h5c.83 0 1.5-.67 1.5-1.5z"></path><path d="M8.5 5H10V3.5C10 2.67 9.33 2 8.5 2S7 2.67 7 3.5 7.67 5 8.5 5z"></path>',slash:'<circle cx="12" cy="12" r="10"></circle><line x1="4.93" y1="4.93" x2="19.07" y2="19.07"></line>',sliders:'<line x1="4" y1="21" x2="4" y2="14"></line><line x1="4" y1="10" x2="4" y2="3"></line><line x1="12" y1="21" x2="12" y2="12"></line><line x1="12" y1="8" x2="12" y2="3"></line><line x1="20" y1="21" x2="20" y2="16"></line><line x1="20" y1="12" x2="20" y2="3"></line><line x1="1" y1="14" x2="7" y2="14"></line><line x1="9" y1="8" x2="15" y2="8"></line><line x1="17" y1="16" x2="23" y2="16"></line>',smartphone:'<rect x="5" y="2" width="14" height="20" rx="2" ry="2"></rect><line x1="12" y1="18" x2="12.01" y2="18"></line>',smile:'<circle cx="12" cy="12" r="10"></circle><path d="M8 14s1.5 2 4 2 4-2 4-2"></path><line x1="9" y1="9" x2="9.01" y2="9"></line><line x1="15" y1="9" x2="15.01" y2="9"></line>',speaker:'<rect x="4" y="2" width="16" height="20" rx="2" ry="2"></rect><circle cx="12" cy="14" r="4"></circle><line x1="12" y1="6" x2="12.01" y2="6"></line>',square:'<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>',star:'<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon>',"stop-circle":'<circle cx="12" cy="12" r="10"></circle><rect x="9" y="9" width="6" height="6"></rect>',sun:'<circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>',sunrise:'<path d="M17 18a5 5 0 0 0-10 0"></path><line x1="12" y1="2" x2="12" y2="9"></line><line x1="4.22" y1="10.22" x2="5.64" y2="11.64"></line><line x1="1" y1="18" x2="3" y2="18"></line><line x1="21" y1="18" x2="23" y2="18"></line><line x1="18.36" y1="11.64" x2="19.78" y2="10.22"></line><line x1="23" y1="22" x2="1" y2="22"></line><polyline points="8 6 12 2 16 6"></polyline>',sunset:'<path d="M17 18a5 5 0 0 0-10 0"></path><line x1="12" y1="9" x2="12" y2="2"></line><line x1="4.22" y1="10.22" x2="5.64" y2="11.64"></line><line x1="1" y1="18" x2="3" y2="18"></line><line x1="21" y1="18" x2="23" y2="18"></line><line x1="18.36" y1="11.64" x2="19.78" y2="10.22"></line><line x1="23" y1="22" x2="1" y2="22"></line><polyline points="16 5 12 9 8 5"></polyline>',tablet:'<rect x="4" y="2" width="16" height="20" rx="2" ry="2"></rect><line x1="12" y1="18" x2="12.01" y2="18"></line>',tag:'<path d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z"></path><line x1="7" y1="7" x2="7.01" y2="7"></line>',target:'<circle cx="12" cy="12" r="10"></circle><circle cx="12" cy="12" r="6"></circle><circle cx="12" cy="12" r="2"></circle>',terminal:'<polyline points="4 17 10 11 4 5"></polyline><line x1="12" y1="19" x2="20" y2="19"></line>',thermometer:'<path d="M14 14.76V3.5a2.5 2.5 0 0 0-5 0v11.26a4.5 4.5 0 1 0 5 0z"></path>',"thumbs-down":'<path d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17"></path>',"thumbs-up":'<path d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"></path>',"toggle-left":'<rect x="1" y="5" width="22" height="14" rx="7" ry="7"></rect><circle cx="8" cy="12" r="3"></circle>',"toggle-right":'<rect x="1" y="5" width="22" height="14" rx="7" ry="7"></rect><circle cx="16" cy="12" r="3"></circle>',tool:'<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"></path>',"trash-2":'<polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path><line x1="10" y1="11" x2="10" y2="17"></line><line x1="14" y1="11" x2="14" y2="17"></line>',trash:'<polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>',trello:'<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><rect x="7" y="7" width="3" height="9"></rect><rect x="14" y="7" width="3" height="5"></rect>',"trending-down":'<polyline points="23 18 13.5 8.5 8.5 13.5 1 6"></polyline><polyline points="17 18 23 18 23 12"></polyline>',"trending-up":'<polyline points="23 6 13.5 15.5 8.5 10.5 1 18"></polyline><polyline points="17 6 23 6 23 12"></polyline>',triangle:'<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path>',truck:'<rect x="1" y="3" width="15" height="13"></rect><polygon points="16 8 20 8 23 11 23 16 16 16 16 8"></polygon><circle cx="5.5" cy="18.5" r="2.5"></circle><circle cx="18.5" cy="18.5" r="2.5"></circle>',tv:'<rect x="2" y="7" width="20" height="15" rx="2" ry="2"></rect><polyline points="17 2 12 7 7 2"></polyline>',twitch:'<path d="M21 2H3v16h5v4l4-4h5l4-4V2zm-10 9V7m5 4V7"></path>',twitter:'<path d="M23 3a10.9 10.9 0 0 1-3.14 1.53 4.48 4.48 0 0 0-7.86 3v1A10.66 10.66 0 0 1 3 4s-4 9 5 13a11.64 11.64 0 0 1-7 2c9 5 20 0 20-11.5a4.5 4.5 0 0 0-.08-.83A7.72 7.72 0 0 0 23 3z"></path>',type:'<polyline points="4 7 4 4 20 4 20 7"></polyline><line x1="9" y1="20" x2="15" y2="20"></line><line x1="12" y1="4" x2="12" y2="20"></line>',umbrella:'<path d="M23 12a11.05 11.05 0 0 0-22 0zm-5 7a3 3 0 0 1-6 0v-7"></path>',underline:'<path d="M6 3v7a6 6 0 0 0 6 6 6 6 0 0 0 6-6V3"></path><line x1="4" y1="21" x2="20" y2="21"></line>',unlock:'<rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 9.9-1"></path>',"upload-cloud":'<polyline points="16 16 12 12 8 16"></polyline><line x1="12" y1="12" x2="12" y2="21"></line><path d="M20.39 18.39A5 5 0 0 0 18 9h-1.26A8 8 0 1 0 3 16.3"></path><polyline points="16 16 12 12 8 16"></polyline>',upload:'<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="17 8 12 3 7 8"></polyline><line x1="12" y1="3" x2="12" y2="15"></line>',"user-check":'<path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="8.5" cy="7" r="4"></circle><polyline points="17 11 19 13 23 9"></polyline>',"user-minus":'<path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="8.5" cy="7" r="4"></circle><line x1="23" y1="11" x2="17" y2="11"></line>',"user-plus":'<path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="8.5" cy="7" r="4"></circle><line x1="20" y1="8" x2="20" y2="14"></line><line x1="23" y1="11" x2="17" y2="11"></line>',"user-x":'<path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="8.5" cy="7" r="4"></circle><line x1="18" y1="8" x2="23" y2="13"></line><line x1="23" y1="8" x2="18" y2="13"></line>',user:'<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle>',users:'<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M23 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path>',"video-off":'<path d="M16 16v1a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h2m5.66 0H14a2 2 0 0 1 2 2v3.34l1 1L23 7v10"></path><line x1="1" y1="1" x2="23" y2="23"></line>',video:'<polygon points="23 7 16 12 23 17 23 7"></polygon><rect x="1" y="5" width="15" height="14" rx="2" ry="2"></rect>',voicemail:'<circle cx="5.5" cy="11.5" r="4.5"></circle><circle cx="18.5" cy="11.5" r="4.5"></circle><line x1="5.5" y1="16" x2="18.5" y2="16"></line>',"volume-1":'<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon><path d="M15.54 8.46a5 5 0 0 1 0 7.07"></path>',"volume-2":'<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon><path d="M19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07"></path>',"volume-x":'<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon><line x1="23" y1="9" x2="17" y2="15"></line><line x1="17" y1="9" x2="23" y2="15"></line>',volume:'<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon>',watch:'<circle cx="12" cy="12" r="7"></circle><polyline points="12 9 12 12 13.5 13.5"></polyline><path d="M16.51 17.35l-.35 3.83a2 2 0 0 1-2 1.82H9.83a2 2 0 0 1-2-1.82l-.35-3.83m.01-10.7l.35-3.83A2 2 0 0 1 9.83 1h4.35a2 2 0 0 1 2 1.82l.35 3.83"></path>',"wifi-off":'<line x1="1" y1="1" x2="23" y2="23"></line><path d="M16.72 11.06A10.94 10.94 0 0 1 19 12.55"></path><path d="M5 12.55a10.94 10.94 0 0 1 5.17-2.39"></path><path d="M10.71 5.05A16 16 0 0 1 22.58 9"></path><path d="M1.42 9a15.91 15.91 0 0 1 4.7-2.88"></path><path d="M8.53 16.11a6 6 0 0 1 6.95 0"></path><line x1="12" y1="20" x2="12.01" y2="20"></line>',wifi:'<path d="M5 12.55a11 11 0 0 1 14.08 0"></path><path d="M1.42 9a16 16 0 0 1 21.16 0"></path><path d="M8.53 16.11a6 6 0 0 1 6.95 0"></path><line x1="12" y1="20" x2="12.01" y2="20"></line>',wind:'<path d="M9.59 4.59A2 2 0 1 1 11 8H2m10.59 11.41A2 2 0 1 0 14 16H2m15.73-8.27A2.5 2.5 0 1 1 19.5 12H2"></path>',"x-circle":'<circle cx="12" cy="12" r="10"></circle><line x1="15" y1="9" x2="9" y2="15"></line><line x1="9" y1="9" x2="15" y2="15"></line>',"x-octagon":'<polygon points="7.86 2 16.14 2 22 7.86 22 16.14 16.14 22 7.86 22 2 16.14 2 7.86 7.86 2"></polygon><line x1="15" y1="9" x2="9" y2="15"></line><line x1="9" y1="9" x2="15" y2="15"></line>',"x-square":'<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="9" y1="9" x2="15" y2="15"></line><line x1="15" y1="9" x2="9" y2="15"></line>',x:'<line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line>',youtube:'<path d="M22.54 6.42a2.78 2.78 0 0 0-1.94-2C18.88 4 12 4 12 4s-6.88 0-8.6.46a2.78 2.78 0 0 0-1.94 2A29 29 0 0 0 1 11.75a29 29 0 0 0 .46 5.33A2.78 2.78 0 0 0 3.4 19c1.72.46 8.6.46 8.6.46s6.88 0 8.6-.46a2.78 2.78 0 0 0 1.94-2 29 29 0 0 0 .46-5.25 29 29 0 0 0-.46-5.33z"></path><polygon points="9.75 15.02 15.5 11.75 9.75 8.48 9.75 15.02"></polygon>',"zap-off":'<polyline points="12.41 6.75 13 2 10.57 4.92"></polyline><polyline points="18.57 12.91 21 10 15.66 10"></polyline><polyline points="8 8 3 14 12 14 11 22 16 16"></polyline><line x1="1" y1="1" x2="23" y2="23"></line>',zap:'<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon>',"zoom-in":'<circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line><line x1="11" y1="8" x2="11" y2="14"></line><line x1="8" y1="11" x2="14" y2="11"></line>',"zoom-out":'<circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line><line x1="8" y1="11" x2="14" y2="11"></line>'}},function(e){e.exports={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":2,"stroke-linecap":"round","stroke-linejoin":"round"}},function(e,n,i){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var t=Object.assign||function(e){for(var n=1;n<arguments.length;n++){var i=arguments[n];for(var t in i)Object.prototype.hasOwnProperty.call(i,t)&&(e[t]=i[t])}return e},l=function(){function e(e,n){for(var i=0;i<n.length;i++){var t=n[i];t.enumerable=t.enumerable||!1,t.configurable=!0,"value"in t&&(t.writable=!0),Object.defineProperty(e,t.key,t)}}return function(n,i,t){return i&&e(n.prototype,i),t&&e(n,t),n}}(),r=a(i(22)),o=a(i(42));function a(e){return e&&e.__esModule?e:{default:e}}var c=function(){function e(n,i){var l=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[];!function(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")}(this,e),this.name=n,this.contents=i,this.tags=l,this.attrs=t({},o.default,{class:"feather feather-"+n})}return l(e,[{key:"toSvg",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return"<svg "+function(e){return Object.keys(e).map(function(n){return n+'="'+e[n]+'"'}).join(" ")}(t({},this.attrs,e,{class:(0,r.default)(this.attrs.class,e.class)}))+">"+this.contents+"</svg>"}},{key:"toString",value:function(){return this.contents}}]),e}();n.default=c},function(e,n,i){"use strict";var t=o(i(12)),l=o(i(39)),r=o(i(38));function o(e){return e&&e.__esModule?e:{default:e}}e.exports={icons:t.default,toSvg:l.default,replace:r.default}},function(e,n,i){e.exports=i(0)},function(e,n,i){var t=i(2)("iterator"),l=!1;try{var r=0,o={next:function(){return{done:!!r++}},return:function(){l=!0}};o[t]=function(){return this},Array.from(o,function(){throw 2})}catch(e){}e.exports=function(e,n){if(!n&&!l)return!1;var i=!1;try{var r={};r[t]=function(){return{next:function(){return{done:i=!0}}}},e(r)}catch(e){}return i}},function(e,n,i){var t=i(30),l=i(2)("toStringTag"),r="Arguments"==t(function(){return arguments}());e.exports=function(e){var n,i,o;return void 0===e?"Undefined":null===e?"Null":"string"==typeof(i=function(e,n){try{return e[n]}catch(e){}}(n=Object(e),l))?i:r?t(n):"Object"==(o=t(n))&&"function"==typeof n.callee?"Arguments":o}},function(e,n,i){var t=i(47),l=i(9),r=i(2)("iterator");e.exports=function(e){if(void 0!=e)return e[r]||e["@@iterator"]||l[t(e)]}},function(e,n,i){"use strict";var t=i(18),l=i(7),r=i(10);e.exports=function(e,n,i){var o=t(n);o in e?l.f(e,o,r(0,i)):e[o]=i}},function(e,n,i){var t=i(2),l=i(9),r=t("iterator"),o=Array.prototype;e.exports=function(e){return void 0!==e&&(l.Array===e||o[r]===e)}},function(e,n,i){var t=i(3);e.exports=function(e,n,i,l){try{return l?n(t(i)[0],i[1]):n(i)}catch(n){var r=e.return;throw void 0!==r&&t(r.call(e)),n}}},function(e,n){e.exports=function(e){if("function"!=typeof e)throw TypeError(String(e)+" is not a function");return e}},function(e,n,i){var t=i(52);e.exports=function(e,n,i){if(t(e),void 0===n)return e;switch(i){case 0:return function(){return e.call(n)};case 1:return function(i){return e.call(n,i)};case 2:return function(i,t){return e.call(n,i,t)};case 3:return function(i,t,l){return e.call(n,i,t,l)}}return function(){return e.apply(n,arguments)}}},function(e,n,i){"use strict";var t=i(53),l=i(24),r=i(51),o=i(50),a=i(27),c=i(49),p=i(48);e.exports=function(e){var n,i,y,h,x=l(e),s="function"==typeof this?this:Array,u=arguments.length,d=u>1?arguments[1]:void 0,f=void 0!==d,g=0,v=p(x);if(f&&(d=t(d,u>2?arguments[2]:void 0,2)),void 0==v||s==Array&&o(v))for(i=new s(n=a(x.length));n>g;g++)c(i,g,f?d(x[g],g):x[g]);else for(h=v.call(x),i=new s;!(y=h.next()).done;g++)c(i,g,f?r(h,d,[y.value,g],!0):y.value);return i.length=g,i}},function(e,n,i){var t=i(32),l=i(54);t({target:"Array",stat:!0,forced:!i(46)(function(e){Array.from(e)})},{from:l})},function(e,n,i){var t=i(6),l=i(3);e.exports=function(e,n){if(l(e),!t(n)&&null!==n)throw TypeError("Can't set "+String(n)+" as a prototype")}},function(e,n,i){var t=i(56);e.exports=Object.setPrototypeOf||("__proto__"in{}?function(){var e,n=!1,i={};try{(e=Object.getOwnPropertyDescriptor(Object.prototype,"__proto__").set).call(i,[]),n=i instanceof Array}catch(e){}return function(i,l){return t(i,l),n?e.call(i,l):i.__proto__=l,i}}():void 0)},function(e,n,i){var t=i(0).document;e.exports=t&&t.documentElement},function(e,n,i){var t=i(28),l=i(13);e.exports=Object.keys||function(e){return t(e,l)}},function(e,n,i){var t=i(8),l=i(7),r=i(3),o=i(59);e.exports=t?Object.defineProperties:function(e,n){r(e);for(var i,t=o(n),a=t.length,c=0;a>c;)l.f(e,i=t[c++],n[i]);return e}},function(e,n,i){var t=i(3),l=i(60),r=i(13),o=i(15),a=i(58),c=i(34),p=i(16)("IE_PROTO"),y=function(){},h=function(){var e,n=c("iframe"),i=r.length;for(n.style.display="none",a.appendChild(n),n.src=String("javascript:"),(e=n.contentWindow.document).open(),e.write("<script>document.F=Object<\/script>"),e.close(),h=e.F;i--;)delete h.prototype[r[i]];return h()};e.exports=Object.create||function(e,n){var i;return null!==e?(y.prototype=t(e),i=new y,y.prototype=null,i[p]=e):i=h(),void 0===n?i:l(i,n)},o[p]=!0},function(e,n,i){var t=i(4);e.exports=!!Object.getOwnPropertySymbols&&!t(function(){return!String(Symbol())})},function(e,n,i){var t=i(4);e.exports=!t(function(){function e(){}return e.prototype.constructor=null,Object.getPrototypeOf(new e)!==e.prototype})},function(e,n,i){"use strict";var t=i(26).IteratorPrototype,l=i(61),r=i(10),o=i(23),a=i(9),c=function(){return this};e.exports=function(e,n,i){var p=n+" Iterator";return e.prototype=l(t,{next:r(1,i)}),o(e,p,!1,!0),a[p]=c,e}},function(e,n,i){var t=i(4),l=/#|\.prototype\./,r=function(e,n){var i=a[o(e)];return i==p||i!=c&&("function"==typeof n?t(n):!!n)},o=r.normalize=function(e){return String(e).replace(l,".").toLowerCase()},a=r.data={},c=r.NATIVE="N",p=r.POLYFILL="P";e.exports=r},function(e,n){n.f=Object.getOwnPropertySymbols},function(e,n,i){var t=i(21),l=Math.max,r=Math.min;e.exports=function(e,n){var i=t(e);return i<0?l(i+n,0):r(i,n)}},function(e,n,i){var t=i(14),l=i(27),r=i(67);e.exports=function(e){return function(n,i,o){var a,c=t(n),p=l(c.length),y=r(o,p);if(e&&i!=i){for(;p>y;)if((a=c[y++])!=a)return!0}else for(;p>y;y++)if((e||y in c)&&c[y]===i)return e||y||0;return!e&&-1}}},function(e,n,i){var t=i(28),l=i(13).concat("length","prototype");n.f=Object.getOwnPropertyNames||function(e){return t(e,l)}},function(e,n,i){var t=i(0),l=i(69),r=i(66),o=i(3),a=t.Reflect;e.exports=a&&a.ownKeys||function(e){var n=l.f(o(e)),i=r.f;return i?n.concat(i(e)):n}},function(e,n,i){var t=i(1),l=i(70),r=i(31),o=i(7);e.exports=function(e,n){for(var i=l(n),a=o.f,c=r.f,p=0;p<i.length;p++){var y=i[p];t(e,y)||a(e,y,c(n,y))}}},function(e,n,i){var t=i(4),l=i(30),r="".split;e.exports=t(function(){return!Object("z").propertyIsEnumerable(0)})?function(e){return"String"==l(e)?r.call(e,""):Object(e)}:Object},function(e,n,i){"use strict";var t={}.propertyIsEnumerable,l=Object.getOwnPropertyDescriptor,r=l&&!t.call({1:2},1);n.f=r?function(e){var n=l(this,e);return!!n&&n.enumerable}:t},function(e,n,i){"use strict";var t=i(32),l=i(64),r=i(25),o=i(57),a=i(23),c=i(5),p=i(29),y=i(2),h=i(17),x=i(9),s=i(26),u=s.IteratorPrototype,d=s.BUGGY_SAFARI_ITERATORS,f=y("iterator"),g=function(){return this};e.exports=function(e,n,i,y,s,v,m){l(i,n,y);var w,M,b,z=function(e){if(e===s&&O)return O;if(!d&&e in H)return H[e];switch(e){case"keys":case"values":case"entries":return function(){return new i(this,e)}}return function(){return new i(this)}},A=n+" Iterator",k=!1,H=e.prototype,V=H[f]||H["@@iterator"]||s&&H[s],O=!d&&V||z(s),j="Array"==n&&H.entries||V;if(j&&(w=r(j.call(new e)),u!==Object.prototype&&w.next&&(h||r(w)===u||(o?o(w,u):"function"!=typeof w[f]&&c(w,f,g)),a(w,A,!0,!0),h&&(x[A]=g))),"values"==s&&V&&"values"!==V.name&&(k=!0,O=function(){return V.call(this)}),h&&!m||H[f]===O||c(H,f,O),x[n]=O,s)if(M={values:z("values"),keys:v?O:z("keys"),entries:z("entries")},m)for(b in M)!d&&!k&&b in H||p(H,b,M[b]);else t({target:n,proto:!0,forced:d||k},M);return M}},function(e,n){var i;i=function(){return this}();try{i=i||Function("return this")()||(0,eval)("this")}catch(e){"object"==typeof window&&(i=window)}e.exports=i},function(e,n,i){var t=i(0),l=i(36),r=t.WeakMap;e.exports="function"==typeof r&&/native code/.test(l.call(r))},function(e,n,i){var t=i(21),l=i(20);e.exports=function(e,n,i){var r,o,a=String(l(e)),c=t(n),p=a.length;return c<0||c>=p?i?"":void 0:(r=a.charCodeAt(c))<55296||r>56319||c+1===p||(o=a.charCodeAt(c+1))<56320||o>57343?i?a.charAt(c):r:i?a.slice(c,c+2):o-56320+(r-55296<<10)+65536}},function(e,n,i){"use strict";var t=i(77),l=i(37),r=i(74),o=l.set,a=l.getterFor("String Iterator");r(String,"String",function(e){o(this,{type:"String Iterator",string:String(e),index:0})},function(){var e,n=a(this),i=n.string,l=n.index;return l>=i.length?{value:void 0,done:!0}:(e=t(i,l,!0),n.index+=e.length,{value:e,done:!1})})},function(e,n,i){i(78),i(55);var t=i(45);e.exports=t.Array.from},function(e,n,i){i(79),e.exports=i(44)}])});
  13 +//# sourceMappingURL=feather.min.js.map
0 14 \ No newline at end of file
... ...
app/static/lib/svg2any/FileSaver.min.js 0 → 100644
... ... @@ -0,0 +1,2 @@
  1 +/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */
  2 +var saveAs=saveAs||function(e){"use strict";if("undefined"==typeof navigator||!/MSIE [1-9]\./.test(navigator.userAgent)){var t=e.document,n=function(){return e.URL||e.webkitURL||e},o=t.createElementNS("http://www.w3.org/1999/xhtml","a"),r="download"in o,i=function(e){var t=new MouseEvent("click");e.dispatchEvent(t)},a=/Version\/[\d\.]+.*Safari/.test(navigator.userAgent),c=e.webkitRequestFileSystem,d=e.requestFileSystem||c||e.mozRequestFileSystem,u=function(t){(e.setImmediate||e.setTimeout)(function(){throw t},0)},s="application/octet-stream",f=0,l=4e4,v=function(e){var t=function(){"string"==typeof e?n().revokeObjectURL(e):e.remove()};setTimeout(t,l)},p=function(e,t,n){t=[].concat(t);for(var o=t.length;o--;){var r=e["on"+t[o]];if("function"==typeof r)try{r.call(e,n||e)}catch(i){u(i)}}},w=function(e){return/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(e.type)?new Blob(["\uFEFF",e],{type:e.type}):e},y=function(t,u,l){l||(t=w(t));var y,m,S,h=this,R=t.type,O=!1,g=function(){p(h,"writestart progress write writeend".split(" "))},b=function(){if(m&&a&&"undefined"!=typeof FileReader){var o=new FileReader;return o.onloadend=function(){var e=o.result;m.location.href="data:attachment/file"+e.slice(e.search(/[,;]/)),h.readyState=h.DONE,g()},o.readAsDataURL(t),void(h.readyState=h.INIT)}if((O||!y)&&(y=n().createObjectURL(t)),m)m.location.href=y;else{var r=e.open(y,"_blank");void 0===r&&a&&(e.location.href=y)}h.readyState=h.DONE,g(),v(y)},E=function(e){return function(){return h.readyState!==h.DONE?e.apply(this,arguments):void 0}},N={create:!0,exclusive:!1};return h.readyState=h.INIT,u||(u="download"),r?(y=n().createObjectURL(t),void setTimeout(function(){o.href=y,o.download=u,i(o),g(),v(y),h.readyState=h.DONE})):(e.chrome&&R&&R!==s&&(S=t.slice||t.webkitSlice,t=S.call(t,0,t.size,s),O=!0),c&&"download"!==u&&(u+=".download"),(R===s||c)&&(m=e),d?(f+=t.size,void d(e.TEMPORARY,f,E(function(e){e.root.getDirectory("saved",N,E(function(e){var n=function(){e.getFile(u,N,E(function(e){e.createWriter(E(function(n){n.onwriteend=function(t){m.location.href=e.toURL(),h.readyState=h.DONE,p(h,"writeend",t),v(e)},n.onerror=function(){var e=n.error;e.code!==e.ABORT_ERR&&b()},"writestart progress write abort".split(" ").forEach(function(e){n["on"+e]=h["on"+e]}),n.write(t),h.abort=function(){n.abort(),h.readyState=h.DONE},h.readyState=h.WRITING}),b)}),b)};e.getFile(u,{create:!1},E(function(e){e.remove(),n()}),E(function(e){e.code===e.NOT_FOUND_ERR?n():b()}))}),b)}),b)):void b())},m=y.prototype,S=function(e,t,n){return new y(e,t,n)};return"undefined"!=typeof navigator&&navigator.msSaveOrOpenBlob?function(e,t,n){return n||(e=w(e)),navigator.msSaveOrOpenBlob(e,t||"download")}:(m.abort=function(){var e=this;e.readyState=e.DONE,p(e,"abort")},m.readyState=m.INIT=0,m.WRITING=1,m.DONE=2,m.error=m.onwritestart=m.onprogress=m.onwrite=m.onabort=m.onerror=m.onwriteend=null,S)}}("undefined"!=typeof self&&self||"undefined"!=typeof window&&window||this.content);"undefined"!=typeof module&&module.exports?module.exports.saveAs=saveAs:"undefined"!=typeof define&&null!==define&&null!==define.amd&&define([],function(){return saveAs});
0 3 \ No newline at end of file
... ...
app/static/lib/svg2any/canvas-toBlob.js 0 → 100644
... ... @@ -0,0 +1,125 @@
  1 +/* canvas-toBlob.js
  2 + * A canvas.toBlob() implementation.
  3 + * 2016-05-26
  4 + *
  5 + * By Eli Grey, http://eligrey.com and Devin Samarin, https://github.com/eboyjr
  6 + * License: MIT
  7 + * See https://github.com/eligrey/canvas-toBlob.js/blob/master/LICENSE.md
  8 + */
  9 +
  10 +/*global self */
  11 +/*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true,
  12 + plusplus: true */
  13 +
  14 +/*! @source http://purl.eligrey.com/github/canvas-toBlob.js/blob/master/canvas-toBlob.js */
  15 +
  16 +(function(view) {
  17 +"use strict";
  18 +var
  19 + Uint8Array = view.Uint8Array
  20 + , HTMLCanvasElement = view.HTMLCanvasElement
  21 + , canvas_proto = HTMLCanvasElement && HTMLCanvasElement.prototype
  22 + , is_base64_regex = /\s*;\s*base64\s*(?:;|$)/i
  23 + , to_data_url = "toDataURL"
  24 + , base64_ranks
  25 + , decode_base64 = function(base64) {
  26 + var
  27 + len = base64.length
  28 + , buffer = new Uint8Array(len / 4 * 3 | 0)
  29 + , i = 0
  30 + , outptr = 0
  31 + , last = [0, 0]
  32 + , state = 0
  33 + , save = 0
  34 + , rank
  35 + , code
  36 + , undef
  37 + ;
  38 + while (len--) {
  39 + code = base64.charCodeAt(i++);
  40 + rank = base64_ranks[code-43];
  41 + if (rank !== 255 && rank !== undef) {
  42 + last[1] = last[0];
  43 + last[0] = code;
  44 + save = (save << 6) | rank;
  45 + state++;
  46 + if (state === 4) {
  47 + buffer[outptr++] = save >>> 16;
  48 + if (last[1] !== 61 /* padding character */) {
  49 + buffer[outptr++] = save >>> 8;
  50 + }
  51 + if (last[0] !== 61 /* padding character */) {
  52 + buffer[outptr++] = save;
  53 + }
  54 + state = 0;
  55 + }
  56 + }
  57 + }
  58 + // 2/3 chance there's going to be some null bytes at the end, but that
  59 + // doesn't really matter with most image formats.
  60 + // If it somehow matters for you, truncate the buffer up outptr.
  61 + return buffer;
  62 + }
  63 +;
  64 +if (Uint8Array) {
  65 + base64_ranks = new Uint8Array([
  66 + 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1
  67 + , -1, -1, 0, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
  68 + , 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25
  69 + , -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35
  70 + , 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
  71 + ]);
  72 +}
  73 +if (HTMLCanvasElement && (!canvas_proto.toBlob || !canvas_proto.toBlobHD)) {
  74 + if (!canvas_proto.toBlob)
  75 + canvas_proto.toBlob = function(callback, type /*, ...args*/) {
  76 + if (!type) {
  77 + type = "image/png";
  78 + } if (this.mozGetAsFile) {
  79 + callback(this.mozGetAsFile("canvas", type));
  80 + return;
  81 + } if (this.msToBlob && /^\s*image\/png\s*(?:$|;)/i.test(type)) {
  82 + callback(this.msToBlob());
  83 + return;
  84 + }
  85 +
  86 + var
  87 + args = Array.prototype.slice.call(arguments, 1)
  88 + , dataURI = this[to_data_url].apply(this, args)
  89 + , header_end = dataURI.indexOf(",")
  90 + , data = dataURI.substring(header_end + 1)
  91 + , is_base64 = is_base64_regex.test(dataURI.substring(0, header_end))
  92 + , blob
  93 + ;
  94 + if (Blob.fake) {
  95 + // no reason to decode a data: URI that's just going to become a data URI again
  96 + blob = new Blob
  97 + if (is_base64) {
  98 + blob.encoding = "base64";
  99 + } else {
  100 + blob.encoding = "URI";
  101 + }
  102 + blob.data = data;
  103 + blob.size = data.length;
  104 + } else if (Uint8Array) {
  105 + if (is_base64) {
  106 + blob = new Blob([decode_base64(data)], {type: type});
  107 + } else {
  108 + blob = new Blob([decodeURIComponent(data)], {type: type});
  109 + }
  110 + }
  111 + callback(blob);
  112 + };
  113 +
  114 + if (!canvas_proto.toBlobHD && canvas_proto.toDataURLHD) {
  115 + canvas_proto.toBlobHD = function() {
  116 + to_data_url = "toDataURLHD";
  117 + var blob = this.toBlob();
  118 + to_data_url = "toDataURL";
  119 + return blob;
  120 + }
  121 + } else {
  122 + canvas_proto.toBlobHD = canvas_proto.toBlob;
  123 + }
  124 +}
  125 +}(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content || this));
... ...
app/templates/base_page.html
... ... @@ -40,17 +40,18 @@
40 40 <div class="sidebar-sticky collapse navbar-collapse" id="navbarSupportedContent">
41 41 <ul class="nav flex-column" id="v_menu">
42 42 <li class="nav-item">
43   - <a aria-expanded="false" id="agents" class="main-nav nav-link" data-target="#agent" data-toggle="collapse"
  43 + <a aria-expanded="false" class="main-nav nav-link" data-target="#agent" data-toggle="collapse"
44 44 href="#">
45 45 <span data-feather="user"></span>
46 46 Agent
47 47 </a>
48 48 <ul aria-expanded="false" class="collapse" class="nav" data-parent="#v_menu" id="agent">
49   - <li class="nav-item"><a id="liste_agents" class="sub_link nav-link "
  49 + <li class="nav-item"><a id="agents_list" class="sub_link nav-link "
50 50 href="{{ url_for('main.agents') }}">Liste des
51 51 agents</a>
52 52 </li>
53   - <li class="nav-item"><a class="sub_link nav-link disabled" href="#">Statistiques</a></li>
  53 + <li class="nav-item"><a id="agents_stats" class="sub_link nav-link"
  54 + href="{{ url_for('main.agents_stats') }}">Statistiques</a></li>
54 55 <li class="nav-item"><a class="sub_link nav-link disabled" href="#">Liste des responsabilités</a></li>
55 56 <li class="nav-item"><a id="capacities" class="sub_link nav-link "
56 57 href="{{ url_for('main.capacities') }}">Liste des
... ... @@ -67,12 +68,11 @@
67 68 Projet
68 69 </a>
69 70 <ul aria-expanded="false" class="collapse" data-parent="#v_menu" id="project">
70   - <li class="nav-item"><a id="projects" class="sub_link nav-link " href="{{ url_for('main.projects') }}">Liste
71   - des
72   - projets</a></li>
73   - <li class="nav-item"><a id="categories" class="sub_link nav-link"
  71 + <li class="nav-item"><a id="projects_list" class="sub_link nav-link "
  72 + href="{{ url_for('main.projects') }}">Liste des projets</a></li>
  73 + <li class="nav-item"><a id="categories_list" class="sub_link nav-link"
74 74 href="{{ url_for('main.categories') }}">Liste des catégories</a></li>
75   - <li class="nav-item"><a id="labels" class="sub_link nav-link" href="{{ url_for('main.labels') }}">Liste
  75 + <li class="nav-item"><a id="labels_list" class="sub_link nav-link" href="{{ url_for('main.labels') }}">Liste
76 76 des labels</a></li>
77 77 <li class="nav-item"><a class="sub_link nav-link disabled" href="#">Listes des statuts de projets</a>
78 78 <li class="nav-item"><a id="projects_stats" class="sub_link nav-link"
... ... @@ -86,7 +86,7 @@
86 86 Service
87 87 </a>
88 88 <ul aria-expanded="false" class="collapse" data-parent="#v_menu" id="service">
89   - <li class="nav-item"><a id="liste_services" class="sub_link nav-link "
  89 + <li class="nav-item"><a id="services_list" class="sub_link nav-link "
90 90 href="{{ url_for('main.services') }}">Liste des
91 91 services</a>
92 92 </li>
... ... @@ -99,13 +99,13 @@
99 99 Chef de service
100 100 </a>
101 101 <ul aria-expanded="false" class="collapse" data-parent="#v_menu" id="cds">
102   - <li class="nav-item"><a id="edit_agent" class="sub_link nav-link "
  102 + <li class="nav-item"><a id="agent_edit" class="sub_link nav-link "
103 103 href="{{ url_for('main.agent_edit') }}">Ajouter/Modifier un
104 104 Agent</a></li>
105 105 <li class="nav-item"><a class="sub_link nav-link disabled" href="#">Renseigner le statut d'un agent pour
106 106 un
107 107 semestre</a></li>
108   - <li class="nav-item"><a id="add_charge" class="sub_link nav-link "
  108 + <li class="nav-item"><a id="charge_add" class="sub_link nav-link "
109 109 href="{{ url_for('main.charge_add') }}">Affecter un
110 110 agent à un
111 111 projet/service</a></li>
... ... @@ -125,20 +125,25 @@
125 125 <li class="nav-item"><a id="project_edit" class="sub_link nav-link"
126 126 href="{{ url_for('main.project_edit') }}">Ajouter/Modifier un projet</a></li>
127 127 <li class="nav-item"><a id="category_edit" class="sub_link nav-link"
128   - href="{{ url_for('main.category_edit') }}">Ajouter/Modifier une catégorie</a></li>
  128 + href="{{ url_for('main.category_edit') }}">Ajouter/Modifier une catégorie</a>
  129 + </li>
129 130 <li class="nav-item"><a id="label_edit" class="sub_link nav-link"
130 131 href="{{ url_for('main.label_edit') }}">Ajouter/Modifier un label</a></li>
131   - <li class="nav-item"><a class="sub_link nav-link disabled" href="#">Ajouter/Modifier une compétence</a></li>
132   - <li class="nav-item"><a class="sub_link nav-link disabled" href="#">Ajouter/Modifier une fonction</a></li>
133   - <li class="nav-item"><a class="sub_link nav-link disabled" href="#">Ajouter/Modifier une responsabilité</a></li>
  132 + <li class="nav-item"><a class="sub_link nav-link disabled" href="#">Ajouter/Modifier une compétence</a>
  133 + </li>
  134 + <li class="nav-item"><a class="sub_link nav-link disabled" href="#">Ajouter/Modifier une fonction</a>
  135 + </li>
  136 + <li class="nav-item"><a class="sub_link nav-link disabled" href="#">Ajouter/Modifier une
  137 + responsabilité</a></li>
134 138 <li class="nav-item"><a class="sub_link nav-link disabled" href="#">Affecter un responsable de
135 139 projet</a></li>
136 140 <li class="nav-item"><a class="sub_link nav-link disabled" href="#">Affecter un responsable de
137 141 service</a></li>
138   - <li class="nav-item"><a id="liste_periodes" class="sub_link nav-link "
  142 + <li class="nav-item"><a id="periods_list" class="sub_link nav-link "
139 143 href="{{ url_for('main.periods') }}">Liste des
140 144 périodes</a></li>
141   - <li class="nav-item"><a class="sub_link nav-link disabled" href="#">Ajouter/Modifier une période</a></li>
  145 + <li class="nav-item"><a class="sub_link nav-link disabled" href="#">Ajouter/Modifier une période</a>
  146 + </li>
142 147  
143 148 </ul>
144 149 </li>
... ... @@ -164,9 +169,9 @@
164 169 {% include 'datatables-includes.html' %}
165 170  
166 171 <!-- Icons -->
167   - <script src="https://unpkg.com/feather-icons/dist/feather.min.js"></script>
  172 + <script src="{{ url_for('static', filename='lib/feather-4.28.0/feather.min.js') }}"></script>
168 173 <script>
169   - feather.replace()
  174 + feather.replace();
170 175 </script>
171 176  
172 177 <script type="text/javascript"
... ...
app/templates/charges-includes.html
1 1 <script
2   - src="https://cdn.rawgit.com/eligrey/canvas-toBlob.js/f1a01896135ab378aa5c0118eadd81da55e698d8/canvas-toBlob.js"></script>
  2 + src="{{ url_for('static', filename='lib/svg2any/canvas-toBlob.js') }}"></script>
3 3 <script
4   - src="https://cdn.rawgit.com/eligrey/FileSaver.js/e9d941381475b5df8b7d7691013401e171014e89/FileSaver.min.js"></script>
  4 + src="{{ url_for('static', filename='lib/svg2any/FileSaver.min.js') }}"></script>
5 5 <script src="{{ url_for('main.static', filename='js/svg_to_any.js', version=config.VERSION) }}"
6 6 type="text/javascript"></script>
7 7 <script src="{{ url_for('main.static', filename='js/charges.js', version=config.VERSION) }}"
... ...
app/templates/datatables-includes.html
... ... @@ -5,7 +5,8 @@
5 5 src="{{ url_for('static', filename='lib/datatables-1.10.24/js/dataTables.buttons.min.js') }}"></script>
6 6 <script type="text/javascript"
7 7 src="{{ url_for('static', filename='lib/datatables-1.10.24/js/buttons.html5.min.js') }}"></script>
8   -<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.3/jszip.min.js"></script>
  8 +<script type="text/javascript"
  9 + src="{{ url_for('static', filename='lib/datatables-1.10.24/js/jszip.min.js') }}"></script>
9 10  
10 11 <script src="{{ url_for('main.static', filename='js/style_tables.js', version=config.VERSION) }}"
11 12 type="text/javascript"></script>
... ...
resources/lesia-btp.sqlite
No preview for this file type
tests/backend_tests.py
... ... @@ -86,3 +86,7 @@ class DbMgrTestCase(BaseTestCase):
86 86 categories = [_cl['name'] for _cl in category_labels]
87 87 self.assertEqual(['Domaine', 'Pôle'], categories)
88 88  
  89 + def test_agents_status_counts(self):
  90 + status_counts = db_mgr.count_agents_by_status(3)
  91 + print(status_counts)
  92 + self.assertEqual(14, len(status_counts))
... ...