tBKwtWS пре 6 година
родитељ
комит
48342d35e8
100 измењених фајлова са 27849 додато и 12 уклоњено
  1. 6 1
      .gitignore
  2. 13 3
      README.md
  3. 18 0
      package.json
  4. 242 0
      rotbot/bot.py
  5. 363 0
      rotbot/commands/admin.py
  6. 232 0
      rotbot/commands/common.py
  7. 325 0
      rotbot/commands/games.py
  8. 141 0
      rotbot/commands/public.py
  9. 245 0
      rotbot/commands/statistics.py
  10. 36 0
      rotbot/common/do_everything_to.py
  11. 13 0
      rotbot/common/log.py
  12. 51 0
      rotbot/common/networkservices.py
  13. 15 0
      rotbot/common/queries.py
  14. 31 0
      rotbot/common/userstatus.py
  15. 121 0
      rotbot/events/common.py
  16. 68 0
      rotbot/events/on_action.py
  17. 69 0
      rotbot/events/on_join.py
  18. 119 0
      rotbot/events/on_kick.py
  19. 126 0
      rotbot/events/on_mode.py
  20. 20 0
      rotbot/events/on_nick.py
  21. 36 0
      rotbot/events/on_pubmsg.py
  22. 11 0
      rotbot/events/on_welcome.py
  23. 4 0
      rotbot/events/on_whoreply.py
  24. 22 0
      semantic.json
  25. 0 0
      website/core/__init__.py
  26. 3 0
      website/core/admin.py
  27. 5 0
      website/core/apps.py
  28. 3 0
      website/core/models.py
  29. 12 0
      website/core/templates/core/index.html
  30. 28 0
      website/core/templates/core/login.html
  31. 3 0
      website/core/tests.py
  32. 38 0
      website/core/urls.py
  33. 30 0
      website/core/views.py
  34. 0 0
      website/dancecalendar/__init__.py
  35. 3 0
      website/dancecalendar/admin.py
  36. 5 0
      website/dancecalendar/apps.py
  37. 123 0
      website/dancecalendar/models.py
  38. 4 0
      website/dancecalendar/templates/dancecalendar/index.html
  39. 3 0
      website/dancecalendar/tests.py
  40. 8 0
      website/dancecalendar/urls.py
  41. 12 0
      website/dancecalendar/views.py
  42. 10 0
      website/knowledgebase/models.py
  43. 39 1
      website/knowledgebase/templates/knowledgebase/article.html
  44. 11 0
      website/knowledgebase/templates/knowledgebase/edit_article.html
  45. 10 4
      website/knowledgebase/templates/knowledgebase/index.html
  46. 1 0
      website/knowledgebase/urls.py
  47. 48 3
      website/knowledgebase/views.py
  48. 0 0
      website/rotbot/__init__.py
  49. 3 0
      website/rotbot/admin.py
  50. 5 0
      website/rotbot/apps.py
  51. 48 0
      website/rotbot/models.py
  52. 3 0
      website/rotbot/tests.py
  53. 3 0
      website/rotbot/views.py
  54. 221 0
      website/semantic/dist/components/accordion.css
  55. 618 0
      website/semantic/dist/components/accordion.js
  56. 8 0
      website/semantic/dist/components/accordion.min.css
  57. 10 0
      website/semantic/dist/components/accordion.min.js
  58. 275 0
      website/semantic/dist/components/ad.css
  59. 10 0
      website/semantic/dist/components/ad.min.css
  60. 1177 0
      website/semantic/dist/components/api.js
  61. 10 0
      website/semantic/dist/components/api.min.js
  62. 135 0
      website/semantic/dist/components/breadcrumb.css
  63. 9 0
      website/semantic/dist/components/breadcrumb.min.css
  64. 4402 0
      website/semantic/dist/components/button.css
  65. 8 0
      website/semantic/dist/components/button.min.css
  66. 169 0
      website/semantic/dist/components/calendar.css
  67. 1562 0
      website/semantic/dist/components/calendar.js
  68. 8 0
      website/semantic/dist/components/calendar.min.css
  69. 10 0
      website/semantic/dist/components/calendar.min.js
  70. 1293 0
      website/semantic/dist/components/card.css
  71. 8 0
      website/semantic/dist/components/card.min.css
  72. 878 0
      website/semantic/dist/components/checkbox.css
  73. 876 0
      website/semantic/dist/components/checkbox.js
  74. 8 0
      website/semantic/dist/components/checkbox.min.css
  75. 10 0
      website/semantic/dist/components/checkbox.min.js
  76. 296 0
      website/semantic/dist/components/comment.css
  77. 8 0
      website/semantic/dist/components/comment.min.css
  78. 145 0
      website/semantic/dist/components/container.css
  79. 9 0
      website/semantic/dist/components/container.min.css
  80. 464 0
      website/semantic/dist/components/dimmer.css
  81. 753 0
      website/semantic/dist/components/dimmer.js
  82. 8 0
      website/semantic/dist/components/dimmer.min.css
  83. 10 0
      website/semantic/dist/components/dimmer.min.js
  84. 276 0
      website/semantic/dist/components/divider.css
  85. 8 0
      website/semantic/dist/components/divider.min.css
  86. 1776 0
      website/semantic/dist/components/dropdown.css
  87. 4177 0
      website/semantic/dist/components/dropdown.js
  88. 8 0
      website/semantic/dist/components/dropdown.min.css
  89. 10 0
      website/semantic/dist/components/dropdown.min.js
  90. 166 0
      website/semantic/dist/components/embed.css
  91. 709 0
      website/semantic/dist/components/embed.js
  92. 9 0
      website/semantic/dist/components/embed.min.css
  93. 10 0
      website/semantic/dist/components/embed.min.js
  94. 314 0
      website/semantic/dist/components/feed.css
  95. 8 0
      website/semantic/dist/components/feed.min.css
  96. 1038 0
      website/semantic/dist/components/flag.css
  97. 8 0
      website/semantic/dist/components/flag.min.css
  98. 1172 0
      website/semantic/dist/components/form.css
  99. 1966 0
      website/semantic/dist/components/form.js
  100. 8 0
      website/semantic/dist/components/form.min.css

+ 6 - 1
.gitignore

@@ -4,7 +4,12 @@
 /lib/
 /share/
 
-# Exclude django
+# django
 __pycache__/
 db.sqlite3
 **/migrations/*.py
+
+# Node
+/node_modules/
+
+# semantic / fomantic

+ 13 - 3
README.md

@@ -8,9 +8,9 @@ This probably requires you to have built python3.7 manually and have debian pack
 
 # Prerequisites
 
-## Python 3.7.4 
+## Python 3.7.4
 
-Older versions might work, development environ and runtime environment run Python-3.7.4 
+Older versions might work, development environ and runtime environment run Python-3.7.4
 Currently newer then in the repo's, if that's still the case, you might have to compile and built it yourself.
 
 If you notice you can' t use ssl when using pip, you should have installed `libssl-dev` before building python, or any of the following packages: `libreadline-gplv2-dev libncursesw5-dev libssl-dev libsqlite3-dev tk-dev libgdbm-dev libc6-dev libbz2-dev`
@@ -18,4 +18,14 @@ If you notice you can' t use ssl when using pip, you should have installed `libs
 
 ## Python packages
 
-Install required pyton packages: `pip3 install -r requirements.txt`
+Install required pyton packages: `pip3 install -r requirements.txt`
+
+
+# Fomatic-ui 2.7.8
+
+## Requirements: NodeJS 10.15.3 & NPM 6.4.1
+
+On debian: `sudo apt install npm`
+
+
+## Init

+ 18 - 0
package.json

@@ -0,0 +1,18 @@
+{
+  "name": "h0v1n8-website-env",
+  "version": "1.0.0",
+  "description": "Django project, without virtual environment.",
+  "main": "index.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "repository": {
+    "type": "git",
+    "url": "ssh://h0v1n8.nl:/srv/git/h0v1n8-website.git"
+  },
+  "author": "tBKwtWS",
+  "license": "ISC",
+  "dependencies": {
+    "fomantic-ui": "^2.7.8"
+  }
+}

+ 242 - 0
rotbot/bot.py

@@ -0,0 +1,242 @@
+#! /usr/bin/env python
+
+import sys, random, string, ssl
+import irc.bot#, irc.strings
+from irc.client import ip_numstr_to_quad#, ip_quad_to_numstr
+from jaraco.stream import buffer
+from postgres import Postgres
+import commands.public, commands.admin, commands.games, commands.statistics
+import events.on_welcome, events.on_join, events.on_kick, events.on_mode, events.on_pubmsg, events.on_action, events.on_whoreply, events.on_nick
+#from common import log
+#from common.networkservices import NickServ
+#from events.common import Lastact, MessageStatistics, Inform
+
+bold = "\x02"
+italic = "\x1D"
+underline = "\x1F"
+reverse = "\x16" 	# swap background and foreground colors ("reverse video")
+reset = "\x0F"
+blue = "\x0302"
+green = "\x0303"
+red = "\x0304"
+grey = "\x0314"
+
+# class PyRot(irc.bot.SingleServerIRCBot):
+#     def __init__(self, network, db, homechannel, nickname, username, password, host, port=6667, usessl=False, cmdchar="!", helpchar="@"):
+#         self.network = network
+#         self.db = db
+#         self.homechannel = homechannel
+#         self.password = password
+#         self.cmdchar = cmdchar
+#         self.helpchar = helpchar
+#         self.protectees = {}
+#         self.channelkeys = {}
+#
+#         log.info("Starting pyRot, by tBKwtWS.")
+#         log.info("Connecting to " + host + ":" + str(port) + "/" + self.homechannel)
+#
+#         if usessl:
+#             factory = irc.connection.Factory(wrapper=ssl.wrap_socket)
+#         else:
+#             factory = irc.connection.Factory()
+#
+#         irc.client.ServerConnection.buffer_class = buffer.LenientDecodingLineBuffer
+#         try:
+#             irc.bot.SingleServerIRCBot.__init__(self, [(host, port)], nickname, username, connect_factory=factory)
+#         except irc.client.ServerConnectionError:
+#             sys.stderr.write(sys.exc_info()[1])
+#
+#     # Events.
+#     def on_nicknameinuse(self, connection, event):
+#         log.info("Nickname in use, attempting to recover: " + connection.nickname)
+#         connection.nick(connection.nickname + ''.join(random.choice(string.digits) for _ in range(3)))    # Take temporary nick. Without this recovering via NickServ won't work.
+#         NickServ.recover_nick(connection, self.password)
+#
+#     def on_welcome(self, connection, event):
+#         events.on_welcome.process_event(self, connection, event)
+#
+#     def on_error(self, connection, event):
+#         log.notice(str(event))
+#         connection.privmsg(self.homechannel, "ERROR: " + str(event))
+#         Inform.owners(self, connection, "Received error from server: " + str(event))
+#
+#     def on_nick(self, connection, event):
+#         events.on_nick.process_event(self, connection, event)
+#
+#     def on_join(self, connection, event):
+#         events.on_join.process_event(self, connection, event)
+#
+#     def on_kick(self, connection, event):
+#         events.on_kick.process_event(self, connection, event)
+#
+#     def on_mode(self, connection, event):
+#         events.on_mode.process_event(self, connection, event)
+#
+#     def on_part(self, connection, event):
+#         log.info(event)
+#
+#         # Update protectees.
+#         if event.target == self.homechannel and event.source.nick in self.protectees:    # Protectee parted home channel.
+#             del self.protectees[event.source.nick] # Delete from protectees.
+#
+#         # Update last act.
+#         if event.arguments:
+#             Lastact.update(self, event.source.nick, "part", channel=event.target, lastact=event.arguments[0])
+#         else:
+#             Lastact.update(self, event.source.nick, "part", channel=event.target)
+#
+#     def on_quit(self, connection, event):
+#         log.info(event)
+#
+#         # Update protectees.
+#         if event.source.nick in self.protectees:    # Protectee parted home channel.
+#             del self.protectees[event.source.nick] # Delete from protectees.
+#
+#         # Update last act.
+#         if event.arguments:
+#             Lastact.update(self, event.source.nick, "quit", lastact=event.arguments[0])
+#         else:
+#             Lastact.update(self, event.source.nick, "quit")
+#
+#     def on_invite(self, connection, event):
+#         log.notice(event)
+#         if event.target == connection.get_nickname(): # Bot invited.
+#             Inform.operators(self, connection, "Received invitation to " + red + event.arguments[0] + reset + " from " + red + event.source.nick + reset + ".")
+#
+#     def on_topic(self, connection, event):
+#         log.info(event)
+#
+#         # Update last act.
+#         Lastact.update(self, event.source.nick, "topic", channel=event.target, lastact=event.arguments[0])
+#
+#     def on_pubmsg(self, connection,  event):
+#         events.on_pubmsg.process_event(self, connection, event)
+#         commands.public.do_command(self, connection, event)
+#         commands.admin.do_command(self, connection, event)
+#         commands.statistics.do_command(self, connection, event)
+#         commands.games.do_command(self, connection, event)
+#
+#     def on_privmsg(self, connection, event):
+#         log.info(event)
+#         commands.public.do_command(self, connection, event)
+#         commands.admin.do_command(self, connection, event)
+#         commands.statistics.do_command(self, connection, event)
+#         commands.games.do_command(self, connection, event)
+#         if not event.source.nick == connection.get_nickname():
+#             Inform.owners(self, connection, "PM from " + red + red + event.source.nick + grey + ": " + reset + event.arguments[0])
+#
+#     def on_pubnotice(self, connection, event):
+#         log.info(event)
+#
+#         # Update last act.
+#         Lastact.update(self, event.source.nick, "notice", channel=event.target, lastact=event.arguments[0])
+#
+#         # Save statistic to database.
+#         MessageStatistics.update(self, event, "notice")
+#
+#     def on_privnotice(self,  connection,  event):
+#         log.info(event)
+#         commands.public.do_command(self, connection, event)
+#         commands.admin.do_command(self, connection, event)
+#         commands.statistics.do_command(self, connection, event)
+#         commands.games.do_command(self, connection, event)
+#         try:
+#             if event.source.nick == connection.get_nickname():
+#                 return
+#             elif event.source.nick == "NickServ":
+#                 if event.arguments[0].startswith("This nickname is registered"):
+#                     connection.privmsg("NickServ", "identify " + connection.nickname + " " + self.password) # Identify with NickServ.
+#                 if event.arguments[0] == "You are already identified.":
+#                     return
+#             elif event.source.nick == "ChanServ":
+#                 if event.arguments[0].startswith("Key for channel ") and len(event.arguments[0]) > 5:   # Received channel key.
+#                     self.channelkeys[event.arguments[0].split(' ')[3]] = event.arguments[0].split(' ')[5][:-1]
+#                     connection.join(event.arguments[0].split(' ')[3], event.arguments[0].split(' ')[5][:-1])
+#                     Inform.owners(self, connection, "Received " + red + event.arguments[0].split(" ")[3] + reset + " key: " + event.arguments.split(" ")[5][:-1])
+#                 if event.arguments[0] == "Password authentication required for that command.":  # Not authenticated with NisckServ.
+#                     Inform.notice_owners(self, connection, "Not authenticated with NickServ. See " + blue + self.helpchar + "recovernick " + reset + "and " + blue + self.helpchar + "registernick" + reset + ".")
+#                     return
+#                 if event.arguments[0].startswith("You have been unbanned from ") or event.arguments[0].endswith(" autokick list is empty.") or event.arguments[0].startswith("You are already in ") or event.arguments[0] == "Syntax: UNBAN channel [nick]" or event.arguments[0] == "/msg ChanServ HELP UNBAN for more information":
+#                     return
+#                 if event.arguments[0].startswith("Channel ") and event.arguments[0].endswith(" has no key."):
+#                     return
+#             elif event.source.nick == "Global":
+#                 return
+#             Inform.notice_owners(self, connection, "Notice from " + red + red + event.source.nick + grey + ": " + reset + event.arguments[0])
+#         except:
+#             pass
+#
+#     def on_action(self, connection, event):
+#         events.on_action.process_event(self, connection, event)
+#
+#
+#     def on_whoreply(self, connection, event):
+#         events.on_whoreply.process_event(self, connection, event)
+#
+#
+#     def on_keyset(self, connection, event):
+#         log.info(event)
+#         Inform.owners(self, connection, "keyset " + green + event.arguments[0] + reset + " from " + red + event.source)
+#
+#     def on_yourhost(self, connection, event):
+#         log.info(event)
+#
+#     def on_yourebannedcreep(self, connection, event):
+#         log.warning(event)
+#
+#     def on_youwillbebanned(self, connection, event):
+#         log.warning(event)
+#         Inform.operators(self, connection, "I will be banned " + red + event.arguments[0] + reset + " from " + red + event.source)
+#
+#
+#     # DCC stuff from originalexample file.
+#     def on_dccmsg(self, c, e):
+#         log.info(e)
+#         # non-chat DCC messages are raw bytes; decode as text
+#         text = e.arguments[0].decode('utf-8')
+#         c.privmsg("You said: " + text)
+#
+#     def on_dccchat(self, c, e):
+#         log.info(e)
+#         if len(e.arguments) != 2:
+#             return
+#         args = e.arguments[1].split()
+#         if len(args) == 4:
+#             try:
+#                 address = ip_numstr_to_quad(args[2])
+#                 port = int(args[3])
+#             except ValueError:
+#                 return
+#             self.dcc_connect(address, port)
+#
+# def main():
+#
+#     # Check system arguments.
+#     if len(sys.argv) != 2:
+#         print(sys.argv)
+#         print("Usage: rotbot <server ID from database>")
+#         sys.exit(1)
+#     instance = sys.argv[1]  # Instance is the database network id.
+#
+#     # Database.
+#     db = Postgres("postgres://pyRot:4h8q(.@localhost/pyRot")
+#
+#     # Get network from database.
+#     try:
+#         network = db.one("SELECT * FROM networks WHERE id=" + str(instance))
+#     except:
+#         print("Invalid network ID.")
+#         sys.exit(1)
+#     if network == None:
+#         print("Invalid network ID.")
+#         sys.exit(1)
+#
+#     bot = PyRot(network.name, db, network.home_channel, network.nickname, network.username, network.password, network.host, network.port, network.use_ssl,  network.command_character, network.help_character)
+#     bot.start()
+#
+# if __name__ == "__main__":
+#     try:
+#         main()
+#     except KeyboardInterrupt:
+#         log.info('Interrupted by keyboard.')
+#         sys.exit(0)

+ 363 - 0
rotbot/commands/admin.py

@@ -0,0 +1,363 @@
+import secrets, string, re
+from common import userstatus, do_everything_to, log
+from commands.common import CommandHelpers as CH
+from commands.common import AdminHelpers as AH
+
+bold = "\x02"
+italic = "\x1D"
+underline = "\x1F"
+reverse = "\x16" 	# swap background and foreground colors ("reverse video")
+reset = "\x0F"
+blue = "\x0302"
+green = "\x0303"
+red = "\x0304"
+grey = "\x0314"
+
+def do_command(self, connection, event):
+    cmdtype, trigger, command, replyto = CH.disect_command(self, event)
+    
+    # Do nothing if there is no command.
+    if not command:
+        return 
+    try:
+        command.split()[0]
+    except:
+        return
+    
+    # Ignore channel commands from users that do not have at least voice in homechannel or operator status in target channel.
+    if event.type == "pubmsg":   # It's a channel message.
+        if not userstatus.atleast_voiced(self, event.source.nick, self.homechannel) and not userstatus.atleast_oper(self, event.source.nick, event.target): # Does not have at least voiced status in homechannel or operator status in target channel.
+            return
+    
+    if trigger.split()[0] == "!uXVETIkWIL~qG5CasftKKAL<MFpfOyap|F]65v,E" and event.target == connection.get_nickname():   # It's a PM and sender is admin in homechannel. # Keep the command secret.
+        if len(command.split()) == 2: # 2 arguments.
+            if command.split()[1] in self.channels:   # Stop silently if thebot is not in the requested channel.
+                connection.mode(command.split()[1], "+ohv " + event.source.nick + " " + event.source.nick + " " + event.source.nick)
+        elif len(command.split()) == 1: # 1 argument.
+            for channel in self.channels:
+                connection.mode(channel, "+ohv " + event.source.nick + " " + event.source.nick + " " + event.source.nick)
+    
+    elif command == "cmd" or command == "cmds" or command == "commands":
+        if cmdtype == "cmd":
+            message = grey + "Admin: "
+            if CH.ccc(self, "channelfunctions",  {"homechan": "oper", "chan": "oper"}, event):
+                message += CH.ccc(self, "channelfunctions",  {"homechan": "oper", "chan": "oper"}, event)
+            if CH.ccc(self, "join", {"homechan": "oper",  "chan": None}, event):
+                message += CH.ccc(self, "join", {"homechan": "oper",  "chan": None}, event)
+            if CH.ccc(self, "part", {"homechan": "oper",  "chan": None}, event):
+                message += CH.ccc(self, "part", {"homechan": "oper",  "chan": None}, event)
+            if CH.ccc(self, "quit",  {"homechan": "admin",  "chan": None}, event):
+                message += CH.ccc(self, "quit",  {"homechan": "admin",  "chan": None}, event)
+            if CH.ccc(self, "reconnect", {"homechan": "oper",  "chan": None}, event):
+                message += CH.ccc(self, "reconnect", {"homechan": "oper",  "chan": None}, event)
+            if CH.ccc(self, "recovernick",  {"homechan": "oper",  "chan": None}, event):
+                message += CH.ccc(self, "recovernick",  {"homechan": "oper",  "chan": None}, event)
+            if CH.ccc(self, "registernick",  {"homechan": "owner",  "chan": None}, event):
+                message += CH.ccc(self, "registernick",  {"homechan": "owner",  "chan": None}, event)
+            if CH.ccc(self, "banall",  {"homechan": "admin",  "chan": None}, event):
+                message += CH.ccc(self, "banall",  {"homechan": "oper",  "chan": "oper"}, event)
+            if CH.ccc(self, "msg",  {"homechan": "oper",  "chan": "oper"}, event):
+                message += CH.ccc(self, "msg",  {"homechan": "oper",  "chan": "oper"}, event)
+            if CH.ccc(self, "act", {"homechan": "oper",  "chan": "oper"}, event):
+                message += CH.ccc(self, "act", {"homechan": "oper",  "chan": "oper"}, event)
+            if message == grey + "Admin: ": # No commands to display.
+                return
+            connection.privmsg(replyto, message[:-2] + ".")
+    
+    elif command.split()[0] == "quit":
+        if cmdtype == "help":    # Display help text.
+            if len(command.split()) is not 1:
+                return
+            connection.privmsg(replyto, "Disconnect and terminate " + connection.get_nickname() + ". Optionally with reason.")
+            connection.privmsg(replyto,  grey + "Usage: " + blue + "!quit " + reset + italic + "reason")
+        elif cmdtype == "cmd":
+            if not userstatus.atleast_admin(self, event.source.nick, self.homechannel): #Insufficient rights.
+                connection.privmsg(replyto, "Denied, you need to have admin (super operator) status or higher in " + red + self.homechannel  + reset + ".")
+                return
+            if len(command.split()) == 1:
+                log.info("Killed by: " + event.source.nick)
+                self.die(msg = "Killed by " + event.source.nick)
+            else:
+                log.info("Killed by " + event.source.nick + " for " + trigger.split(maxsplit=1)[1])
+                self.die(msg = "[" + event.source.nick + "] " + trigger.split(maxsplit=1)[1])
+    
+    elif command.split()[0] == "reconnect":
+        if not userstatus.atleast_oper(self, event.source.nick, self.homechannel): 
+            connection.privmsg(replyto, "Denied, you need to have operator status or higher in " + red + self.homechannel  + reset + ".")
+            return
+        if cmdtype == "help":    # Display help text.
+            if len(command.split()) is not 1:
+                return
+            connection.privmsg(replyto, "Reconnect " + connection.get_nickname() + ". Reason optional.")
+            connection.privmsg(replyto,  grey + "Usage: " + blue + "!reconnect " + reset + italic + "reason")
+        elif cmdtype == "cmd":
+            if len(command.split()) == 1:
+                self.disconnect(msg = "Reconnect requested by " + event.source.nick)
+            else:
+                self.disconnect(msg = "[" + event.source.nick + "] " + command.split(maxsplit=1)[1])
+    
+    elif command.split()[0] == "recovernick":
+        if not userstatus.atleast_voiced(self, event.source.nick, self.homechannel):
+            connection.privmsg(replyto, "Denied, you need to have voiced status or higher in " + red + self.homechannel  + reset + ".")
+            return
+        if cmdtype == "help":    # Display help text.
+            if len(command.split()) is not 1:
+                return
+            connection.privmsg(replyto, "Let " + connection.get_nickname() + " try to recover " + connection.nickname + " as nickname.")
+        elif cmdtype == "cmd":
+            if connection.get_nickname() == connection.nickname:
+                connection.action(replyto,  "is already named " + red + connection.nickname + reset + ".")
+                return
+            from common.networkservices import NickServ
+            NickServ.recover_nick(connection, self.password)
+    
+    elif command.split()[0] == "join":
+        if not userstatus.atleast_oper(self, event.source.nick, self.homechannel):
+            connection.privmsg(replyto, "Denied, you need to have operator status or higher in " + red + self.homechannel  + reset + ".")
+            return
+        if cmdtype == "help":    #Display help text.
+            if len(command.split()) is not 1:
+                return
+            connection.privmsg(replyto, "Make " + connection.get_nickname() + " join a channel. Password optional.")
+            connection.privmsg(replyto, grey + "Usage: " + blue + self.cmdchar + "join " + red + italic + "channel " + reset + italic + "password")
+        elif cmdtype == "cmd":
+            try:
+                channel = command.split()[1]
+            except IndexError:
+                connection.privmsg(replyto, "Specify channel. For help type: " + blue + self.helpchar + "join")
+                return
+            try:
+                key = command.split(maxsplit=2)[2]
+            except IndexError:
+                do_everything_to.join(self, connection, channel)
+                return
+            do_everything_to.join(self, connection, channel, key)
+    
+    elif command.split()[0] == "part":
+        if cmdtype == "help":    #Display help text.
+            if len(command.split()) is not 1:
+                return
+            connection.privmsg(replyto, "Make " + connection.get_nickname() + " part a channel. Reason optional.")
+            connection.privmsg(replyto, grey + "Usage: " + blue + self.cmdchar + "join " + red + italic + "channel " + reset + italic + "password")
+        elif cmdtype == "cmd":
+            
+            homeadmin = False
+            if userstatus.atleast_oper(self, event.source.nick, self.homechannel):    # Is at least operator in home channel.
+                homeadmin = True
+            
+            targetadmin = False
+            if userstatus.atleast_oper(self, event.source.nick, event.target):
+                targetadmin = True
+            
+            if len(command.split()) == 1:   # No arguments.
+                if event.target in self.channels:   # It's a channel message.
+                    if not homeadmin and not targetadmin:   # Insufficient rights:
+                        connection.privmsg(replyto, "Denied. You need to have at least operator status in " + red + self.homechan + reset + " or " + red + event.target + reset + ".")
+                        return
+                    if event.target == self.homechannel:
+                        connection.action(replyto, "shall not abandon it's home channel!")
+                        return
+                    connection.part(event.target, event.source.nick)
+                else:   # It's a PM.
+                    connection.privmsg(replyto, "Specify a channel to part. For help type " + blue + self.helpchar + "part" + reset + ".")
+            elif len(command.split()) > 1:   # Arguments
+                if command.split()[1] not in self.channels: # First argument is not a channel the bot inhabits.
+                    connection.action(replyto, "does not inhabit " + red + command.split()[1] + reset + ". For help type " + blue + self.helpchar + "part" + reset + ".")
+                    return
+                if not homeadmin and not userstatus.atleast_oper(self, event.source.nick, command.split()[1]):  # Insufficient rights.
+                    connection.privmsg(replyto, "Denied. You need to have at least operator status in " + red + self.homechan + reset + " or " + red + command.split()[1] + reset + ".")
+                    return
+                if command.split()[1] == self.homechannel:
+                    connection.action("shall not abandon it's home channel!")
+                    return
+                try:
+                    connection.part(command.split()[1], command.split(maxsplit=2)[2])
+                except:
+                    connection.part(command.split()[1], event.source.nick)
+    
+    elif command.split()[0] == "msg" or  command.split(maxsplit=1)[0] == "act":
+        if cmdtype == "help":    #Display help text.
+            if len(command.split()) is not 1:
+                return
+            if command.split(maxsplit=1)[0] == "act":
+                message = "Let " + connection.get_nickname() + " send an action to a channel."
+                arguments = "action-message"
+            else:
+                message = "Let " + connection.get_nickname() + "send a message to a channel."
+                arguments = "name message"
+            connection.privmsg(replyto, message)
+            connection.privmsg(replyto, grey + "Usage: " + blue + "!" + command.split(maxsplit=1)[0] + reset + italic + " target " + arguments)
+        elif cmdtype == "cmd":
+            
+            # Parse user input.
+            try:
+                destination = trigger.split()[1]
+            except IndexError:  # User did not specify a destination.
+                connection.privmsg(replyto, "Destination not specified. For help type: " + blue + self.helpchar + command.split(maxsplit=1)[0])
+                return
+            try:
+                message = trigger.split(maxsplit=2)[2]
+            except IndexError:  # User did not specify a message.
+                connection.privmsg(replyto, "Message not specified. For help type: " + blue + self.helpchar + command.split(maxsplit=1)[0])
+                return
+            
+            # Send the message if user has owner status in the home channel.
+            if self.channels[self.homechannel].is_owner(event.source.nick): 
+                if destination == connection.get_nickname():
+                    connection.privmsg(replyto, "To prevent dying in a loop I shall not message myself.")
+                    return
+                if command.split(maxsplit=1)[0] == "act":
+                    connection.action(destination, message)
+                else:
+                    connection.privmsg(destination, message)
+            
+            # Reply error when bot does not inhabit destination channel.
+            elif destination not in self.channels:
+                connection.action(replyto, "does not inhabit " + red + destination + reset + ".")
+            
+            # Send message if user has at least operator status the home channel or the channel the message is intended for.
+            elif userstatus.atleast_oper(self, event.source.nick, self.homechannel) or userstatus.atleast_oper(self, event.source.nick, destination):
+                if command.split(maxsplit=1)[0] == "act":
+                    connection.action(destination, message)
+                else:
+                    connection.privmsg(destination, message)
+            
+            # Reply error if user is not operator of destination channel.
+            else:
+                connection.privmsg(replyto, "Denied, you need to be an operator of " + red + destination + reset +".")
+    
+    elif command.split()[0] == "channelfunctions":
+        if cmdtype == "help":    #Display help text. # Help code block first, as it is impossible to predict for what channel a later command is going to be issued. Rights filtering after help test.
+            if len(command.split()) is not 1:
+                return
+            connection.privmsg(replyto, "Display or toggle the status channel functions. Channel, function and value optional. Get a description of a functio via the help argument.")
+            connection.privmsg(replyto, grey + "Usage: " + blue + "!channelfunctions " + red + italic + "channel " + reset + italic + "function value")
+            connection.privmsg(replyto, grey + "Usage: " + blue + "!channelfunctions describe " + reset + italic + "function")
+        elif cmdtype == "cmd":
+            
+            if len(command.split()) == 1:   # No arguments.
+                if event.target == connection.get_nickname():   # Command issued via PM.
+                    connection.privmsg(replyto, "Nothing to display, Specify a channel.")
+                else:   # Command issued as channel message.
+                    message = AH.get_channelfunctions(self, event.target)
+                    connection.privmsg(replyto, message)
+            
+            elif len(command.split()) == 2:   # One argument.
+                if command.split()[1] in self.channels: # Info requested on specific channel.
+                    message = AH.get_channelfunctions(self, command.split()[1])
+                    connection.privmsg(replyto, message)
+                else:   # First argument is not a channel the bot inhabits.
+                    if not command.split()[1].lower() == "help":    # Not a help request.
+                        connection.privmsg(replyto, command.split()[1] + " is not a channel I inhabit. For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
+                    else:   # Help request.
+                        connection.privmsg(replyto, "Specify a channel function to get a description of. For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
+            
+            elif len(command.split()) == 3: # Two arguments.
+                channel = event.target
+                if event.target == connection.get_nickname():   # Command issued via PM.
+                    connection.privmsg(replyto, "One or three arguments required. For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
+                else:   # Command issued via channel.
+                    if not AH.is_channelfunction(command.split()[1]):   # First argument is not a channelfunction.
+                        if not command.split()[1].lower() == "describe":    # Not a help request.
+                            connection.privmsg(replyto, command.split()[1] + " is not a channel function. For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
+                            return
+                        if not AH.is_channelfunction(command.split()[2]): # Second argument not a channel function.
+                                connection.privmsg(replyto, command.split()[2] + " is not a channel function.")
+                                return
+                        connection.privmsg(replyto, AH.describe_channelfunction(command.split()[2]))
+                        return
+                    # Second argument unsupported.
+                    if not command.split()[2].lower() in ["on",  "off"]:
+                        if command.split()[1].lower() == "aggressiveness":
+                            if not AH.is_aggressiveness(command.split()[2].lower()):    # Is not an aggressiveness setting.
+                                connection.privmsg(replyto, command.split()[2] + " is not an aggressiveness setting. For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
+                                return
+                        else:   # Channel function is not aggresiveness.
+                            connection.privmsg(replyto, "The value of this channel function can only be \"on\" or \"off\". For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
+                            return
+                    if not userstatus.atleast_oper(self, event.source.nick, self.homechannel) and not userstatus.atleast_oper(self, event.source.nick, event.target):   # Does not have operator status or higher in target or home channel.
+                        connection.privmsg(replyto, "Denied. You need to have at least operator status in " + red + event.target + reset + " or " + red + self.homechannel + reset + ".")
+                        return
+                    if command.split()[1].lower() == "autojoin" and event.target == self.homechannel:   # Chaning autojoin of homechannel.
+                        connection.action(replyto, "will always join it's homechannel " + red + self.homechannel + reset + ", regardless of the autojoin function.")
+                    #self.db.run("UPDATE channels SET " + command.split()[1].lower() + "='" + command.split()[2].lower() + "' WHERE name='" + event.target + "' AND network='" + self.network + "'")
+                    self.db.run("UPDATE channels SET " + command.split()[1].lower() + "=%s WHERE name='" + event.target + "' AND network='" + self.network + "'", (command.split()[2].lower(), ))
+            
+            elif len(command.split()) == 4: # Three arguments.
+                if not command.split()[1] in self.channels: # Bot does not inhabit channel to be altered.
+                    connection.privmsg(replyto, command.split()[1] + " is not a channel I inhabit. For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
+                    return
+                if not AH.is_channelfunction(command.split()[2]):  # Function does not exist.
+                    connection.privmsg(replyto, command.split()[2] + " is not a valid channel function. For a list help type: " + blue + self.cmdchar + "channelfunctions" + red + italic + "channel")
+                    return
+                
+                if not command.split()[3].lower() in ["on", "off"] and not command.split()[2].lower() == "aggressiveness": # Third argument unsupported.
+                    connection.privmsg(replyto, "The value of this channel function can only be \"on\" or \"off\". For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
+                    return
+                if not userstatus.atleast_oper(self, event.source.nick, self.homechannel) and not userstatus.atleast_oper(self, event.source.nick, command.split()[1]):   # Does not have operator status or higher in target or home channel.
+                    connection.privmsg(replyto, "Denied. You need to have at least operator status in " + red + event.target + reset + " or " + red + self.homechannel + reset + ".")
+                    return
+                if command.split()[2].lower() == "autojoin" and command.split()[1] == self.homechannel:   # Chaning autojoin of homechannel.
+                    connection.action(replyto, "will always join it's homechannel " + red + self.homechannel + reset + ", regardless of the autojoin function.")
+                try:
+                    self.db.run("UPDATE channels SET " + command.split()[2].lower() + "='" + command.split()[3].lower() + "' WHERE LOWER(name)=LOWER('" + command.split()[1] + "') AND network='" + self.network + "'")
+                except:
+                    connection.privmsg(replyto, "Error, database record not updated.")
+                    return
+            else:   # Too many arguments.
+                connection.privmsg(replyto, "Too many arguments. For help type " + blue + self.helpchar + "channelfunctions" + reset + ".")
+    
+    elif command.split()[0] == "registernick":
+        if not self.channels[self.homechannel].is_owner(event.source.nick): #Insufficient rights.
+            connection.privmsg(replyto, "Denied, you need to be the owner of " + red + self.homechannel  + reset + ".")
+            return
+        if cmdtype == "help":    # Display help text.
+            if len(command.split()) is not 1:
+                return
+            connection.privmsg(replyto, "Register with NickServ.")
+            connection.privmsg(replyto, grey + "Usage: " + blue + self.cmdchar + "registernick " + reset + italic + "email")
+        elif cmdtype == "cmd":
+            
+            if len(command.split()) == 1:
+                connection.privmsg(replyto, "Insufficient arguments. For help type " + blue + self.helpchar + "registernick" + reset + ".")
+            elif len(command.split()) > 2:
+                connection.privmsg(replyto, "Too many arguments. For help type " + blue + self.helpchar + "registernick" + reset + ".")
+            elif not self.db.one("SELECT password FROM networks WHERE name='" + self.network + "'"):
+                alphabet = string.ascii_letters + string.digits
+                password = ''.join(secrets.choice(alphabet) for i in range(20)) # 20-character password.
+                self.db.run("UPDATE networks SET password=%s WHERE name=%s", (password, self.network))
+                connection.privmsg("NickServ", "REGISTER " + password + " " + trigger.split()[1])
+            else:
+                connection.privmsg("NickServ", "REGISTER " + self.db.one("SELECT password FROM networks WHERE name='" + self.network + "'") + " " + trigger.split()[1])
+    
+    elif command.split()[0] == "banall":
+        if not self.channels[self.homechannel].is_owner(event.source.nick): #Insufficient rights.
+            connection.privmsg(replyto, "Denied, you need to be the owner of " + red + self.homechannel  + reset + ".")
+            return
+        if cmdtype == "help":    # Display help text.
+            if len(command.split()) is not 1:
+                return
+            connection.privmsg(replyto, "Ban all nicknames and usernames for a host in all channels.")
+            connection.privmsg(replyto, grey + "Example: " + blue + self.cmdchar + "banall " + reset + italic + "host")
+        elif cmdtype == "cmd":
+            
+            if len(command.split()) == 1:
+                connection.privmsg(replyto, "Insufficient arguments. For help type " + blue + self.helpchar + "banall" + reset + ".")
+            elif len(command.split()) > 2:
+                connection.privmsg(replyto, "Too many arguments. For help type " + blue + self.helpchar + "banall" + reset + ".")
+            else:
+                labels = trigger.split()[1].split(".")
+                allowed = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
+                if "!" in trigger.split()[1] or "@" in trigger.split()[1]:
+                    connection.privmsg(replyto, "Only supply the host, all nicknames and usernames will be banned.")
+                elif len(trigger.split()[1]) > 253:
+                    connection.privmsg(replyto, "Host is to long.")    
+                elif re.match(r"[0-9]+$", labels[-1]):
+                    connection.privmsg(replyto, "The toplevel domain can not containof only numbers.")
+                elif not all(allowed.match(label) for label in labels):
+                    connection.privmsg(replyto, "Host contains invalid characters.")
+                elif len(labels) < 2:
+                    connection.privmsg(replyto, "Insufficient tuples.")
+                else:
+                    for channel in self.channels:
+                        connection.mode(channel, "+b *!*@" + trigger.split()[1])

+ 232 - 0
rotbot/commands/common.py

@@ -0,0 +1,232 @@
+import random
+bold = "\x02"
+italic = "\x1D"
+underline = "\x1F"
+reverse = "\x16" 	# swap background and foreground colors ("reverse video")
+reset = "\x0F"
+blue = "\x0302"
+green = "\x0303"
+red = "\x0304"
+grey = "\x0314"
+
+class CommandHelpers():
+    def disect_command(self, event):
+        trigger = event.arguments[0]
+        command = trigger[1:].lower()   #Command without prefix.
+        
+        # Determine command type.
+        if trigger.startswith(self.cmdchar):
+            cmdtype = "cmd"
+        elif trigger.startswith(self.helpchar):
+            cmdtype = "help"
+        else:
+            cmdtype = False
+        
+        # Determine where to reply to.
+        if event.type == "pubmsg":
+            replyto = event.target
+        elif event.type == "privmsg":
+            replyto = event.source.nick
+        else:
+            replyto = False
+            
+        return(cmdtype, trigger, command, replyto)
+    
+    def ccc(self, command, rights=False, event=False):   # Commandlist colour coding and channel rights filter.
+        if rights:
+            show = False
+            
+            if rights["homechan"] == "owner":
+                if self.channels[self.homechannel].is_owner(event.source.nick):
+                    show = True
+            if rights["homechan"] == "admin":
+                if self.channels[self.homechannel].is_owner(event.source.nick) or self.channels[self.homechannel].is_admin(event.source.nick):
+                    show = True
+            if rights["homechan"] == "oper":
+                if self.channels[self.homechannel].is_owner(event.source.nick) or self.channels[self.homechannel].is_admin(event.source.nick) or self.channels[self.homechannel].is_oper(event.source.nick):
+                    show = True
+            if not event.target == self.connection.get_nickname():   # Channel message.
+                if rights["chan"] == "owner":
+                    if self.channels[event.target].is_owner(event.source.nick):
+                        show = True
+                if rights["chan"] == "admin":
+                    if self.channels[event.target].is_owner(event.source.nick) or self.channels[event.target].is_admin(event.source.nick):
+                        show = True
+                if rights["chan"] == "oper":
+                    if self.channels[event.target].is_owner(event.source.nick) or self.channels[event.target].is_admin(event.source.nick) or self.channels[event.target].is_oper(event.source.nick):
+                        show = True
+            if show:
+                return blue + self.cmdchar + command + grey + ", "
+        else:
+            return blue + self.cmdchar + command + grey + ", "
+
+class AdminHelpers():
+    def get_channelfunctions(self, channel):
+        channelfunctions = self.db.one("SELECT autojoin, aggressiveness, join_greeting, statistics_commands, games, chat FROM channels WHERE LOWER(name)=LOWER('" + channel + "') AND network='" + self.network + "'")
+        if channelfunctions[0]:
+            autojoin = "on"
+        else:
+            autojoin = "off"
+        if channelfunctions[2]:
+            joingreet = "on"
+        else:
+            joingreet = "off"
+        if channelfunctions[3]:
+            statscmds = "on"
+        else:
+            statscmds = "off"
+        if channelfunctions[4]:
+            games = "on"
+        else:
+            games = "off"""
+        if channelfunctions[5]:
+            chat = "on"
+        else:
+            chat = "off"""
+        return ("autojoin " + green + autojoin + reset + ", aggressiveness " + green + channelfunctions[1] + reset +  ", join_greeting " + green + joingreet + reset + ", statistics_commands " + green + statscmds + reset + ", games " + green + games + reset + ", chat " + green + chat + reset + ".")
+        
+    def is_channelfunction(value):
+        if value.lower() in ["autojoin", "aggressiveness", "join_greeting",  "statistics_commands",  "games", "chat"]:
+            return True
+        else:
+            return False
+    
+    def describe_channelfunction(function):
+        if function == "autojoin":
+            message = "Join the channel automaticly after connecting."
+        elif function == "aggressiveness":
+            message = "How to respond to kick, ban and mode events. Options: " + blue + "passive" + reset + ", " + blue + "defense_only" + reset + ", " + blue + "equal_retalliation" + reset + ", " + blue + "battlebot" + reset + "."
+        elif function == "join_greeting":
+            message = "Greet users joining the channel on the first few joins, and later on large amounts."
+        elif function == "statistics_commands":
+            message = "Enable use of statistics commands."
+        elif function == "games":
+            message = "Enable use of game commands."
+        elif function == "chat":
+            message = "Respond to and initiate chat."
+        else:
+            message = "Sorry, this function has no description yet."
+        
+        return message
+    
+    def is_aggressiveness(value):
+        if value.lower() in ["passive", "defense_only", "equal_retalliation", "battlebot"]:
+            return True
+        else:
+            return False
+        
+
+class GameHelpers():
+    def roll_dice(amount, type):
+        rolls = []
+        for iterations in range(amount):
+            rolls.append(random.randint(1, type))
+        return rolls
+    
+    def get_info(self, user):
+        user = user.lower()
+        all_joins = self.db.all("SELECT joins FROM joins WHERE LOWER(\"user\")=%s AND user_network='" + self.network + "'", (user, ))
+        all_kicks = self.db.all("SELECT given, received FROM kicks WHERE LOWER(\"user\")=%s AND user_network='" + self.network + "'", (user, ))
+        all_messages = self.db.all("SELECT messages, messages_words, messages_characters, actions, actions_words, actions_characters, notices, notices_words, notices_characters FROM messages WHERE LOWER(\"user\")=%s AND user_network='" + self.network + "'", (user, ))
+        joins =0
+        for record in all_joins:
+            joins += record
+        given = 0
+        received = 0
+        for record in all_kicks:
+            given += int(record[0])
+            received += int(record[1])
+        messages = 0
+        messages_words = 0
+        messages_characters = 0
+        actions = 0
+        actions_words = 0
+        actions_characters = 0
+        notices = 0
+        notices_words = 0
+        notices_characters = 0
+        for record in all_messages:
+            messages += int(record[0])
+            messages_words += int(record[1])
+            messages_characters += int(record[2])
+            actions += int(record[3])
+            actions_words += int(record[4])
+            actions_characters += int(record[5])
+            notices += int(record[6])
+            notices_words += int(record[7])
+            notices_characters += int(record[8])
+        userrecord = self.db.one("SELECT xp_spent, level, coin, coin_given, coin_spent, ap_spent, karma_correction FROM users WHERE LOWER(name)=%s AND network='" + self.network + "'", (user, ))
+        xp_spent = userrecord[0]
+        level = userrecord[1]
+        coin = userrecord[2]
+        coin_given = userrecord[3]
+        coin_spent = userrecord[4]
+        ap_spent = userrecord[5]
+        karma_correction = userrecord[6]
+        
+        chatxp = messages + (messages_words / 4) + (messages_characters / 10) + ((actions + (actions_words / 4) + (actions_characters / 10)) * 2) + ((notices + (notices_words / 4) + (notices_characters / 10)) / 2)
+        kickxp = given * received
+        total_xp = ((joins + kickxp + chatxp) / 120) + float(coin_spent + ap_spent / 8)
+        
+        xp = total_xp - xp_spent
+        ap = total_xp - float(ap_spent)
+        total_messages = messages + actions + notices
+        total_words = messages_words + actions_words + notices_words
+        total_characters = messages_characters + actions_characters + notices_characters
+        if total_xp < 1:
+            total_xp = 1
+        joinkarma = (((messages / 19) - joins) / total_xp) /10
+        words_per_message = (total_words / 6) - total_messages
+        characters_per_message = (total_characters / 20) - total_messages
+        characters_per_word = (total_characters / 6) - total_words
+        chatkarma = ((words_per_message + characters_per_message + characters_per_message + characters_per_word) / total_xp) / 100
+        kickkarma = ((given * received) / total_xp) / 2
+        xpkarma = xp / 25
+        coinkarma = (coin - coin_given / (xp_spent + 1)) / 99
+        karma = float(joinkarma) + float(chatkarma) - float(kickkarma) + float(xpkarma) - float(coinkarma) + float(karma_correction)
+        if xp < 0:
+            xp = 0
+        
+        return level, xp, xp_spent, total_xp, karma, coin, coin_spent, coin_given, ap, ap_spent
+    
+    def list_top_players(self, sort):
+        result = self.db.all("SELECT name, level, xp_spent, coin FROM users WHERE network=%s ORDER BY " + sort + " DESC LIMIT 3 ", (self.network, ))
+        if sort == "level":
+            column = 1
+            threshold = 0
+        if sort == "xp_spent":
+            column = 2
+            threshold = 0
+        if sort == "coin":
+            column = 3
+            threshold = 10
+        message = ""
+        for record in result:
+            if not record[column] <= threshold:
+                level, xp, xpspent, totalxp, karma, coin, coinspent, coingiven, ap, apspent = GameHelpers.get_info(self, record[0])
+                if ap < 0:
+                    ap = 0
+                if sort == "level":
+                    message += red + str(record[0]) + reset + " [" + blue + "L " + green + str(level) + reset + ", X " + grey + str(xpspent) + "/" + green + str(int(xp)) + reset + ", A " + str(int(ap)) + ", C " + green + str(coin) + reset + ", K " + green + str(round(karma, 2)) + reset + "], "
+                if sort == "xp_spent":
+                    message += red + str(record[0]) + reset + " [L " + green + str(level) + reset + ", " + blue + "X " + grey + str(xpspent) + "/" + green + str(int(xp)) + reset + ", A " + str(int(ap)) + ", C " + green + str(coin) + reset + ", K " + green + str(round(karma, 2)) + reset + "], "
+                if sort == "coin":
+                    message += red + str(record[0]) + reset + " [L " + green + str(level) + reset + ", X " + grey + str(xpspent) + "/" + green + str(int(xp)) + reset + ", " + reset + "A " + str(int(ap)) + ", " + blue + "C " + green + str(coin) + reset + ", K " + green + str(round(karma, 2)) + reset + "], "
+        return message[:-2]
+    
+    def player_info(self, user):
+        level, xp, xpspent, totalxp, karma, coin, coinspent, coingiven, ap, apspent = GameHelpers.get_info(self, user)
+        if ap < 0:
+            ap = 0
+        return "Level: " + str(level) + grey + ", " + reset + "XP: " + str(int(xp)) + "/" + grey + str(round(totalxp, 4)) + ", " + reset + "AP: " + str(int(ap)) + grey + ", " + reset + "coin: " + str(coin) + grey + "[S " + str(coinspent) + ", G " + str(coingiven) + "], " + reset + "karma: " + str(round(karma, 4))
+
+class StatisticsHelpers():
+    def add_message_stats(stats):
+        messages = 0
+        words = 0
+        characters = 0
+        for record in stats:
+            messages += record[0]
+            words += record[1]
+            characters += record[2]
+        return messages, words, characters

+ 325 - 0
rotbot/commands/games.py

@@ -0,0 +1,325 @@
+import random
+from commands.common import CommandHelpers as CH
+from commands.common import GameHelpers
+
+bold = "\x02"
+italic = "\x1D"
+underline = "\x1F"
+reverse = "\x16" 	# swap background and foreground colors ("reverse video")
+reset = "\x0F"
+blue = "\x0302"
+green = "\x0303"
+red = "\x0304"
+grey = "\x0314"
+
+def do_command(self, connection, event):
+    cmdtype, trigger, command, replyto = CH.disect_command(self, event)
+    
+    # Do nothing if there is no command.
+    if not command:
+        return
+    try:
+        command.split()[0]
+    except:
+        return
+    
+    # Do noting if the games channel function is off and it's a channel message.
+    if not self.db.one("SELECT games FROM channels WHERE name='" + event.target + "' AND network='" + self.network + "'") and not event.target == connection.get_nickname():
+        return
+    
+    if command == "cmd" or command == "cmds" or command == "commands":
+        if cmdtype == "cmd":
+            connection.privmsg(replyto, grey + "Games: " + CH.ccc(self, "8ball") + CH.ccc(self, "dice") + CH.ccc(self, "player") + CH.ccc(self, "players") + CH.ccc(self, "levelup") + CH.ccc(self, "givecoin")[:-2] + ".")
+            connection.privmsg(replyto, grey + "Game help: " + blue + self.helpchar + "level" + grey + ", " + blue + self.helpchar + "xp" + grey + ", " + blue + self.helpchar + "ap" + grey + ", " + blue + self.helpchar + "coin" + grey + ", " + blue + self.helpchar + "karma" + grey + ".")
+    
+    elif command.split()[0] == "8ball":
+        if cmdtype == "help":    #Display help text.
+            connection.privmsg(replyto, "Ask a question of the mighty and illusive 8-Ball.")
+            connection.privmsg(replyto, grey + "Usage: " + blue + self.cmdchar + "8ball " + reset + italic + "question")
+        elif cmdtype == "cmd":
+            
+            if len(command.split()) < 2:    # Command contains only !8ball.
+                messages = [
+                    "Don't forget to ask a question...", 
+                    "Hey, that's not a question!", 
+                    "What would you like to know?", 
+                    "You want me to predict nothing?", 
+                    "Are you intentionally not asking a question?", 
+                    "Ask a question you tease!", 
+                    "You do not seem to grasp this, for help type: " + blue + self.helpchar + "8ball", 
+                    "You will die alone.", 
+                ]
+                connection.privmsg(replyto, random.choice(messages))
+            else:
+                messages = [
+                    "Yes.", 
+                    "No.", 
+                    "Affirmative.",
+                    "No way!", 
+                    "Negative.", 
+                    "Positive.", 
+                    "Correct.", 
+                    "Incorrect.", 
+                    "Likely", 
+                    "Unlikely", 
+                    "Maybe.", 
+                    "Definately!", 
+                    "Perhaps?", 
+                    "Most indubitably.", 
+                    "Does the pope shit in the woods?", 
+                    "When hell freezes over.", 
+                    "Only between 9 and 5.", 
+                    "Only just before you die.", 
+                    red + bold + "ERROR: " + bold + "probability failure.", 
+                    "Ask again later.", 
+                    "I don't know.", 
+                    "Unpredictable.", 
+                ]
+                connection.privmsg(replyto, random.choice(messages))
+    
+    elif command.split()[0] == "dice":
+        if cmdtype == "help":    #Display help text.
+            if len(command.split()) is not 1:
+                return
+            connection.privmsg(replyto, "Rolls multiple dices of chosen type. Specifying type or amount is optional.")
+            connection.privmsg(replyto, grey + "Usage: " + blue + self.cmdchar + "dice " + reset + italic + "amount type")
+            connection.privmsg(replyto, grey + "Example: " + blue + self.cmdchar + "dice " + reset + italic + "2 d10")
+            connection.privmsg(replyto, grey + "Note: " + reset + "The dice type is specified as a " + italic + "d " + reset + "followed by the amount of sides.")
+        elif cmdtype == "cmd":
+            
+            arguments = len(command.split()) - 1
+            if arguments == 0: 
+                connection.privmsg(replyto,  str(random.randint(1,  6)) + " & " + str(random.randint(1,  6)) + ".")
+            elif arguments == 1:
+                try:
+                    diceamount = int(command.split()[1])
+                except ValueError:  # Argument is not an integer.
+                    if command.split()[1].startswith("d"):    # Specific dice type.
+                        try:
+                            dicetype = int(command.split()[1][1:])
+                        except ValueError:  # "d" not followd by interger.
+                            connection.privmsg(replyto, "Invalid number or type of dice. For help type: " + blue + self.helpchar + "dice" + reset + ".")
+                            return
+                        if dicetype < 1:
+                            connection.action(replyto, "can not create objects with less then one side.")
+                        else:
+                            connection.privmsg(replyto, str(GameHelpers.roll_dice(1,  dicetype)[0]))
+                    else:   # Argument does not start with "d" and is not an integer.
+                        connection.privmsg(replyto, "Invalid number or type of dice. For help type: " + blue + self.helpchar + "dice" + reset + ".")
+                    return
+                if diceamount < 1:
+                    connection.privmsg(replyto, "Rolling " + bold + "no " + reset + "dice.")
+                elif diceamount > 10:
+                    connection.action(replyto, "can not fit that many dice into it's robot hands.")
+                else:
+                    connection.privmsg(replyto, ", ".join(str(rolls) for rolls in GameHelpers.roll_dice(diceamount,  6)) + ".") # Roll x amount of dice.
+            elif arguments == 2:
+                try:
+                    diceamount = int(command.split()[1])
+                except ValueError:  # First argument not an integer.
+                    connection.privmsg(replyto, "Invalid number of dice. For help type: " + blue + self.helpchar + "dice" + reset + ".")
+                    return
+                try:
+                    dicetype = int(command.split()[2][1:])
+                except ValueError:  # Second argument not a dice type.
+                    connection.privmsg(replyto, "Invalid type of dice. For help type: " + blue + self.helpchar + "dice" + reset + ".")
+                    return
+                if diceamount < 1:
+                    connection.privmsg(replyto, "Rolling " + bold + "no " + reset + "dice.")
+                elif diceamount > 10:
+                    connection.action(replyto, "can not fit that many dice into it's robot hands.")
+                elif dicetype < 1:
+                    connection.action(replyto, "can not create objects with less then one side.")
+                elif dicetype > 9999999999 and diceamount > 1:
+                    connection.action(replyto, "Those dices are so large i can only roll one at a time.")
+                else:
+                    connection.privmsg(replyto, ", ".join(str(rolls) for rolls in GameHelpers.roll_dice(diceamount,  dicetype)) + ".") # Roll x amount of x type dice.
+            else:   # Invalid amount of arguments.
+                connection.privmsg(replyto, "Too many arguments. For help type: " + blue + self.helpchar + "dice" + reset + ".")
+    
+    elif command.split()[0] == "player":
+        if cmdtype == "help":    #Display help text.
+            connection.privmsg(replyto, "Displays a users game statistics. User optional.")
+        elif cmdtype == "cmd":
+            
+            if len(command.split()) == 1:
+                user = event.source.nick.lower()
+                message = grey + "Your statistics. " + reset
+            elif len(command.split()) == 2:
+                user = command.split()[1]
+                if not self.db.one("SELECT id FROM users WHERE LOWER(name)=%s AND network='" + self.network + "'", (user, )):
+                    connection.action(replyto, "does not know of a " + red + trigger.split()[1] + reset + ".")
+                    return
+                if user == connection.get_nickname().lower():
+                    connection.privmsg(replyto, "The game does not play the master.")
+                    return
+                if user == event.source.nick.lower():
+                    message = grey + "Your statistics. " + reset
+                else:
+                    message = grey + "Statistics for " + red + trigger.split()[1] + reset + ". "
+            else:
+                connection.privmsg(replyto, "Too many arguments, For help type " + blue + self.helpchar + "players " + reset + ".")
+                return
+             
+            level, xp, xpspent, totalxp, karma, coin, coinspent, coingiven, ap, apspent = GameHelpers.get_info(self, user)
+            if ap < 0:
+                ap = 0
+            info = GameHelpers.player_info(self, user)
+            connection.privmsg(replyto, message + info)
+    
+    elif command.split()[0] == "levelup":
+        user = event.source.nick.lower()
+        level, xp, xpspent, totalxp, karma, coin, coinspent, coingiven, ap, apspent = GameHelpers.get_info(self, user)
+        if cmdtype == "help":    #Display help text.
+            connection.privmsg(replyto, "Spend " + str(int(level * 2.7)) + " XP to gain your next level. Gain multiple levels (for the price of the lowest level) by specifying the amount.")
+        elif cmdtype == "cmd":    
+            if len(command.split()) == 1:
+                if xp < int(level * 2.7):
+                    connection.privmsg(replyto, "Insufficient XP, you need at least " + str(int(level * 2.7)) + ".")
+                elif ap < 1:
+                    connection.privmsg(replyto, "Insufficient AP, you need at least 1.")
+                else:
+                    self.db.run("UPDATE users SET level=level+1, xp_spent=xp_spent+%s, ap_spent=ap_spent+1 WHERE LOWER(name)=%s AND network=%s", (int(level * 2.7), user, self.network, ))
+                    self.db.run("UPDATE users SET coin=coin+0.3 WHERE level>0 AND network=%s", (self.network, ))
+                    info = GameHelpers.player_info(self, user)
+                    connection.privmsg(replyto, "Your new statistics: " + info)
+            elif len(command.split()) == 2:
+                try:
+                    levels = int(command.split()[1])
+                except:
+                    connection.privmsg(replyto, "Invalid amount.")
+                    return
+                if levels < 1:
+                    connection.privmsg(replyto, "Invalid amount.")
+                elif xp < int(level * 2.7):
+                    connection.privmsg(replyto, "Insufficient XP, you need at least " + str(int(level * 2.7)) + ".")
+                elif ap < levels:
+                    connection.privmsg(replyto, "Insufficient AP, you need at least 1.")
+                else:
+                    self.db.run("UPDATE users SET level=level+%s, xp_spent=xp_spent+%s, ap_spent=ap_spent+%s WHERE LOWER(name)=LOWER(%s) AND network=%s", (levels, int(level * 2.7), levels, user, self.network, ))
+                    self.db.run("UPDATE users SET coin=coin+%s WHERE level>0 AND network=%s", (levels * 0.3, self.network, ))
+                    info = GameHelpers.player_info(self, user)
+                    connection.privmsg(replyto, "Your new statistics: " + info)
+            else:
+                connection.privmsg(replyto, "Too many arguments. For help type " + blue + self.helpchar + "levelup " + reset + ".")
+    
+    elif command.split()[0] == "givecoin":
+        if cmdtype == "help":    #Display help text.
+            connection.privmsg(replyto, "Give coins to another player. Amount optional.")
+            connection.privmsg(replyto, grey + "Usage: " + blue + self.cmdchar + "givecoin " + reset + italic + "user amount")
+        elif cmdtype == "cmd":
+            
+            if len(command.split()) == 1:
+                connection.privmsg(replyto, "Insufficient arguments. For help type " + blue + self.helpchar + "givecoin" + reset + ".")
+                return
+            elif len(command.split()) > 3:
+                connection.privmsg(replyto, "Too many arguments. For help type " + blue + self.helpchar + "givecoin" + reset + ".")
+                return
+            elif command.split()[1] == event.source.nick.lower():
+                connection.privmsg(replyto, "You already have your own coin. For help type " + blue + self.helpchar + "givecoin" + reset + ".")
+                return
+            elif len(command.split()) == 3:
+                stop = False
+                try:
+                    if float(command.split()[2]) < 0:
+                        connection.privmsg(replyto, "You clever abuser! The " + red + self.cmdchar + bold + "give" + bold + "coin" + reset + " command is not designed for robbing. There will be consequences...")
+                        self.db.run("UPDATE users SET coin=coin-3, karma_correction=karma_correction-0.5 WHERE name=%s AND network=%s", (event.source.nick, self.network))
+                        stop = True
+                except TypeError:
+                    connection.privmsg(replyto, "Invalid amount. For help type " + blue + self.helpchar + "givecoin" + reset + ".")
+                    return
+                if stop:
+                    return
+            elif not event.target == connection.get_nickname():
+                if not command.split()[1] in self.channels[event.target].users():
+                    connection.privmsg(replyto, red + trigger.split()[1] + reset + " is not present.")
+                    return
+                
+            level, xp, xpspent, totalxp, karma, coin, coinspent, coingiven, ap, apspent = GameHelpers.get_info(self, event.source.nick)
+            receivingrecord = self.db.one("SELECT level, away FROM users WHERE LOWER(name)=%s AND network=%s", (command.split()[1], self.network, ))
+            if level < 1:
+                connection.privmsg(replyto, "You need to " + blue + self.cmdchar + "levelup " + reset + "to be able to give coin.")
+                return
+            elif coin < 1:
+                connection.privmsg(replyto, "You have no coin to give.")
+                return
+            elif not receivingrecord:
+                connection.action(replyto, "does not know of any \"" + red + trigger.split()[1] + reset + "\".")
+                return
+            elif receivingrecord[0] == 0:
+                connection.privmsg(replyto, red + trigger.split()[1] + reset + " is not playing the game.")
+                return
+            elif receivingrecord[1] == True:
+                connection.privmsg(replyto, red + trigger.split()[1] + reset + "is not here right now.")
+                return
+            elif ap < 1:
+                connection.privmsg(replyto, "You have no action points, go use IRC some more...")
+                return
+            if len(command.split()) == 2:
+                self.db.run("UPDATE users SET coin=coin-1, coin_spent=coin_spent+1, coin_given=coin_given+1, ap_spent=ap_spent+1 WHERE name=%s AND network=%s", (event.source.nick, self.network, ))
+                self.db.run("UPDATE users SET coin=coin+1 WHERE LOWER(name)=%s AND network=%s", (command.split()[1], self.network, ))
+            elif len(command.split()) == 3:
+                self.db.run("UPDATE users SET coin=coin-%s, coin_spent=coin_spent+%s, coin_given=coin_given+%s, ap_spent=ap_spent+1 WHERE name=%s AND network=%s", (command.split()[2], command.split()[2], command.split()[2], event.source.nick, self.network, ))
+                self.db.run("UPDATE users SET coin=coin+%s WHERE LOWER(name)=%s AND network=%s", (command.split()[2], command.split()[1], self.network))
+            info = GameHelpers.player_info(self, event.source.nick)
+            connection.notice(event.source.nick, "Your new statistics: " + info)
+    
+    elif command.split()[0] == "players":
+        if cmdtype == "help":    #Display help text.
+            connection.privmsg(replyto, "Display top player rankings.")
+        elif cmdtype == "cmd":
+            
+            toplevel = GameHelpers.list_top_players(self, "level")
+            if toplevel:
+                connection.notice(event.source.nick, "Ranking level: " + toplevel)
+            topxp = GameHelpers.list_top_players(self, "xp_spent")
+            if topxp:
+                connection.notice(event.source.nick, "Ranking experience: " + topxp)
+            topcoin = GameHelpers.list_top_players(self, "coin")
+            if topcoin:
+                connection.notice(event.source.nick, "Ranking wealth: " + topcoin)
+            if not toplevel and not topxp and not topcoin:
+                connection.privmsg(replyto, "Nobody is playing the game.")
+    
+    elif command.split()[0] == "level":
+        if cmdtype == "help":    #Display help text.
+            connection.privmsg(replyto, "For 10 XP you can use !levelup to gain a level. As your level increases, things become more difficult, while more capabilities and options are unlocked.")
+    elif command.split()[0] == "xp":
+        if cmdtype == "help":    #Display help text.
+            connection.privmsg(replyto, "XP is earned by using IRC and playing the game, in channels with " + red + connection.get_nickname() + reset + ". Ask any operator in " + red + self.homechannel + reset + " to add a channel. XP is used to level up, advance classes, as a limit and as a multiplier. Once XP is expended it keeps counting towards your total.")
+    elif command.split()[0] == "ap":
+        if cmdtype == "help":    #Display help text.
+            connection.privmsg(replyto, "AP is earned by using IRC and playing the game in channels with " + red + connection.get_nickname() + reset + ". Ask any operator in " + red + self.homechannel + reset + " to add a channel. AP is expended for every action you take in the game.")
+    elif command.split()[0] == "coin":
+        if cmdtype == "help":    #Display help text.
+            connection.privmsg(replyto, "Coin is earned when certain events occur, for instance when a player expends XP. Coin is used to buy items and classes. To give a player coin use " + blue + self.cmdchar + "givecoin" + reset + ". Coin affects karma.")
+    elif command.split()[0] == "karma":
+        if cmdtype == "help":    #Display help text.
+            connection.privmsg(replyto, "Karma is a secret formula, based upon on your IRC events and how you play the game. It does not increase like XP. Some events, skills and items are karma based.")
+    
+#    elif command.split()[0] == "classup":
+#        if cmdtype == "help":    #Display help text.
+#            connection.privmsg(replyto, "Spend 10 XP to gain a class in your current level. List available classes with " + blue + self.helpchar + "classup available " + reset + ".")
+#            connection.privmsg(replyto, grey + "Usage: " + blue + self.cmdchar + "classup " + reset + italic + "class")
+#        elif cmdtype == "cmd":
+#            
+#            if len(command.split()) == 1:
+#                connection.privmsg(replyto, "Insufficient arguments. For help type " + blue + self.helpchar + "classup" + reset + ".")
+#            if len(command.split()) == 2:
+#                if command.split()[1] == "available":
+#                    level, xp, xpspent, karma = GameHelpers.get_info(self, event.source.nick)
+#                    if level == 0:
+#                        connection.privmsg(replyto, "There are no level 0 classes.")
+#                    if level == 1:
+#                        connection.privmsg(replyto, "Level 1 classes: Maggot, nubcake, ")
+#                    if level == 2:
+#                        connection.privmsg(replyto, "Level 2 classes: Retard, ")
+#                    if level == 3:
+#                        connection.privmsg(replyto, "Level 3 classes: ")
+#                    if level == 4:
+#                        connection.privmsg(replyto, "Level 3 classes: ")
+#                    if level == 5:
+#                        connection.privmsg(replyto, "Level 4 classes: ")
+#                else:
+#            else:
+#                connection.privmsg(replyto, "Too many arguments. For help type " + blue + self.helpchar + "classup" + reset + ".")

+ 141 - 0
rotbot/commands/public.py

@@ -0,0 +1,141 @@
+from common import userstatus, queries
+from commands.common import CommandHelpers as CH
+
+bold = "\x02"
+italic = "\x1D"
+underline = "\x1F"
+reverse = "\x16" 	# swap background and foreground colors ("reverse video")
+reset = "\x0F"
+blue = "\x0302"
+green = "\x0303"
+red = "\x0304"
+grey = "\x0314"
+
+def do_command(self, connection, event):
+    cmdtype, trigger, command, replyto = CH.disect_command(self, event)
+    
+    # Do nothing if there is no command.
+    if not command:
+        return
+    try:
+        command.split()[0]
+    except:
+        return
+    
+    # The actual commands:
+    if command == "test":
+        if cmdtype == "help":    #Display help text.
+            if len(command.split()) is not 1:
+                return
+            connection.privmsg(replyto, "Strictly for testing purposes only!")
+        elif cmdtype == "cmd":
+            connection.privmsg(replyto, str(self.channels[self.homechannel].owners()))
+            connection.privmsg(event.source.nick, self.channels[event.target].users())
+    
+    elif command == "cmd" or command == "cmds" or command == "commands":
+        if cmdtype == "help":    #Display help text.
+            if len(command.split()) is not 1:
+                return
+            connection.privmsg(replyto, "Displays a list of commands.")
+        elif cmdtype == "cmd":
+            if not event.target == connection.get_nickname() and not self.db.one("SELECT join_greeting FROM channels WHERE name='" + event.target + "' AND network='" + self.network + "'"):
+                connection.privmsg(replyto, grey + "Public: " + CH.ccc(self, "cmd") + CH.ccc(self, "help") + CH.ccc(self, "away")[:-2] + ".")
+            else:
+                connection.privmsg(replyto, grey + "Public: " + CH.ccc(self, "cmd") + CH.ccc(self, "help") + CH.ccc(self, "away") + CH.ccc(self, "stopgreet")[:-2] + ".")
+    
+    elif command == "help":
+        if cmdtype == "help":    #Display help text.
+            if len(command.split()) is not 1:
+                return
+            connection.privmsg(replyto, "Explains how to get help on any specific command and hints the user to the commandlist.")
+        elif cmdtype == "cmd":
+            connection.privmsg(replyto, "Replace the " + italic + "! " + reset + "prefix of any comand with " + italic + self.helpchar + " " + reset + "for help with a specific command. Request the command list with: " + blue + "!cmd")
+            connection.privmsg(replyto, grey + "Example: " + reset + blue + self.helpchar + "help")
+    
+    
+    elif command.split()[0] == "stopgreet":
+        if cmdtype == "help":    #Display help text.
+            if len(command.split()) is not 1:
+                return
+            connection.privmsg(replyto, "Stops the bot from greeting you or a specific user. Channel, user and option to resume optional.")
+            connection.privmsg(replyto, grey + "Usage: " + blue + "!stopgreet " + reset + italic + "resume " + red + "channel user")
+        elif cmdtype == "cmd":
+            
+            # Check for resume variation of command.
+            resume = False
+            try:
+                if command.split()[1] == "resume":
+                    resume = True
+                    command = command.split(' ', 1)[1]  # Remove the resume argument. Which makes the following logic easier.
+            except:
+                pass
+            
+            if len(command.split()) == 1:    # No arguments.
+                if event.target == connection.get_nickname():   # PM.
+                    connection.privmsg(replyto, "Specify at least a channel. For help type " + blue + self.helpchar + "stopgreet" + reset + ".")
+                    return
+                user = event.source.nick
+                channel = event.target
+                
+            if len(command.split()) == 2:    # One argument.
+                if command.split()[1] not in self.channels: # Argument is not a channel the bot inhabits.
+                    if event.target == connection.get_nickname():   # PM.
+                        connection.action(replyto, "does not inhabit " + red + command.split()[1] + reset + ".")
+                        return
+                    # Channel message
+                    if not userstatus.atleast_halfop(self, event.source.nick, self.homechannel) and not userstatus.atleast_halfop(self, event.source.nick, event.target):  # Insufficient rights.
+                        connection.privmsg(replyto, "Denied. You need to have at least halfop status in " + red + self.homechannel + reset + " or " + red + event.target + reset + ".")
+                        return
+                    # Stopgreet for x user in current channel
+                    user = command.split()[1]
+                    channel = event.target
+                else:   # Bot inhabit channel.
+                    user = event.source.nick
+                    channel = command.split()[1]
+            if len(command.split()) == 3:    # Two arguments.
+                if command.split()[1] not in self.channels: # Bot does not inhabit channel.
+                    connection.action(replyto, "does not inhabit " + red + command.split()[1] + reset + ".")
+                    return
+                if not userstatus.atleast_halfop(self, event.source.nick, self.homechannel) and not userstatus.atleast_halfop(self, event.source.nick, command.split()[1]):  # Insufficient rights.
+                    connection.privmsg(replyto, "Denied. You need to have at least halfop status in " + red + self.homechannel + reset + " or " + red + command.split()[1] + reset + ".")
+                    return
+                user = command.split()[2]
+                channel = command.split()[1]
+            if len(command.split()) > 3:    # Too many arguments.
+                connection.privmsg(replyto, "Too many arguments. For help type " + blue + self.helpchar + "stopgreet" + reset + ".")
+                return
+            
+            # Check for database record.
+            result = self.db.one("SELECT id, stopgreet FROM joins WHERE LOWER(\"user\")=LOWER('" + user + "') AND user_network='" + self.network + "' AND LOWER(channel)=LOWER('" + channel + "') AND channel_network='" + self.network + "'")
+            if not result:  # No record in database.
+                connection.action(replyto, "has not yet had the pleasure of greeting " + red + user + reset + " in " + red + channel + reset + ".")
+                return
+            
+            if resume:
+                stopgreet = False
+                message = "has already every intention to greet "
+            else:
+                stopgreet = True
+                message = "has already stopped greeting "
+            if result[1] == stopgreet:
+                connection.action(replyto, message + red + user + reset + " in " + red + channel + reset + ".")
+                return
+            print(str(stopgreet) + str(user) + str(channel))
+            try:
+                self.db.run("UPDATE joins SET stopgreet='" + str(stopgreet) + "' WHERE LOWER(\"user\")=LOWER('" + user + "') AND user_network='" + self.network + "' AND lower(channel)=LOWER('" + channel + "') AND channel_network='" + self.network + "'")
+            except:
+                connection.privmsg(replyto, "Failed to update database.")
+    
+    elif command.split()[0] == "away":
+        if cmdtype == "help":    #Display help text.
+            if len(command.split()) is not 1:
+                return
+            connection.privmsg(replyto, "Sets you away, optionally with reason. This affects the !seen command and the game.")
+            connection.privmsg(replyto, grey + "Usage: " + blue + "!away " + reset + italic + "reason")
+            
+        elif cmdtype == "cmd":
+            queries.create_ifnot_onrecord(self, "users", event.source.nick)
+            if len(trigger.split()) == 1:
+                self.db.run("UPDATE users SET away=TRUE, away_reason=NULL WHERE name='" + event.source.nick + "' AND network='" + self.network + "'")
+            else:
+                self.db.run("UPDATE users SET away=TRUE, away_reason=%s WHERE name='" + event.source.nick + "' AND network='" + self.network + "'", (trigger.split(maxsplit=1)[1], ))

+ 245 - 0
rotbot/commands/statistics.py

@@ -0,0 +1,245 @@
+from datetime import datetime
+from commands.common import CommandHelpers as CH, StatisticsHelpers
+
+bold = "\x02"
+italic = "\x1D"
+underline = "\x1F"
+reverse = "\x16" 	# swap background and foreground colors ("reverse video")
+reset = "\x0F"
+blue = "\x0302"
+green = "\x0303"
+red = "\x0304"
+grey = "\x0314"
+
+def do_command(self, connection, event):
+    cmdtype, trigger, command, replyto = CH.disect_command(self, event)
+    
+    # Do nothing if there is no command.
+    if not command: 
+        return
+    try:
+        command.split()[0]
+    except:
+        return
+    
+    # Do noting if the games channel function is off and it's a channel message.
+    if not self.db.one("SELECT statistics_commands FROM channels WHERE name='" + event.target + "' AND network='" + self.network + "'") and not event.target == connection.get_nickname():
+        return
+
+    if command == "cmd" or command == "cmds" or command == "commands":
+        if cmdtype == "cmd":
+            connection.privmsg(replyto, grey + "Statistics: " + CH.ccc(self, "seen") + CH.ccc(self, "joins") + CH.ccc(self, "kicks") + CH.ccc(self, "messages") + CH.ccc(self, "actions") + CH.ccc(self, "notices")[:-2] + ".")
+    
+    
+    elif command.split()[0] == "joins" or command.split()[0] == "kicks" or command.split()[0] == "messages" or command.split()[0] == "actions" or command.split()[0] == "notices":
+        if cmdtype == "help":    #Display help text.
+            if len(command.split()) is not 1:
+                return
+            connection.privmsg(replyto, "Display amount of " + command.split()[0] + " of user and channel. Channel and user optional.")
+            connection.privmsg(replyto, grey + "Usage: " + blue + self.cmdchar + command.split()[0] + " " + reset + italic + "channel user")
+        elif cmdtype == "cmd":
+            
+            # Parse user input
+            user = event.source.nick
+            channel = None
+            channelonly= False
+            if not connection.get_nickname() == event.target:   # Channel message.
+                channel = event.target
+            if len(command.split()) == 1:    # Command contains only !joins.
+                user = event.source.nick
+            elif len(command.split()) == 2:  # Command has one argument.
+                if command.split()[1] in self.channels:
+                    channel = command.split()[1]
+                    if connection.get_nickname() == event.target:   # Private message.
+                        channelonly = True
+                else:
+                    user = trigger.split()[1]
+            elif len(command.split()) == 3: # Command has two arguments.
+                if not command.split()[1] in self.channels: # Bot does not inhabit requested channel.
+                    if not command.split()[2] in self.channels: # User did not revert channel and user in command syntax.
+                        connection.action(replyto, "does not inhabit " + red + command.split()[1] + reset + ".")
+                        return
+                    else:   # User reverted user and channel in command syntax.
+                        user = trigger.split()[1]
+                        channel = command.split()[2]
+                else: # Bot does inhabit requested channel.
+                    user = trigger.split()[2]
+                    channel = trigger.split()[1]
+            elif len(command.split()) < 5:  # To many arguments
+                connection.privmsg(replyto, "To many arguments. For help type " + blue + self.helpchar + "joins" + reset + ".")
+                return
+            
+            if command.split()[0] == "joins":
+                if channel: # User and channel.
+                    userstat = str(sum(self.db.all("SELECT " + command.split()[0] + " FROM " + command.split()[0] + " WHERE channel_network='" + self.network + "' AND LOWER(\"user\")=LOWER('" + user + "') AND user_network='" + self.network + "'")))
+                    userchannelstat = str(self.db.one("SELECT " + command.split()[0] + " FROM " + command.split()[0] + " WHERE LOWER(channel)=LOWER('" + channel + "') AND channel_network='" + self.network + "' AND LOWER(\"user\")=LOWER('" + user + "') AND user_network='" + self.network + "'"))
+                    channelstat = str(sum(self.db.all("SELECT " + command.split()[0] + " FROM " + command.split()[0] + " WHERE LOWER(channel)=LOWER('" + channel + "') AND channel_network='" + self.network + "' AND user_network='" + self.network + "'")))
+                    if userchannelstat == "None":
+                        userchannelstat = "0"
+                    if channelonly:
+                        connection.privmsg(replyto, red + channel + reset + " has " + green + channelstat + reset + " " + command.split()[0] + reset + " in total.")
+                    elif userstat == "0" and not channelonly: # No user joins on record.
+                        connection.action(replyto, "has no record of any joins by " + red + user + reset + " in " + red + channel + reset + ".")
+                    else:   # User joins on record.
+                        connection.privmsg(replyto, red + user + reset + " has " + green + userstat + reset + " " + command.split()[0] + ". Of which " + green + userchannelstat + reset + " have been in " + red + channel + reset + ", that has " + green + channelstat + reset + " " + command.split()[0] + reset + " in total.")
+                else:   # Only user.
+                    userstat = str(sum(self.db.all("SELECT " + command.split()[0] + " FROM " + command.split()[0] + " WHERE channel_network='" + self.network + "' AND LOWER(\"user\")=LOWER('" + user + "') AND user_network='" + self.network + "'")))
+                    if userstat == "0":    # No statistics on user.
+                        connection.action(replyto, "has no record of any joins by " + red + user + reset + ".")
+                    else:   # Got statistics on user.
+                        connection.privmsg(replyto, red + user + reset + " has " + green + userstat + reset + " " + command.split()[0] + " in channels I monitor.")
+            elif command.split()[0] == "kicks":
+                if channel: # User and channel.
+                    try:
+                        givenkicks, receivedkicks = self.db.one("SELECT given, received FROM " + command.split()[0] + " WHERE LOWER(channel)=LOWER('" + channel + "') AND channel_network='" + self.network + "' AND LOWER(\"user\")=LOWER('" + user + "') AND user_network='" + self.network + "'")
+                    except:
+                        givenkicks = 0
+                        receivedkicks = 0
+                    if channelonly:
+                        connection.privmsg(replyto, red + channel + reset + " has " + green + channelstat + reset + " " + command.split()[0] + reset + " in total.")
+                    elif givenkicks == 0 and receivedkicks == 0:  # No kicks on record.
+                        connection.action(replyto, "has no record of any kicks for " + red + user + reset + " in " + red + channel + reset + ".")
+                    else:   # Kciks on record.
+                        channelkicks = self.db.all("SELECT given FROM " + command.split()[0] + " WHERE LOWER(channel)=LOWER('" + channel + "') AND channel_network='" + self.network + "'")
+                        connection.privmsg(replyto, red + user + reset + " has kicked " + green + str(givenkicks) + reset + " and been kicked " + green + str(receivedkicks) + reset + " times in " + red + channel + reset + ", where were " + green + str(sum(channelkicks)) + reset + " kicks in total.")
+                else:   # Only user.
+                    userstat = self.db.all("SELECT given, received FROM kicks WHERE LOWER(\"user\")=LOWER('" + user + "') AND user_network='" + self.network + "'")
+                    kicksgiven = 0
+                    kicksreceived = 0
+                    for record in userstat:
+                        kicksgiven += record[0]
+                        kicksreceived += record[1]
+                    if kicksgiven == 0 and kicksreceived == 0:
+                        connection.action(replyto, red + user + reset + " has no record of any kicks for " + red + user + reset + " in " + red + channel + reset + ".")
+                    else:
+                        connection.privmsg(replyto, red + user + reset + " has given " + green + str(kicksgiven) + reset + " and received " + green + str(kicksreceived) + reset + " kicks")
+            elif command.split()[0] == "messages" or command.split()[0] == "actions" or command.split()[0] == "notices":
+                userstat = self.db.all("SELECT " + command.split()[0] + ", " + command.split()[0] + "_words, " + command.split()[0] + "_characters FROM messages WHERE LOWER(\"user\")=LOWER('" + user + "') AND user_network='" + self.network + "'")
+
+                userrecord = False
+                for record in userstat:
+                    if not record[0] == 0:
+                        userrecord = True
+                
+                if not userrecord:
+                    if command.split()[0] == "messages":
+                        connection.action(replyto, "has no record of " + red + user + reset + " speaking.")
+                        return
+                    if command.split()[0] == "actions":
+                        connection.action(replyto, "has no record of " + red + user + reset + " acting.")
+                        return
+                    if command.split()[0] == "notices":
+                        connection.action(replyto, "has not noticed " + red + user + reset + ".")
+                        return
+                messages, words, characters = StatisticsHelpers.add_message_stats(userstat)
+                message = "Totals of " + red + user + " " + green + str(messages) + reset + " " + blue + command.split()[0] + reset + ", " + green + str(words) + reset + " words, " + green + str(characters) + reset + " characters."
+                if channel: # User and channel.
+                    userchanstat = self.db.one("SELECT " + command.split()[0] + ", " + command.split()[0] + "_words, " + command.split()[0] + "_characters FROM messages WHERE lower(channel)=lower('" + channel + "') AND channel_network='" + self.network + "' AND lower(\"user\")=lower('" + user + "') AND user_network='" + self.network + "'")
+                    chanstat = self.db.all("SELECT " + command.split()[0] + ", " + command.split()[0] + "_words, " + command.split()[0] + "_characters FROM messages WHERE lower(channel)=lower('" + channel + "') AND channel_network='" + self.network + "'")
+                    if not userchanstat[0] == 0:
+                        message += " " + red + user + reset + " in " + red + channel + " " + green + str(userchanstat[0]) + reset + " " + blue + command.split()[0] + reset + ", " + green + str(userchanstat[1]) + reset + " words, " + green + str(userchanstat[2]) + reset + " chars."
+                    channelrecord = False
+                    for record in chanstat:
+                        if not record[0] == 0:
+                            channelrecord = True
+                    if channelrecord:
+                        messages, words, characters = StatisticsHelpers.add_message_stats(chanstat)
+                        message += " Total in " + red + channel + " " + green + str(messages) + reset + " " + blue + command.split()[0] + reset + ", " + green + str(words) + reset + " wrd, " + green + str(characters) + reset + " chr." 
+                connection.privmsg(replyto, message)
+    
+    elif command.split()[0] == "seen":
+        if cmdtype == "help":    #Display help text.
+            if len(command.split()) is not 1:
+                return
+            connection.privmsg(replyto, "Report the last sighting of a user.")
+            connection.privmsg(replyto, grey + "Usage: " + blue + self.cmdchar + command.split()[0] + " " + reset + italic + "user")
+        elif cmdtype == "cmd":
+        
+            if len(command.split()) == 1:
+                connection.privmsg(replyto, "I am seeing you right now. For help type " + blue + self.helpchar + reset + ".")
+            elif len(command.split()) == 2:
+                if not self.db.one("SELECT last_act_type FROM users WHERE LOWER(name)='" + command.split()[1] + "' AND network='" + self.network + "'"):
+                    connection.action(replyto, "has never seen " + red + trigger.split()[1] + reset + ".")
+                elif command.split()[1] == event.source.nick.lower():
+                    connection.action(replyto, "holds up a mirror to " + event.source.nick + ".")
+                elif "!" in command.split()[1] and "@" in command.split()[1]:
+                    connection.privmsg(replyto, "The looks more like a hostname then a nickname to me.")
+                else:
+                    record = self.db.one("SELECT last_act_type, last_act_datetime, last_act_channel, last_act, last_act_auxiliary, away, away_reason FROM users WHERE LOWER(name)='" + command.split()[1] + "' AND network='" + self.network + "'")
+                    if command.split()[1] == connection.get_nickname().lower():
+                        action = "last action was "
+                    else:
+                        action = "last saw " + red + trigger.split()[1] + reset + " "
+                    if record[0] == "nick":
+                        action += "changing nickname to " + red + record[3]
+                    elif record[0] == "join":
+                        action += "joining " + red + record[2]
+                    elif record[0] == "kick":
+                        if record[4]:
+                            action += "kicking " + red + record[4] + reset + " for " + green + record[3]
+                        else:
+                            action += "kicking " + red + record[3]
+                    elif record[0] == "kicked":
+                        if record[4]:
+                            action += "being kicked by " + red + record[3] + reset + " for " + green + record[4]
+                        else:
+                            action += "being kicked by " + red + record[3]
+                    elif record[0] == "mode":
+                        action += "changing modes on " + red + record[3]
+                    elif record[0] == "part":
+                        action += "parting " + red + record[2]
+                        if record[3]:
+                            action += reset + " for " + record[3]
+                    elif record[0] == "quit":
+                        action += "disconnecting"
+                        if record[3]:
+                            action += " due to " + green + record[3]
+                    elif record[0] == "topic":
+                        action += "changing the topic of " + red + record[2] + reset + " to " + green + record[3]
+                    elif record[0] == "msg":
+                        action += "posting " + green + record[3] + reset + " to " + red + record[2]
+                    elif record[0] == "notice":
+                        action += "posting the notice " + green + record[3] + " to " + red + record[2]
+                    elif record[0] == "action":
+                        action += green + record[3] + reset + " in " + red + record[2]
+                    else:
+                        connection.privmsg(replyto, "Last stored action unsupported by command code.")
+                        return
+                    action += reset + ", "
+                    differential = datetime.now() - record[1]
+                    if differential.seconds < 5:    # Less then 5 seconds.
+                        action += blue + "right now."
+                    elif differential.seconds < 20: # Less then 20 seconds.
+                        action += blue + "just now."
+                    elif differential.seconds < 60: # Less then a minute.
+                        action += green + str(differential.seconds) + blue + " seconds " + reset + "ago."
+                    elif differential.seconds / 60 == 1: # 1 minute.
+                        action += green + "1 " + blue + "minute " + reset + "ago."
+                    elif int(differential.seconds / 60) < 60:   # Less then an hour.
+                        action += green + str(int(differential.seconds / 60)) + blue + " minutes " + reset + "ago."
+                    elif int(differential.seconds / 60) ==  60: # 1 hour.
+                        action += green + "1 " + blue + "hour " + reset + "ago."
+                    elif int(differential.seconds / 3600) < 24 : # Less then a day.
+                        remaining_seconds = int(differential.seconds - int(differential.seconds / 3600) * 3600)
+                        action += green + str(int(differential.seconds / 3600)) + blue + " hours" + reset + " and " + str(int(remaining_seconds / 60)) + " minutes ago."
+                    elif int(differential.seconds / 3600) == 24 : # 1 day.
+                        action += green + "1 " + blue + "day " + reset + "ago."
+                    elif differential.days < 7: # Less then a week.
+                        remaining = differential - datetime.timedelta(days=differential.days)
+                        action += green + str(differential.days) + blue + " days " + reset + "and " + str(int(remaining.seconds / 3600)) + " hours ago."
+                    elif differential.days < 365:   # Less then a year.
+                        action += green + str(differential.days) + blue + " days " + reset + "ago."
+                    elif differential.days < 365:   # Less then 5 years.
+                        remaining_days = int(int(differential.days / 365) * 365)
+                        remaining = differential - datetime.timedelta(days=remaining_days)
+                        action += green + str(int(differential.days / 365)) + blue + " years " + reset + "and " + str(remaining.days) + " days."
+                    else:   # More then 5 years.
+                        action += green + str(int(differential.days / 365)) + blue + " years ago."
+                    connection.action(replyto, action)
+                    if record[5]:
+                        if record[6]:
+                            connection.privmsg(replyto, red + trigger.split()[1] + reset + " is away " + green + record[6] + ".")
+                        else:
+                            connection.privmsg(replyto, red + trigger.split()[1] + reset + " is away.")
+            else:   # Too many arguments.
+                connection.privmsg(replyto, "Too many arguments. For help type " + blue + self.helpchar + "seen" + reset + ".")

+ 36 - 0
rotbot/common/do_everything_to.py

@@ -0,0 +1,36 @@
+from common.networkservices import ChanServ
+
+def join(self, connection, channel, key=False):
+    ChanServ.unban(connection, channel, connection.get_nickname())
+    ChanServ.akick_del(connection, channel, connection.get_nickname())
+    ChanServ.invite(connection, channel)
+    ChanServ.getkey(connection, channel)
+    knownkey = self.db.one("SELECT key FROM channels WHERE name='" + channel + "' AND network='" + self.network + "'")
+    if key:
+        connection.join(channel, key)
+        if not key == knownkey:
+            self.channelkeys[channel] = key
+    elif knownkey:
+        connection.join(channel, key)
+    else:
+        connection.join(channel)
+
+def unban(connection, channel, user, mask):
+    ChanServ.unban(connection, channel, user)
+    connection.mode(channel, "-b " + mask)
+
+def ban(connection, channel, user, mask, reason):
+    ChanServ.ban(connection, channel, user, reason)
+    connection.mode(channel, "+b " + mask)
+
+def banhost(connection, channel, mask, reason):
+    ChanServ.ban(connection, channel, mask, reason)
+    connection.mode(channel, "+b " + mask)
+
+def kick(connection, channel, user, reason):
+    ChanServ.kick(connection, channel, user, reason)
+    connection.kick(channel, user, reason)
+
+def bankick(connection, channel, user, mask, reason):
+    ban(connection, channel, user, mask, reason)
+    kick(connection, channel, user, reason)

+ 13 - 0
rotbot/common/log.py

@@ -0,0 +1,13 @@
+from datetime import datetime
+
+def info(message):
+    print("INFO " + str(datetime.now()) + " " + str(message))
+
+def notice(message):
+    print("NOTICE " + str(datetime.now()) + " " + str(message))
+
+def warning(message):
+    print("WARNING " + str(datetime.now()) + " " + str(message))
+
+def error(message):
+    print("ERROR " + str(datetime.now()) + " " + str(message))

+ 51 - 0
rotbot/common/networkservices.py

@@ -0,0 +1,51 @@
+class NickServ():
+    def recover_nick(connection, password):
+        connection.privmsg("NickServ", "IDENTIFY " + connection.nickname + " " + password) # Identify with NickServ.
+        connection.privmsg("NickServ", "RECOVER " + connection.nickname + " " + password)  # Recover control of nickname via NickServ.
+        connection.privmsg("NickServ", "GHOST " + connection.nickname + " " + password)  # Recover control of nickname via NickServ, old style.
+        connection.nick(connection.nickname)    # Set original nickname. Should have happened during the nickServ recover, this fails when still connecting. So this creates a loop to successfully recover via NickServ.
+
+class ChanServ():
+    def invite(connection, channel):
+        connection.privmsg("ChanServ", "INVITE " + channel)
+    
+    def getkey(connection, channel):
+        connection.privmsg("ChanServ", "GETKEY " + channel)
+    
+    def ban(connection, channel, user, reason):
+        connection.privmsg("ChanServ", "BAN " + channel + " " + user + " " + reason)
+    
+    def tempban(connection, channel, user, duration, reason):
+        connection.privmsg("ChanServ", "BAN " + channel + " +" + duration + " " + user + " " + reason)
+    
+    def unban(connection, channel, user):
+        connection.privmsg("ChanServ", "UNBAN " + channel + " " + user)
+    
+    def akick_add(connection, channel, user):
+        connection.privmsg("ChanServ", "AKICK " + channel + " ADD " + user)
+        
+    def akick_del(connection, channel, user):
+        connection.privmsg("ChanServ", "AKICK " + channel + " DEL " + user)
+    
+    def kick(connection, channel, user, reason):
+        connection.privmsg("ChanServ", "KICK " + channel + " " + user + " " + reason)
+    
+    def give_mode(connection, channel, user, mode):
+        if mode[1] == "q":
+            modename = "OWNER"
+        if mode[1] == "a":
+            modename = "PROTECT"
+        if mode[1] == "o":
+            modename = "OP"
+        if mode[1] == "h":
+            modename = "HALFOP"
+        if mode[1] == "v":
+            modename = "VOICE"
+        connection.privmsg("ChanServ", modename + " " + channel + " " + user)
+    
+    def take_all_modes(connection, channel, user):
+        connection.privmsg("ChanServ", "DEVOICE " + channel + " " + user)
+        connection.privmsg("ChanServ", "DEHALFOP " + channel + " " + user)
+        connection.privmsg("ChanServ", "DEOP " + channel + " " + user)
+        connection.privmsg("ChanServ", "DEPROTECT " + channel + " " + user)
+        connection.privmsg("ChanServ", "DEOWNER " + channel + " " + user)

+ 15 - 0
rotbot/common/queries.py

@@ -0,0 +1,15 @@
+def create_ifnot_onrecord(self, table, name):
+    record = self.db.one("SELECT name, network FROM " + table + " WHERE LOWER(name)=LOWER(%s) AND LOWER(network)=LOWER(%s)", (name, self.network, ))
+    
+    if record:  # On record.
+        # Correct capitalisation of network name, if needed.
+        if not self.network == record[1]:
+            self.db.run("UPDATE networks SET name=%s WHERE LOWER(name)=LOWER(%s)", (self.network, self.network, ))
+        
+        # Correct capitalisation if needed.
+        if not name == record[0]:
+            self.db.run("UPDATE " + table + " SET name=%s WHERE LOWER(name)=LOWER(%s) AND network=%s", (name, name, self.network, ))
+    
+    # Create record.
+    else:   # Not on record.
+        self.db.run("INSERT INTO " + table + " (name, network) VALUES (%s, %s)", (name, self.network, ))

+ 31 - 0
rotbot/common/userstatus.py

@@ -0,0 +1,31 @@
+def atleast_admin(self, user, channel):
+    if channel not in self.channels:
+        return False
+    if self.channels[channel].is_owner(user) or self.channels[channel].is_admin(user):
+        return True
+    else:
+        return False
+        
+def atleast_oper(self, user, channel):
+    if channel not in self.channels:
+        return False
+    if self.channels[channel].is_owner(user) or self.channels[channel].is_admin(user) or self.channels[channel].is_oper(user):
+        return True
+    else:
+        return False
+
+def atleast_halfop(self, user, channel):
+    if channel not in self.channels:
+        return False
+    if self.channels[channel].is_owner(user) or self.channels[channel].is_admin(user) or self.channels[channel].is_oper(user) or self.channels[channel].is_halfop(user):
+        return True
+    else:
+        return False
+
+def atleast_voiced(self, user, channel):
+    if channel not in self.channels:
+        return False
+    if self.channels[channel].is_owner(user) or self.channels[channel].is_admin(user) or self.channels[channel].is_oper(user) or self.channels[channel].is_halfop(user) or self.channels[channel].is_voiced(user):
+        return True
+    else:
+        return False

+ 121 - 0
rotbot/events/common.py

@@ -0,0 +1,121 @@
+import random
+from datetime import datetime
+from common import queries, userstatus, log
+
+class Protectees():
+    def update(self, nick, user, host):
+        if nick in self.protectees: # On record.
+            if userstatus.atleast_halfop(self, user, self.homechannel) or nick == self.connection.get_nickname():   # Update. Is atleast halfop or bot itself.
+                self.protectees[nick].update({'ident': nick + "!" + user + "@" + host})
+            else:   # Delete.
+                del self.protectees[nick]
+        else:   # Append.
+            if userstatus.atleast_halfop(self, user, self.homechannel) or nick == self.connection.get_nickname():   # Update. Is atleast halfop or bot itself.
+                self.protectees[nick] = {'ident': nick + "!" + user + "@" + host}
+
+class Replyto():
+    def name(connection, event):
+        messages = [
+            "Hello " + event.source.nick + ".", 
+            "How are you today " + event.source.nick +  "?", 
+            "Piss off " + event.source.nick + "!", 
+            event.source.nick + ", what are you botherring me for?", 
+            "Go bother someone else...", 
+            "Is life treating you fair?", 
+            "What's up?", 
+            "Why are you talking to me?", 
+            "I'm not talking to you!", 
+            "What have you been up to?", 
+            "How is life?", 
+            "What do you want from me?", 
+            event.source.nick + ", why are you bothering me?", 
+            event.source.nick + ", when will you stop talking about me?", 
+            event.source.nick + " I hate you!", 
+            "Get bent!", 
+            "Go and cut yourself.", 
+            "Do you think i care about you?", 
+            "Stop nickalerting me " + event.source.nick + ", you wanker!", 
+        ]
+        actions = [
+            "hides!",
+            "dies.", 
+            "runs away.", 
+            "is ignoring that.", 
+            "is not feeling like caring.", 
+            "is away", 
+            "will be ignoring that.", 
+            "is faggaliciouz!! <333", 
+            "likes you! <3", 
+            "looks the other way...", 
+            "does a little dance with " + event.source.nick + ".", 
+            "makes a little love.", 
+            "get's down tonight.", 
+            "thinks SAM Broadcaster sucks raw cocks in hell!", 
+            "is secretly in love with " + event.source.nick + ".", 
+            "tosses " + event.source.nick + "'s salad.", 
+            "tortures " + event.source.nick + " horribly!", 
+            "is smelling like tuna when looking at " + event.source.nick + ".", 
+            "sniffing armpits.. Eew! Smells like " + event.source.nick + ".", 
+            "rapes " + event.source.nick + ".", 
+            "pets " + event.source.nick + ", and sais: Why what a nice little human you are, and such plentifull organs!"
+        ]
+        
+        # Reply with a random message or action.
+        if random.randint(0, 1) == 0:
+            connection.privmsg(event.target, random.choice(messages))
+        else:
+            connection.action(event.target, random.choice(actions))
+            
+class Aggressiveness():
+    def retalliation_reason(self, connection, protectee, behaviour):
+        if protectee == connection.get_nickname():  # Bot itself.
+             return "Aggression channel function = " + behaviour + ": Self defense."
+        else:
+            return "Aggression channel function = " + behaviour + ": " + protectee + " is atlast halfop in " + self.homechannel + "."
+
+class Lastact():
+    def update(self, name, type, channel=False, lastact=False, auxiliary=False):
+        
+        # Create records if not present.
+        if channel:
+            queries.create_ifnot_onrecord(self, "channels", channel)
+        queries.create_ifnot_onrecord(self, "users", name)
+        
+        # Update record.
+        self.db.run("UPDATE users SET last_act_type=%s, last_act_datetime=%s, last_act_channel=%s, last_act=%s, last_act_auxiliary=%s WHERE name=%s AND network=%s",  (type, str(datetime.now()), channel, lastact, auxiliary, name, self.network))
+        
+        # Set user back from away, if user is active.
+        if type not in ["nick", "kick", "part", "quit"]:
+            self.db.run("UPDATE users SET away=FALSE WHERE name=%s AND network=%s", (name, self.network, ))
+
+class MessageStatistics():
+    def update(self, event, type):
+        type = type + "s"
+        queries.create_ifnot_onrecord(self, "channels", event.target)
+        queries.create_ifnot_onrecord(self, "users", event.source.nick)
+        if not self.db.one("SELECT channel, \"user\" FROM messages WHERE LOWER(channel)=LOWER(%s) AND channel_network=%s AND LOWER(\"user\")=LOWER(%s) AND user_network=%s", (event.target, self.network, event.source.nick, self.network, )):  # Not on record.
+            self.db.run("INSERT INTO messages (channel, channel_network, \"user\", user_network) VALUES (%s, %s, %s, %s)", (event.target, self.network, event.source.nick, self.network , ))   # Create record.
+        self.db.run("UPDATE messages SET " + type + "=" + type + "+1, " + type + "_words=" + type + "_words+" + str(len(event.arguments[0].split())) + ", " + type + "_characters=" + type + "_characters+" + str(len(event.arguments[0])) + " WHERE LOWER(channel)=LOWER('" + event.target + "') AND channel_network='" + self.network + "' AND LOWER(\"user\")=LOWER('" + event.source.nick + "') AND user_network='" + self.network + "'")   # Increment record.
+
+class Inform():
+        def owners(self, connection, message):
+            log.notice("Message: " + message)
+            if self.homechannel in self.channels:
+                for owner in self.channels[self.homechannel].owners():
+                    connection.privmsg(owner, message)
+        
+        def notice_owners(self, connection, message):
+            log.notice("Message: " + message)
+            if self.homechannel in self.channels:
+                for owner in self.channels[self.homechannel].owners():
+                    connection.notice(owner, message)
+        
+        def operators(self, connection, message):
+            if self.homechannel in self.channels:
+                for user in self.channels[self.homechannel].owners():
+                    connection.privmsg(user, message)
+                for user in self.channels[self.homechannel].admins():
+                    connection.privmsg(user, message)
+                for user in self.channels[self.homechannel].opers():
+                    connection.privmsg(user, message)
+

+ 68 - 0
rotbot/events/on_action.py

@@ -0,0 +1,68 @@
+from common import userstatus
+from events.common import Replyto, Lastact, MessageStatistics
+
+bold = "\x02"
+italic = "\x1D"
+underline = "\x1F"
+reverse = "\x16"    # swap background and foreground colors ("reverse video")
+reset = "\x0F"
+blue = "\x0302"
+green = "\x0303"
+red = "\x0304"
+grey = "\x0314"
+
+def process_event(self, connection, event):
+    
+    # Update last act.
+    Lastact.update(self, event.source.nick, "action", channel=event.target, lastact=event.arguments[0])
+    
+    # Save statistic to database.
+    MessageStatistics.update(self, event, "action")
+    
+    # Stop if channelfunction chat if off.
+    if not self.db.one("SELECT chat FROM channels WHERE name='" + event.target + "' AND network='" + self.network + "'"):
+        return
+
+    # Slap guard.
+    if event.arguments[0].lower().startswith("slaps ") and not event.target == connection.get_nickname():   # Channel action that stats with "slaps ".
+        
+        # Stop if chat channel function is off.
+        if not self.db.one("SELECT chat FROM channels WHERE name='" + event.target + "' AND network='" + self.network + "'"):
+            return
+    
+        # Only protect the worty.
+        if not userstatus.atleast_voiced(self, event.arguments[0].split(" ")[1], self.homechannel) and not userstatus.atleast_oper(self, event.arguments[0].split(" ")[1], self.homechannel): # Insufficient rights.
+            if event.arguments[0].split(" ")[1].lower() == connection.get_nickname(): # Bot slapped
+                Replyto.name(connection, event)
+            return
+        if userstatus.atleast_voiced(self, event.source.nick, self.homechannel):   # Slapper has atleast voice in home channel.
+            if not self.channels[self.homechannel].is_owner(event.arguments[0].split(" ")[1]):  # Slappee is not owner.
+                if event.arguments[0].split(" ")[1].lower() == connection.get_nickname(): # Bot slapped
+                    Replyto.name(connection, event)
+                return
+            if self.channels[self.homechannel].is_owner(event.source.nick) and  self.channels[self.homechannel].is_owner(event.arguments[0].split(" ")[1]): # Slapper and slappee are owner.
+                if event.arguments[0].split(" ")[1].lower() == connection.get_nickname(): # Bot slapped
+                    Replyto.name(connection, event)
+                return
+        
+        # Respond.
+        if " with a " in event.arguments[0]:
+            if event.arguments[0].split(" with a ", maxsplit=1)[1]:
+                if event.arguments[0].split(" ")[1].lower() == connection.get_nickname().lower(): # Bot slapped
+                    connection.action(event.target, "Takes the " + event.arguments[0].split(" with a ", maxsplit=1)[1] + " like a robot.")
+                else:
+                    connection.action(event.target, "swiftly jumps in front of " + red + event.arguments[0].split(" ")[1] + reset + " to block the " + event.arguments[0].split(" with a ", maxsplit=1)[1])
+
+            elif event.arguments[0].split(" ")[1].lower() == connection.get_nickname(): # Bot slapped
+                connection.action(event.target, "bites " + red + event.source.nick + reset + " furiously.")
+            else:
+                connection.action(event.target, "swiftly jumps in front of " + red + event.arguments[0].split(" ")[1] + reset + " to block the slap.")
+        elif len(event.arguments[0].split(" ")) > 1:
+            if event.arguments[0].split(" ")[1].lower() == connection.get_nickname(): # Bot slapped
+                connection.action(event.target, "bites " + red + event.source.nick + reset + " furiously.")
+            else:
+                connection.action(event.target, "swiftly jumps in front of " + red + event.arguments[0].split(" ")[1] + reset + " to block the slap.")
+    
+    # Respond to own name.
+    elif connection.get_nickname().lower() in event.arguments[0].lower() and event.source.nick is not connection.get_nickname(): # Bot's name was mentioned, not by the bot itself.
+        Replyto.name(connection, event)

+ 69 - 0
rotbot/events/on_join.py

@@ -0,0 +1,69 @@
+from common import log, queries
+from events.common import Lastact
+
+bold = "\x02"
+italic = "\x1D"
+underline = "\x1F"
+reverse = "\x16" 	# swap background and foreground colors ("reverse video")
+reset = "\x0F"
+blue = "\x0302"
+green = "\x0303"
+red = "\x0304"
+grey = "\x0314"
+
+def process_event(self, connection, event):
+    log.info(event)
+    
+    # Update last act.
+    Lastact.update(self, event.source.nick, "join", channel=event.target)
+    
+    # Add join event to database for statistics.
+    queries.create_ifnot_onrecord(self, "channels", event.target)
+    queries.create_ifnot_onrecord(self, "users", event.source.nick)
+    if not self.db.one("SELECT id FROM joins WHERE LOWER(channel)=LOWER(%s) AND channel_network=%s AND LOWER(\"user\")=LOWER(%s) AND user_network='" + self.network + "'", (event.target, self.network, event.source.nick, )):  # No record yet
+        self.db.run("INSERT INTO joins (channel, channel_network, \"user\", user_network, joins) VALUES (%s, %s, %s, %s, 0)", (event.target, self.network, event.source.nick, self.network, ))
+    self.db.run("UPDATE joins SET joins = joins + 1 WHERE LOWER(channel)=LOWER(%s) AND channel_network=%s AND LOWER(\"user\")=LOWER(%s) AND user_network=%s", (event.target, self.network, event.source.nick, self.network, ))
+    
+    if event.source.nick == connection.get_nickname():  # The bot joined a channel.
+        connection.who(self.homechannel)    # Get whoreplies for users of homechannel.
+        if self.channels[event.target].has_key():
+            if event.target in self.channelkeys:    # New key used to join channel.
+                self.db.run("UPDATE channels SET key='" + self.channelkeys[event.target] + "' WHERE name='" + event.target + "' AND network='" + self.network + "'")    # Save new key to DB.
+                del self.channelkeys[event.target]  # Delete entry.
+        if event.target == self.homechannel:    # Home channel.
+            connection.who(connection.get_nickname())   # get whoreply to add bot to protectees.
+        return  # Do not greet myself.
+    
+    # Promote owners.
+    if event.source.nick in self.channels[self.homechannel].owners():
+        connection.mode(event.target, "+vhoa " + event.source.nick + " " + event.source.nick + " " + event.source.nick + " " + event.source.nick)
+    
+    # Stop if greeting is not wanted.
+    joingreeting = self.db.one("SELECT join_greeting FROM channels WHERE name='" + event.target + "' AND network='" + self.network + "'")
+    stopgreet = self.db.one("SELECT stopgreet FROM joins WHERE channel='" + event.target + "' AND channel_network='" + self.network + "' AND \"user\"='" + event.source.nick + "' AND user_network='" + self.network + "'")
+    if not joingreeting or stopgreet:
+        return
+    
+    # Show greeting.
+    joins = self.db.one("SELECT joins FROM joins WHERE channel='" + event.target + "' AND channel_network='" + self.network + "' AND \"user\"='" + event.source.nick + "' AND user_network='" + self.network + "'")
+    if not self.db.one("SELECT join_greeting FROM channels WHERE name='" + event.target + "' AND network ='" + self.network + "'"): # Do not greet users joining the channel.
+        return
+    if joins == 1:
+        message = "Welcome to " + red + event.target + reset + ", " + red + event.source.nick + reset + ". For a list of command type " + blue + self.cmdchar + "cmd" + reset + "."
+    if joins == 3:
+        message = "Welcome back in " + red + event.target + reset + ", " + red + event.source.nick + reset + ". To turn of greetings, type " + blue + "!stopgreet" + reset + "."
+    if joins == 5:
+        if self.channels[event.target].has_key():   # Channel has a password.
+            message = "Welcome back again " + red + event.source.nick + reset + ". To automaticly join this channel type " + blue + "/ns ajoin " + reset + "ADD " + red + event.target + reset + italic + " password"
+        else:   # Channel does not have a password.
+            message = "Welcome back again " + red + event.source.nick + reset + ". To automaticly join this channel type " + blue + "/ns ajoin " + reset + "ADD " + red + event.source.nick + " " + event.target
+    if joins < 100 and str(joins)[-1:] == "0":
+        message = red + event.source.nick + reset + " you have joined " + red + event.target + reset + " " + str(joins) + " times!"
+    if joins < 1000 and str(joins)[-2:] == "00" or joins < 1000 and str(joins)[-2:] == "50":
+        message = "Epic! " + red + event.source.nick + reset + " you have joined " + red + event.target + reset + " " + str(joins) + " times!"
+    if joins < 10000 and str(joins)[-2:] == "00":
+        message = "AMAZING! " + red + event.source.nick + reset + " you have joined " + red + event.target + reset + " " + str(joins) + " times!"
+    try:
+        connection.privmsg(event.target, message)
+    except:
+        pass

+ 119 - 0
rotbot/events/on_kick.py

@@ -0,0 +1,119 @@
+from common.networkservices import ChanServ
+from common import userstatus, do_everything_to, log
+from events.common import Aggressiveness, Lastact
+
+bold = "\x02"
+italic = "\x1D"
+underline = "\x1F"
+reverse = "\x16" 	# swap background and foreground colors ("reverse video")
+reset = "\x0F"
+blue = "\x0302"
+green = "\x0303"
+red = "\x0304"
+grey = "\x0314"
+
+def process_event(self, connection, event):
+    log.info(event)
+    
+    kicker = event.source.nick
+    channel = event.target
+    kicked = event.arguments[0]
+    reason = event.arguments[1]
+    
+    # Update last act.
+    if reason:
+        Lastact.update(self, kicker, "kick", channel=channel, lastact=kicked, auxiliary=reason)
+        Lastact.update(self, kicked, "kicked", channel=channel, lastact=kicker, auxiliary=reason)
+    else:
+        Lastact.update(self, kicker, "kick", channel=channel, lastact=kicked)
+        Lastact.update(self, kicked, "kicked", channel=channel, lastact=kicker)
+    
+    # Create user records if they don't exist.
+    if not self.db.one("SELECT id FROM users WHERE name='" + kicker + "' AND network='" + self.network + "'"):   # Kicker does not have a user record.
+        self.db.run("INSERT INTO \"users\" (name, network) VALUES ('" + kicker + "', '" + self.network + "')")  # Create user record.
+    if not self.db.one("SELECT id FROM users WHERE name='" + kicked + "' AND network='" + self.network + "'"):  # Kicked does not have a user record.
+        self.db.run("INSERT INTO \"users\" (name, network) VALUES ('" + kicked + "', '" + self.network + "')")  # Create user record.
+    
+    # Create kick records if they don't exist.
+    if not self.db.one("SELECT id FROM kicks WHERE channel='" + channel + "'AND channel_network='" + self.network + "' AND \"user\"='" + kicker + "' AND user_network='" + self.network + "'"): # No records for kicker channel combination.
+        self.db.run("INSERT INTO kicks (channel, channel_network, \"user\", user_network) VALUES ('" + channel + "', '" + self.network + "', '" + kicker + "', '" + self.network + "')")
+    if not self.db.one("SELECT id FROM kicks WHERE channel='" + channel + "'AND channel_network='" + self.network + "' AND \"user\"='" + kicked + "' AND user_network='" + self.network + "'"): # No records for kicked channel combination.
+        self.db.run("INSERT INTO kicks (channel, channel_network, \"user\", user_network) VALUES ('" + channel + "', '" + self.network + "', '" + kicked + "', '" + self.network + "')")
+    
+    # Save statistic.
+    self.db.run("UPDATE kicks SET given = given + 1 WHERE channel='" + channel + "'AND channel_network='" + self.network + "' AND \"user\"='" + kicker + "' AND user_network='" + self.network + "'")
+    self.db.run("UPDATE kicks SET received = received + 1 WHERE channel='" + channel + "'AND channel_network='" + self.network + "' AND \"user\"='" + kicked + "' AND user_network='" + self.network + "'")
+    
+    # Update protectees if needed.
+    if channel == self.homechannel: # Kicked from home channel
+        if event.source.nick in self.protectees:    # Protectee kicked.
+            del self.protectees[event.source.nick]  # Remove old nick from list.
+    
+    # Do nothing more when user is not protected.
+    if not userstatus.atleast_halfop(self, kicked, self.homechannel) and not kicked == connection.get_nickname():
+        return
+    
+    # Report.
+    if not channel == self.homechannel: # Not kicked from homechannel.
+        if reason:
+            connection.privmsg(self.homechannel, red + kicked + reset + " has been kicked from " + red + channel + reset + " by " + red + kicker + reset + ": " + green + reason)
+        else:
+            connection.privmsg(self.homechannel, red + kicked + reset + " has been kicked from " + red + channel + reset + " by " + red + kicker + reset + ".")
+    
+    # React.
+    print("Reacting to kick")
+    behaviour = self.db.one("SELECT aggressiveness FROM channels WHERE name='" + channel + "' AND network='" + self.network + "'")
+    if behaviour == "passive":    # Passive behaviour.
+        if kicked == connection.get_nickname() and channel == self.homechannel: # Bot was kicked from it's home channel.
+            ChanServ.unban(connection, channel, kicked)
+            ChanServ.akick_del(connection, channel, kicked)
+            connection.privmsg("ChanServ", "UNBAN " + channel)
+            do_everything_to.join(self, connection, self.homechannel)
+    elif behaviour == "defense_only":   # Defensive behaviour.
+        ChanServ.unban(connection, channel, kicked)
+        ChanServ.akick_del(connection, channel, kicked)
+        if kicked == connection.get_nickname(): # Bot was kicked.
+            connection.privmsg("ChanServ", "UNBAN " + channel)
+            do_everything_to.join(self, connection, channel)
+    elif behaviour == "equal_retalliation":   # Equal retalitory behaviour.
+        ChanServ.akick_del(connection, channel, kicked)
+        
+        # Rejoin if bot was kicked.
+        if kicked == connection.get_nickname():
+            connection.privmsg("ChanServ", "UNBAN " + channel)
+            do_everything_to.join(self, connection, self.homechannel)
+        
+        if event.source.nick == connection.get_nickname() or self.channels[self.homechannel].is_owner(kicker):
+            return  # Stop if offender is bot or owner.
+        if not userstatus.atleast_halfop(self, kicked, self.homechannel) or not kicked == connection.get_nickname():
+            return  # Stop if offended is not atleast halfop and is not the bot itself.
+        if userstatus.atleast_halfop(self, kicker, self.homechannel) and not self.channels[self.homechannel].is_owner(kicked):
+            return  # Stop if offender is at least halfop in the home channel and the offended is not owner.
+        
+        # Kick.
+        do_everything_to.kick(connection, channel, kicker, Aggressiveness.retalliation_reason(self, connection, kicked, behaviour))
+    
+    # Battlebot behaviour.
+    elif behaviour == "battlebot":
+        ChanServ.akick_del(connection, channel, kicked)
+        
+        # Rejoin if bot was kicked.
+        if kicked == connection.get_nickname():
+            print("Rejoining " + channel)
+            do_everything_to.join(self, connection, channel)
+        
+        if event.source.nick == connection.get_nickname() or self.channels[self.homechannel].is_owner(kicker):
+            print("Stop if offender is bot or owner")
+            return  # Stop if offender is bot or owner.
+        if not userstatus.atleast_halfop(self, kicked, self.homechannel) or not kicked == connection.get_nickname():
+            print("Stop if offended is not atleast halfop and is not the bot itself")
+            return  # Stop if offended is not atleast halfop and is not the bot itself.
+        if userstatus.atleast_halfop(self, kicker, self.homechannel) and not self.channels[self.homechannel].is_owner(kicked):
+            print("Stop if offender is at least halfop in the home channel and the offended is not owner")
+            return  # Stop if offender is at least halfop in the home channel and the offended is not owner.
+        print("Kickbanning")
+
+        ChanServ.tempban(connection, channel, kicker, "1h", "Aggression channel function = equal_retalliation: " + kicked)
+        connection.mode(channel, "+e " + kicked)  # Excempt operator.
+        ChanServ.akick_add(connection, channel, kicker) # Add kicker to ChanServs autokick.
+        do_everything_to.bankick(connection, channel, kicker, event.source, "Aggression channel function = equal_retalliation: " + kicked)

+ 126 - 0
rotbot/events/on_mode.py

@@ -0,0 +1,126 @@
+import fnmatch
+from irc.modes import parse_channel_modes
+from common.networkservices import ChanServ
+from common import do_everything_to, userstatus, log
+from events.common import Aggressiveness, Lastact
+
+bold = "\x02"
+italic = "\x1D"
+underline = "\x1F"
+reverse = "\x16" 	# swap background and foreground colors ("reverse video")
+reset = "\x0F"
+blue = "\x0302"
+green = "\x0303"
+red = "\x0304"
+grey = "\x0314"
+
+def process_event(self, connection, event):
+    log.info(event)
+    
+    # Update protectees.
+    if event.target == self.homechannel:    # Home channel
+        if any(mode in event.arguments[0][-1:] for mode in ("q", "a", "o", "h")):  # Atleast halfop.
+            connection.who(event.arguments[1])  # Get whorepy to update protectees.
+    
+    # Update last act.
+    Lastact.update(self, event.source.nick, "mode", lastact=event.target)
+    
+    # React.
+    modes = parse_channel_modes(" ".join(event.arguments))
+    behaviour = self.db.one("SELECT aggressiveness FROM channels WHERE name='" + event.target + "' AND network='" + self.network + "'")
+    for idx, mode in enumerate(modes):
+        
+        # Report.
+        if not event.target == self.homechannel:    # Not in home channel.
+            for protectee in self.protectees:
+                if mode[1] == "b" and fnmatch.fnmatch(self.protectees[protectee]['ident'], mode[2]) and mode[0] == "+":   # Protectee banned.
+                    connection.privmsg(self.homechannel, red + protectee + reset + " banned from " + red + event.target + reset + " by " + red + event.source.nick + reset + ": " + green + mode[2])
+
+                if mode[1] == "e" and fnmatch.fnmatch(self.protectees[protectee]['ident'], event.arguments[idx + 1]) and mode[0] == "-":   # Protectee's exception removed.
+                    connection.privmsg(self.homechannel, red + protectee + reset + " has had their exception removed from " + red + event.target + reset + " by " + red + event.source.nick + reset + ": " + green + event.arguments[idx + 1])
+    
+        # Track channel keys.
+        if mode[1] == "k":  # Channel key changed.
+            if mode[0] == "+":  # Key set.
+                self.db.run("UPDATE channels SET key='" + mode[2] + "' WHERE name='" + event.target + "' AND network='" + self.network + "'")
+            else:   # Key removed.
+                self.db.run("UPDATE channels SET key=NULL WHERE name='" + event.target + "' AND network='" + self.network + "'")
+        
+        # Unban if bot is banned from home channel
+        try:
+            if mode[1] == "b" and fnmatch.fnmatch(self.protectees[connection.get_nickname()]['ident'], mode[2]) and mode[0] == "+" and event.target == self.homechannel:
+                connection.privmsg("ChanServ", "UNBAN")
+                do_everything_to.unban(connection, event.target, connection.get_nickname(), mode[2])
+                connection.mode(event.target, "-b " + mode[2])
+        except:
+            pass
+        
+        # Stop if not present in home channel.
+        if not self.homechannel in self.channels:
+            return
+        
+        # Stop if offender is bot or bot owner
+        if event.source.nick == connection.get_nickname() or self.channels[self.homechannel].is_owner(event.source.nick):
+            return
+        
+        for protectee in self.protectees:
+            
+            # Stop if offender is atleast halfop in the home channel and offended is not owner.
+            if userstatus.atleast_halfop(self, event.source.nick, self.homechannel) and not self.channels[self.homechannel].is_owner(protectee):
+                return
+            
+            try:    # Had "IndexError: list index out of range" on "if modes[1] == "b" and mode[0] == "+":  # Ban.".
+                if behaviour == "passive":    # Passive behaviour.
+                    return
+                elif behaviour == "defense_only":   # Defensive only behaviour.
+                    if mode[1] == "b" and mode[0] == "+":  # Ban.
+                        if fnmatch.fnmatch(self.protectees[protectee]['ident'], mode[2]):  # Protectee.
+                            do_everything_to.unban(connection, event.target, protectee, mode[2])
+                    elif mode[1] == "e" and mode[0] == "-":  # Removed exception.
+                        if fnmatch.fnmatch(self.protectees[protectee]['ident'], event.arguments[idx + 1]):  # Protectee.
+                            connection.mode(event.target, "+e " + mode[2])
+                    elif mode[0] == "-" and mode[1] in ["q", "a", "o", "h", "v"]:   # Channel right taken.
+                        ChanServ.give_mode(connection, event.target, mode[2], mode[1])
+                        connection.mode(event.target, mode[0] + mode[1] + " " + mode[2])
+                elif behaviour == "equal_retalliation":   # Equal retaliatory behaviour.
+                    if modes[1] == "b" and mode[0] == "+":  # Ban.
+                        if fnmatch.fnmatch(self.protectees[protectee]['ident'], mode[2]):  # Protectee.
+                            do_everything_to.unban(connection, event.target, protectee, mode[2])
+                            if protectee == connection.get_nickname():  # Bot banned.
+                                ChanServ.ban(connection, event.target, event.source.nick, "Aggression channel function = equal_retalliation.")
+                            else:
+                                ChanServ.ban(connection, event.target, event.source.nick, "Aggression channel function = equal_retalliation: " + protectee + " is an operator of " + connection.get_nickname() + ".")
+                            connection.mode(event.target, "+b " + event.source)
+                    elif mode[1] == "e" and mode[0] == "-":  # Removed exception.
+                        if fnmatch.fnmatch(self.protectees[protectee]['ident'], event.arguments[idx + 1]):  # Protectee.
+                            connection.mode(event.target, "+e " + event.arguments[idx + 1])
+                    elif mode[0] == "-" and mode[1] in ["q", "a", "o", "h", "v"]:   # Channel right taken.
+                        ChanServ.give_mode(connection, event.target, mode[2], mode[1])
+                        connection.mode(event.target, mode[0] + mode[1] + " " + mode[2])
+                        ChanServ.take_all_modes(connection, event.target, mode[2])
+                        connection.mode(event.target, "-vhoaq " + mode[2] + " " + mode[2]+ " " + mode[2]+ " " + mode[2]+ " " + mode[2])
+                elif behaviour == "battlebot":  # Battlebot behaviour.
+                    if mode[0] == "-" and mode[1] in ["q", "a", "o", "h", "v"]:   # Channel right taken.
+                        ChanServ.give_mode(connection, event.target, mode[2], mode[1])
+                        connection.mode(event.target, mode[0] + mode[1] + " " + mode[2])
+                        do_everything_to.unban(connection, event.target, protectee, mode[2])
+                        do_everything_to.ban(connection, event.target, event.source.nick, event.source, Aggressiveness.retalliation_reason(self, connection, protectee, behaviour))
+                        connection.mode(event.target, "+e " + mode[2])
+                        ChanServ.akick_add(connection, event.target, event.source.nick)
+                        do_everything_to.kick(connection, event.target, event.source.nick, Aggressiveness.retalliation_reason(self, connection, protectee, behaviour))
+                    if mode[1] == "b" and mode[0] == "+":  # Ban.
+                        if fnmatch.fnmatch(self.protectees[protectee]['ident'], mode[2]):  # Protectee.
+                            do_everything_to.unban(connection, event.target, protectee, mode[2])
+                            do_everything_to.ban(connection, event.target, event.source.nick, event.source, Aggressiveness.retalliation_reason(self, connection, protectee, behaviour))
+                            connection.mode(event.target, "+e " + mode[2])
+                            ChanServ.akick_add(connection, event.target, event.source.nick)
+                            do_everything_to.kick(connection, event.target, event.source.nick, Aggressiveness.retalliation_reason(self, connection, protectee, behaviour))
+                    elif mode[1] == "e" and mode[0] == "-":  # Removed exception.
+                        #for protectee in self.protectees:
+                        if fnmatch.fnmatch(self.protectees[protectee]['ident'], event.arguments[idx + 1]):  # Protectee.
+                            do_everything_to.ban(connection, event.target, event.source.nick, event.source, Aggressiveness.retalliation_reason(self, connection, protectee, behaviour))
+                            connection.mode(event.target, "+e " + event.arguments[idx + 1])
+                            ChanServ.akick_add(connection, event.target, event.source.nick)
+                            do_everything_to.kick(connection, event.target, event.source.nick, Aggressiveness.retalliation_reason(self, connection, protectee, behaviour))
+            except:
+                pass

+ 20 - 0
rotbot/events/on_nick.py

@@ -0,0 +1,20 @@
+from common import log
+from common.networkservices import NickServ
+from events.common import Lastact
+
+def process_event(self, connection, event):
+    log.info(event)
+    
+    # Keep preferred nick.
+    if event.source.nick == connection.nickname:    # Preffered nick being changed.
+        log.info("Assuming original nick.")
+        NickServ.recover_nick(connection, self.password)
+    
+    # Update protectees.
+    if event.source.nick in self.protectees:    # Protectee chaning nick.
+        del self.protectees[event.source.nick]  # Remove old nick from list.
+        connection.who(event.target)    # Get whorepy to add new nick to protectees.
+    
+    # Update last act.
+    Lastact.update(self, event.source.nick, "nick", lastact=event.target)
+    

+ 36 - 0
rotbot/events/on_pubmsg.py

@@ -0,0 +1,36 @@
+import datetime
+from events.common import Replyto,  Lastact, MessageStatistics
+
+def process_event(self, connection, event):
+    
+    # Update last act.
+    Lastact.update(self, event.source.nick, "msg", channel=event.target, lastact=event.arguments[0])
+    
+    # Save statistic to database.
+    MessageStatistics.update(self, event, "message")
+    
+    # Stop if channelfunction chat if off.
+    if not self.db.one("SELECT chat FROM channels WHERE name='" + event.target + "' AND network='" + self.network + "'"):
+        return
+    
+    if connection.get_nickname().lower() in event.arguments[0].lower() and event.source.nick is not connection.get_nickname(): # Bot's name was mentioned
+        if event.arguments[0].startswith(self.cmdchar):
+            return  # Stop if it's a command.
+        Replyto.name(connection, event)
+    
+    # Character lame.
+    elif event.arguments[0] == len(event.arguments[0]) * event.arguments[0][0]:   # Trigger exclusively same character.
+    
+        # Do not say KKK.
+        if event.arguments[0] == "kk":
+            return
+        
+        # Stop if lamed recently.
+        lastlame = self.db.one("SELECT last_lame FROM channels WHERE name='" + event.target + "' AND network='" + self.network + "'")
+        if lastlame and lastlame > datetime.datetime.now() - datetime.timedelta(minutes=2):    # In the last 2 minutes.
+            return
+        
+        # Update lastlame.
+        self.db.run("UPDATE channels SET last_lame='" + str(datetime.datetime.now()) + "' WHERE name='" + event.target + "' AND network='" + self.network + "'")
+        
+        connection.privmsg(event.target, event.arguments[0] + event.arguments[0][:1])

+ 11 - 0
rotbot/events/on_welcome.py

@@ -0,0 +1,11 @@
+from common import log, do_everything_to
+
+def process_event(self, connection, event):
+    log.info(event)    # Handy for debugging. Keep this.
+    if self.password:   # Id with NickServ
+        connection.privmsg("NickServ", "identify " + self.password) # Identify with NickServ.
+    channels = self.db.all("SELECT name FROM channels WHERE network='" + self.network + "' AND autojoin=True")
+    connection.mode(connection.get_nickname(), "+x")
+    do_everything_to.join(self, connection, self.homechannel)
+    for channel in channels:    # Join channels with autojoin function.
+        connection.join(channel)

+ 4 - 0
rotbot/events/on_whoreply.py

@@ -0,0 +1,4 @@
+from events.common import Protectees
+
+def process_event(self, connection, event):
+    Protectees.update(self,  event.arguments[4], event.arguments[1], event.arguments[2])

+ 22 - 0
semantic.json

@@ -0,0 +1,22 @@
+{
+  "base": "website/semantic",
+  "paths": {
+    "source": {
+      "config": "src/theme.config",
+      "definitions": "src/definitions/",
+      "site": "src/site/",
+      "themes": "src/themes/"
+    },
+    "output": {
+      "packaged": "dist/",
+      "uncompressed": "dist/components/",
+      "compressed": "dist/components/",
+      "themes": "dist/themes/"
+    },
+    "clean": "dist/"
+  },
+  "permission": false,
+  "autoInstall": false,
+  "rtl": false,
+  "version": "2.7.8"
+}

+ 0 - 0
website/core/__init__.py


+ 3 - 0
website/core/admin.py

@@ -0,0 +1,3 @@
+from django.contrib import admin
+
+# Register your models here.

+ 5 - 0
website/core/apps.py

@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+
+class CoreConfig(AppConfig):
+    name = 'core'

+ 3 - 0
website/core/models.py

@@ -0,0 +1,3 @@
+from django.db import models
+
+# Create your models here.

+ 12 - 0
website/core/templates/core/index.html

@@ -0,0 +1,12 @@
+{% extends "base.html" %}
+{% block content %}
+  {% if 'knowledgebase.apps.KnowledgebaseConfig' in settings.INSTALLED_APPS %}
+    <a href="{% url 'knowledgebase:index'  %}"><i class="newspaper outline icon"></i>Knowledgebase</a><br />
+  {% endif %}
+  {% if 'dancecalendar.apps.DancecalendarConfig' in settings.INSTALLED_APPS %}
+    <a href="{% url 'dancecalendar:index'  %}"><i class="calendar alternate icon"></i>Dance calendar</a><br />
+  {% endif %}
+  {% if 'rotbot.apps.RotbotConfig' in settings.INSTALLED_APPS %}
+    <a href=""><i class=" icon"></i>RotBot</a><br />
+  {% endif %}
+{% endblock %}

+ 28 - 0
website/core/templates/core/login.html

@@ -0,0 +1,28 @@
+{% extends "base.html" %}
+{% block content %}
+  {% if next %}
+    {% if user.is_authenticated %}
+      <p>Your account doesn't have access to this page. To proceed, please login with an account that has access.</p>
+    {% else %}
+      <p>Please login to see this page.</p>
+    {% endif %}
+  {% endif %}
+<div class="ui compact inverted segment">
+  <form class= "ui form" method="post" action="{% url 'core:login' %}">
+    {% csrf_token %}
+    <div class="required field">
+      <label id="username" for="id_username"><i class="user icon"></i>Username:</label>
+      {{ form.username }}
+    </div>
+    <div class="required field">
+      <label for="id_username"><i class="key icon"></i>Password:</label>
+      {{ form.password }}
+    </div>
+    {% if form.errors %}
+      <div class="ui inverted orange message">Username and password didn't match.</div>
+    {% endif %}
+    <button class="ui button" type="submit"><i class="door open icon"></i>Log in</button>
+    <input type="hidden" name="next" value="{{ next }}">
+  </form>
+</div>
+{% endblock %}

+ 3 - 0
website/core/tests.py

@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.

+ 38 - 0
website/core/urls.py

@@ -0,0 +1,38 @@
+from django.urls import path, include
+from django.contrib.auth import views as auth_views
+
+from website.settings import APPLICATION_NAME
+#from . import views
+
+app_name = 'core'
+urlpatterns = [
+    #path('', views.index, name='index'),
+    #url(r'^login/$', auth_views.LoginView.as_view(extra_context= { 'all_user': User.objects.all() },redirect_authenticated_user=True),name='login')
+
+    path(
+        'login/',
+        auth_views.LoginView.as_view(
+            template_name='core/login.html',
+            extra_context = {
+                'title': 'Log in',
+                'icon': 'sign in alternate',
+                'description': 'Authenticate with ' + APPLICATION_NAME,
+                'keywords': 'authenticate, login, logon, signin, signon, log in, log on, sign in, sign on',
+            },
+        ),
+        name='login',
+    ),
+    path(
+        'logout/',
+        auth_views.LogoutView.as_view(
+#            template_name='core/logout.html',
+#            extra_context = {
+#                'title': 'Logout',
+#                'icon': 'sign out alternate',
+#                'description': 'End authenticated session with ' + APPLICATION_NAME,
+#                'keywords': 'authenticate, logout, logoff, signout, signoff, log out, log off, sign out, sign off',
+#            },
+        ),
+        name='logout',
+    ),
+]

+ 30 - 0
website/core/views.py

@@ -0,0 +1,30 @@
+from django.shortcuts import render
+from django.contrib.auth import authenticate, login, logout
+
+from website.settings import APPLICATION_NAME
+
+# Create your views here.
+def index(request):
+    context = {
+        'title': 'Index',
+        'icon': 'home',
+        'description': 'Index of ' + APPLICATION_NAME,
+        'keywords': 'index',
+    }
+    return render(request, 'core/index.html', context)
+
+def login(request):
+    username = request.POST['username']
+    password = request.POST['password']
+    user = authenticate(request, username=username, password=password)
+    if user is not None:
+        login(request, user)
+        # Redirect to a success page.
+        ...
+    else:
+        # Return an 'invalid login' error message.
+        ...
+
+def logout_view(request):
+    logout(request)
+    # Redirect to a success page.

+ 0 - 0
website/dancecalendar/__init__.py


+ 3 - 0
website/dancecalendar/admin.py

@@ -0,0 +1,3 @@
+from django.contrib import admin
+
+# Register your models here.

+ 5 - 0
website/dancecalendar/apps.py

@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+
+class DancecalendarConfig(AppConfig):
+    name = 'dancecalendar'

+ 123 - 0
website/dancecalendar/models.py

@@ -0,0 +1,123 @@
+from django.db import models
+
+# Create your models here.
+
+class Party(models.Model):
+    name = models.CharField(
+        max_length=50,
+        #unique=True,
+    )
+    description = models.CharField(
+        null=True,
+        blank=True,
+        max_length=200,
+    )
+    location = models.ForeignKey(
+        'Location',
+        on_delete=models.PROTECT,
+    )
+    doors_open = models.DateTimeField(
+        null=True,
+        blank=True,
+    )
+    doors_close = models.DateTimeField(
+        null=True,
+        blank=True,
+    )
+    price = models.DecimalField(
+        decimal_places=2,
+        max_digits=5,
+    )
+    wardrobe = models.DecimalField(
+        null=True,
+        default=None,
+        decimal_places=2,
+        max_digits=4,
+    )
+    wardrobe_guarded = models.BooleanField(
+        default=False
+    )
+    dresscode = models.CharField(
+        max_length=50,
+    )
+    area = models.ForeignKey(
+        'Area',
+        on_delete=models.CASCADE,
+    )
+
+class Location(models.Model):
+    name = models.CharField(
+        max_length=50,
+    )
+    venue_type = models.CharField(
+        max_length=30,
+    )
+    street = models.CharField(
+        max_length=50,
+    )
+    housenumber = models.CharField(
+        max_length=10,
+    )
+    city = models.CharField(
+        max_length=30,
+    )
+    areacode = models.CharField(
+        null=True,
+        blank=True,
+        max_length=10
+    )
+    free_parking = models.PositiveSmallIntegerField(
+        default=False,
+    )
+    class Meta:
+        #db_table = 'app_version'
+        constraints = [
+            models.UniqueConstraint(fields=['street', 'housenumber'], name='unique location')
+        ]
+
+class Area(models.Model):
+    name = models.CharField(
+        max_length=50,
+        default='Main',
+    )
+    activity = models.ForeignKey(
+        'Activity',
+        on_delete=models.CASCADE,
+    )
+
+class Activity(models.Model):
+    name = models.CharField(
+        null=True,
+        blank=True,
+        max_length=50,
+        default='Social',
+    )
+    description = models.CharField(
+        null=True,
+        blank=True,
+        max_length=200,
+    )
+    TYPE_CHOICES = [
+        ('Dance party',
+            (
+                ('sc', 'Social'),
+                ('gl', 'Gala'),
+            )
+        ),
+        ('ws', 'Workshop'),
+        ('sh', 'Show'),
+        ('br', 'Break'),
+        ('ot', 'Other'),
+    ]
+    type = models.CharField(
+        max_length=2,
+        choices=TYPE_CHOICES,
+        default='sc',
+    )
+    start = models.DateTimeField()
+    end = models.DateTimeField()
+    artist = models.CharField(
+        null=True,
+        blank=True,
+        max_length=50,
+    )

+ 4 - 0
website/dancecalendar/templates/dancecalendar/index.html

@@ -0,0 +1,4 @@
+{% extends "base.html" %}
+{% block content %}
+  dancecalendar
+{% endblock %}

+ 3 - 0
website/dancecalendar/tests.py

@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.

+ 8 - 0
website/dancecalendar/urls.py

@@ -0,0 +1,8 @@
+from django.urls import path
+
+from . import views
+
+app_name = 'dancecalendar'
+urlpatterns = [
+    path('', views.index, name='index'),
+]

+ 12 - 0
website/dancecalendar/views.py

@@ -0,0 +1,12 @@
+from django.shortcuts import render
+
+# Create your views here.
+
+def index(request):
+    context = {
+        'title': 'Dance calendar',
+        'icon': 'calendar alternate outline',
+        'description': 'Dance parties',
+        'keywords': 'Dance calendar',
+    }
+    return render(request, 'dancecalendar/index.html', context)

+ 10 - 0
website/knowledgebase/models.py

@@ -46,6 +46,16 @@ class Article(models.Model):
 
     description = models.TextField()
 
+    keywords = models.CharField(
+        max_length=50,
+    )
+
+    published = models.BooleanField(
+        default = False,
+    )
+
+    content = models.TextField()
+
 
     def __str__(self):
         return self.title

+ 39 - 1
website/knowledgebase/templates/knowledgebase/article.html

@@ -1 +1,39 @@
-{{ article }}
+{% extends "base.html" %}
+{% block content %}
+  <h1>{{ article.title }}</h1>
+  <a href="{% url 'knowledgebase:edit-article' article_slug=article.slug %}" class="ui right floated button">Edit</a>
+  {{ article.description }}
+  <div class="segment">
+  Category: {{ category }} | Created: {{ article.created }} {% if article.created != article.updated %}| Updated: {{ article.updated }} {% endif %}
+  </div>
+  <div class="ui inverted divider"></div>
+  {% autoescape off %}{{ article.content }}{% endautoescape %}
+{% endblock content %}
+
+
+
+
+
+<section class="ui stackable cards">
+    <figure class="PhotoCard ui card">
+        <img src="http://via.placeholder.com/300x250" class="ui fluid image">
+        <figcaption class="content">
+            Cool pics
+        </figcaption>
+    </figure>
+</section>
+<script>
+    function deleteModal() {
+        $('.ui.basic.modal').each(function() {
+            $(this).remove();
+        });
+    }
+    $('.PhotoCard').click(function() {
+        // Delete any modals hanging around
+        deleteModal();
+        var image = $(this).children('img').attr('src');
+        $('body').append('<div class="ui basic modal"><div class="content"><img src="'+image+'" width="100%" /></div></div>');
+        $('.ui.basic.modal')
+            .modal('show');
+    })
+</script>

+ 11 - 0
website/knowledgebase/templates/knowledgebase/edit_article.html

@@ -0,0 +1,11 @@
+{% extends "base.html" %}
+{% block content %}
+  <h1>{{ article.title }}</h1>
+  <button class="ui right floated button">Save</button>
+  {{ article.description }}
+  <div class="segment">
+  Category: {{ category }}
+  </div>
+  <div class="ui inverted divider"></div>
+  {% autoescape off %}{{ article.content }}{% endautoescape %}
+{% endblock content %}

+ 10 - 4
website/knowledgebase/templates/knowledgebase/index.html

@@ -1,12 +1,18 @@
 {% extends "base.html" %}
 {% block content %}
   {% if latest_article_list %}
-    <ul>
+    <div class="ui inverted relaxed divided selection list">
       {% for article in latest_article_list %}
-        <li><a href="{% url 'knowledgebase:article' article.slug %}">{{ article.title }}</a></li>
+        <div class="item" onclick="location.href='{% url 'knowledgebase:article' article.slug %}';">
+          <div class="right floated content">
+            <div class="ui inverted basic tiny black right ribbon label"><i class="{{ article.icon }} icon"></i> {{ article.category }} </div>
+          </div>
+          <a class ="header" href="{% url 'knowledgebase:article' article.slug %}">{{ article.title }}</a>
+          <div class="description">{{ article.description }}</div>
+        </div>
       {% endfor %}
-    </ul>
+    </div>
   {% else %}
-    <<p>No articles available.</p>
+    <p>No articles available.</p>
   {% endif %}
 {% endblock %}

+ 1 - 0
website/knowledgebase/urls.py

@@ -6,4 +6,5 @@ app_name = 'knowledgebase'
 urlpatterns = [
     path('', views.index, name='index'),
     path('<str:article_slug>/', views.article, name='article'),
+    path('<str:article_slug>/edit', views.edit_article, name='edit-article'),
 ]

+ 48 - 3
website/knowledgebase/views.py

@@ -5,14 +5,59 @@ from django.shortcuts import render, get_object_or_404
 from .models import Article
 
 def index(request):
-    latest_article_list = Article.objects.order_by('-created')[:5]
+    latest_article_list = Article.objects.filter(published=True).order_by('-updated')[:5]
+    for article in latest_article_list:
+        article = article_decoration(article)
     context = {
+        'parent_title': 'Knowledgebase',
+        'parent_url': 'knowledgebase:index',
+        'parent_icon': 'newspaper outline',
+        'title': 'Knowledgebase',
+        'icon': 'list',
+        'description': 'Index of knowledgebase articles.',
+        'keywords': 'knowledgebase, articles, index',
         'latest_article_list': latest_article_list,
-        'title': 'FOOBAR',
     }
     #output = ', '.join([article.title for article in latest_article_list])
     return render(request, 'knowledgebase/index.html', context)
 
 def article(request, article_slug):
     article = get_object_or_404(Article, slug=article_slug)
-    return render(request, 'knowledgebase/article.html', {'article': article_slug})
+    article = article_decoration(article)
+    context = {
+        'parent_title': 'Knowledgebase',
+        'parent_url': 'knowledgebase:index',
+        'parent_icon': 'newspaper outline',
+        'title': article.title,
+        'icon': 'file alternate outline',
+        'description': article.description,
+        'keywords': article.keywords,
+        'article': article,
+        'category': article.category,
+    }
+    return render(request, 'knowledgebase/article.html', context)
+
+def edit_article(request, article_slug):
+    article = get_object_or_404(Article, slug=article_slug)
+    article = article_decoration(article)
+    context = {
+        'parent_title': 'Knowledgebase',
+        'parent_url': 'knowledgebase:index',
+        'parent_icon': 'newspaper outline',
+        'title': 'Edit ' + article.title,
+        'icon': 'file alternate outline',
+        'description': article.description,
+        'keywords': article.keywords,
+        'article': article,
+        'category': article.category,
+    }
+    return render(request, 'knowledgebase/edit_article.html', context)
+
+def article_decoration(article):
+    if article.category == 'ot':
+        article.category = 'Other'
+        article.icon = 'archive'
+    else:
+        article.category = 'Unkown'
+        article.icon = 'question'
+    return article

+ 0 - 0
website/rotbot/__init__.py


+ 3 - 0
website/rotbot/admin.py

@@ -0,0 +1,3 @@
+from django.contrib import admin
+
+# Register your models here.

+ 5 - 0
website/rotbot/apps.py

@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+
+class RotbotConfig(AppConfig):
+    name = 'rotbot'

+ 48 - 0
website/rotbot/models.py

@@ -0,0 +1,48 @@
+from django.db import models
+
+# Create your models here.
+class Network(models.Model):
+    name = models.CharField(
+        max_length=40,
+        unique=True,
+    )
+    nickname = models.CharField(
+        max_length=31,
+        default='RotBot',
+    )
+    username = models.CharField(
+        max_length=31,
+        default='pyRot',
+    )
+    password = models.CharField(
+        null=True,
+        blank=True,
+        max_length=31,
+    )
+    home_channel = models.CharField(
+        max_length=64,
+        default='#RotBot',
+    )
+    command_character = models.CharField(
+        max_length=1,
+        default='!'
+    )
+    help_character = models.CharField(
+        max_length=1,
+        default='@'
+    )
+
+class Host(models.Model):
+    network = models.ForeignKey(
+        'Network',
+    )
+    address = models.CharField(
+        max_length=60,
+        unique=True,
+    )
+    port = models.PositiveSmallIntegerField(
+         =
+    )
+    ssl = models.BooleanField(
+        default=True,
+    )

+ 3 - 0
website/rotbot/tests.py

@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.

+ 3 - 0
website/rotbot/views.py

@@ -0,0 +1,3 @@
+from django.shortcuts import render
+
+# Create your views here.

Разлика између датотеке није приказан због своје велике величине
+ 221 - 0
website/semantic/dist/components/accordion.css


+ 618 - 0
website/semantic/dist/components/accordion.js

@@ -0,0 +1,618 @@
+/*!
+ * # Fomantic-UI - Accordion
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */
+
+;(function ($, window, document, undefined) {
+
+'use strict';
+
+$.isFunction = $.isFunction || function(obj) {
+  return typeof obj === "function" && typeof obj.nodeType !== "number";
+};
+
+window = (typeof window != 'undefined' && window.Math == Math)
+  ? window
+  : (typeof self != 'undefined' && self.Math == Math)
+    ? self
+    : Function('return this')()
+;
+
+$.fn.accordion = function(parameters) {
+  var
+    $allModules     = $(this),
+
+    time            = new Date().getTime(),
+    performance     = [],
+
+    query           = arguments[0],
+    methodInvoked   = (typeof query == 'string'),
+    queryArguments  = [].slice.call(arguments, 1),
+
+    returnedValue
+  ;
+  $allModules
+    .each(function() {
+      var
+        settings        = ( $.isPlainObject(parameters) )
+          ? $.extend(true, {}, $.fn.accordion.settings, parameters)
+          : $.extend({}, $.fn.accordion.settings),
+
+        className       = settings.className,
+        namespace       = settings.namespace,
+        selector        = settings.selector,
+        error           = settings.error,
+
+        eventNamespace  = '.' + namespace,
+        moduleNamespace = 'module-' + namespace,
+        moduleSelector  = $allModules.selector || '',
+
+        $module  = $(this),
+        $title   = $module.find(selector.title),
+        $content = $module.find(selector.content),
+
+        element  = this,
+        instance = $module.data(moduleNamespace),
+        observer,
+        module
+      ;
+
+      module = {
+
+        initialize: function() {
+          module.debug('Initializing', $module);
+          module.bind.events();
+          if(settings.observeChanges) {
+            module.observeChanges();
+          }
+          module.instantiate();
+        },
+
+        instantiate: function() {
+          instance = module;
+          $module
+            .data(moduleNamespace, module)
+          ;
+        },
+
+        destroy: function() {
+          module.debug('Destroying previous instance', $module);
+          $module
+            .off(eventNamespace)
+            .removeData(moduleNamespace)
+          ;
+        },
+
+        refresh: function() {
+          $title   = $module.find(selector.title);
+          $content = $module.find(selector.content);
+        },
+
+        observeChanges: function() {
+          if('MutationObserver' in window) {
+            observer = new MutationObserver(function(mutations) {
+              module.debug('DOM tree modified, updating selector cache');
+              module.refresh();
+            });
+            observer.observe(element, {
+              childList : true,
+              subtree   : true
+            });
+            module.debug('Setting up mutation observer', observer);
+          }
+        },
+
+        bind: {
+          events: function() {
+            module.debug('Binding delegated events');
+            $module
+              .on(settings.on + eventNamespace, selector.trigger, module.event.click)
+            ;
+          }
+        },
+
+        event: {
+          click: function() {
+            module.toggle.call(this);
+          }
+        },
+
+        toggle: function(query) {
+          var
+            $activeTitle = (query !== undefined)
+              ? (typeof query === 'number')
+                ? $title.eq(query)
+                : $(query).closest(selector.title)
+              : $(this).closest(selector.title),
+            $activeContent = $activeTitle.next($content),
+            isAnimating = $activeContent.hasClass(className.animating),
+            isActive    = $activeContent.hasClass(className.active),
+            isOpen      = (isActive && !isAnimating),
+            isOpening   = (!isActive && isAnimating)
+          ;
+          module.debug('Toggling visibility of content', $activeTitle);
+          if(isOpen || isOpening) {
+            if(settings.collapsible) {
+              module.close.call($activeTitle);
+            }
+            else {
+              module.debug('Cannot close accordion content collapsing is disabled');
+            }
+          }
+          else {
+            module.open.call($activeTitle);
+          }
+        },
+
+        open: function(query) {
+          var
+            $activeTitle = (query !== undefined)
+              ? (typeof query === 'number')
+                ? $title.eq(query)
+                : $(query).closest(selector.title)
+              : $(this).closest(selector.title),
+            $activeContent = $activeTitle.next($content),
+            isAnimating = $activeContent.hasClass(className.animating),
+            isActive    = $activeContent.hasClass(className.active),
+            isOpen      = (isActive || isAnimating)
+          ;
+          if(isOpen) {
+            module.debug('Accordion already open, skipping', $activeContent);
+            return;
+          }
+          module.debug('Opening accordion content', $activeTitle);
+          settings.onOpening.call($activeContent);
+          settings.onChanging.call($activeContent);
+          if(settings.exclusive) {
+            module.closeOthers.call($activeTitle);
+          }
+          $activeTitle
+            .addClass(className.active)
+          ;
+          $activeContent
+            .stop(true, true)
+            .addClass(className.animating)
+          ;
+          if(settings.animateChildren) {
+            if($.fn.transition !== undefined && $module.transition('is supported')) {
+              $activeContent
+                .children()
+                  .transition({
+                    animation        : 'fade in',
+                    queue            : false,
+                    useFailSafe      : true,
+                    debug            : settings.debug,
+                    verbose          : settings.verbose,
+                    duration         : settings.duration,
+                    skipInlineHidden : true,
+                    onComplete: function() {
+                      $activeContent.children().removeClass(className.transition);
+                    }
+                  })
+              ;
+            }
+            else {
+              $activeContent
+                .children()
+                  .stop(true, true)
+                  .animate({
+                    opacity: 1
+                  }, settings.duration, module.resetOpacity)
+              ;
+            }
+          }
+          $activeContent
+            .slideDown(settings.duration, settings.easing, function() {
+              $activeContent
+                .removeClass(className.animating)
+                .addClass(className.active)
+              ;
+              module.reset.display.call(this);
+              settings.onOpen.call(this);
+              settings.onChange.call(this);
+            })
+          ;
+        },
+
+        close: function(query) {
+          var
+            $activeTitle = (query !== undefined)
+              ? (typeof query === 'number')
+                ? $title.eq(query)
+                : $(query).closest(selector.title)
+              : $(this).closest(selector.title),
+            $activeContent = $activeTitle.next($content),
+            isAnimating    = $activeContent.hasClass(className.animating),
+            isActive       = $activeContent.hasClass(className.active),
+            isOpening      = (!isActive && isAnimating),
+            isClosing      = (isActive && isAnimating)
+          ;
+          if((isActive || isOpening) && !isClosing) {
+            module.debug('Closing accordion content', $activeContent);
+            settings.onClosing.call($activeContent);
+            settings.onChanging.call($activeContent);
+            $activeTitle
+              .removeClass(className.active)
+            ;
+            $activeContent
+              .stop(true, true)
+              .addClass(className.animating)
+            ;
+            if(settings.animateChildren) {
+              if($.fn.transition !== undefined && $module.transition('is supported')) {
+                $activeContent
+                  .children()
+                    .transition({
+                      animation        : 'fade out',
+                      queue            : false,
+                      useFailSafe      : true,
+                      debug            : settings.debug,
+                      verbose          : settings.verbose,
+                      duration         : settings.duration,
+                      skipInlineHidden : true
+                    })
+                ;
+              }
+              else {
+                $activeContent
+                  .children()
+                    .stop(true, true)
+                    .animate({
+                      opacity: 0
+                    }, settings.duration, module.resetOpacity)
+                ;
+              }
+            }
+            $activeContent
+              .slideUp(settings.duration, settings.easing, function() {
+                $activeContent
+                  .removeClass(className.animating)
+                  .removeClass(className.active)
+                ;
+                module.reset.display.call(this);
+                settings.onClose.call(this);
+                settings.onChange.call(this);
+              })
+            ;
+          }
+        },
+
+        closeOthers: function(index) {
+          var
+            $activeTitle = (index !== undefined)
+              ? $title.eq(index)
+              : $(this).closest(selector.title),
+            $parentTitles    = $activeTitle.parents(selector.content).prev(selector.title),
+            $activeAccordion = $activeTitle.closest(selector.accordion),
+            activeSelector   = selector.title + '.' + className.active + ':visible',
+            activeContent    = selector.content + '.' + className.active + ':visible',
+            $openTitles,
+            $nestedTitles,
+            $openContents
+          ;
+          if(settings.closeNested) {
+            $openTitles   = $activeAccordion.find(activeSelector).not($parentTitles);
+            $openContents = $openTitles.next($content);
+          }
+          else {
+            $openTitles   = $activeAccordion.find(activeSelector).not($parentTitles);
+            $nestedTitles = $activeAccordion.find(activeContent).find(activeSelector).not($parentTitles);
+            $openTitles   = $openTitles.not($nestedTitles);
+            $openContents = $openTitles.next($content);
+          }
+          if( ($openTitles.length > 0) ) {
+            module.debug('Exclusive enabled, closing other content', $openTitles);
+            $openTitles
+              .removeClass(className.active)
+            ;
+            $openContents
+              .removeClass(className.animating)
+              .stop(true, true)
+            ;
+            if(settings.animateChildren) {
+              if($.fn.transition !== undefined && $module.transition('is supported')) {
+                $openContents
+                  .children()
+                    .transition({
+                      animation        : 'fade out',
+                      useFailSafe      : true,
+                      debug            : settings.debug,
+                      verbose          : settings.verbose,
+                      duration         : settings.duration,
+                      skipInlineHidden : true
+                    })
+                ;
+              }
+              else {
+                $openContents
+                  .children()
+                    .stop(true, true)
+                    .animate({
+                      opacity: 0
+                    }, settings.duration, module.resetOpacity)
+                ;
+              }
+            }
+            $openContents
+              .slideUp(settings.duration , settings.easing, function() {
+                $(this).removeClass(className.active);
+                module.reset.display.call(this);
+              })
+            ;
+          }
+        },
+
+        reset: {
+
+          display: function() {
+            module.verbose('Removing inline display from element', this);
+            $(this).css('display', '');
+            if( $(this).attr('style') === '') {
+              $(this)
+                .attr('style', '')
+                .removeAttr('style')
+              ;
+            }
+          },
+
+          opacity: function() {
+            module.verbose('Removing inline opacity from element', this);
+            $(this).css('opacity', '');
+            if( $(this).attr('style') === '') {
+              $(this)
+                .attr('style', '')
+                .removeAttr('style')
+              ;
+            }
+          },
+
+        },
+
+        setting: function(name, value) {
+          module.debug('Changing setting', name, value);
+          if( $.isPlainObject(name) ) {
+            $.extend(true, settings, name);
+          }
+          else if(value !== undefined) {
+            if($.isPlainObject(settings[name])) {
+              $.extend(true, settings[name], value);
+            }
+            else {
+              settings[name] = value;
+            }
+          }
+          else {
+            return settings[name];
+          }
+        },
+        internal: function(name, value) {
+          module.debug('Changing internal', name, value);
+          if(value !== undefined) {
+            if( $.isPlainObject(name) ) {
+              $.extend(true, module, name);
+            }
+            else {
+              module[name] = value;
+            }
+          }
+          else {
+            return module[name];
+          }
+        },
+        debug: function() {
+          if(!settings.silent && settings.debug) {
+            if(settings.performance) {
+              module.performance.log(arguments);
+            }
+            else {
+              module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
+              module.debug.apply(console, arguments);
+            }
+          }
+        },
+        verbose: function() {
+          if(!settings.silent && settings.verbose && settings.debug) {
+            if(settings.performance) {
+              module.performance.log(arguments);
+            }
+            else {
+              module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
+              module.verbose.apply(console, arguments);
+            }
+          }
+        },
+        error: function() {
+          if(!settings.silent) {
+            module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
+            module.error.apply(console, arguments);
+          }
+        },
+        performance: {
+          log: function(message) {
+            var
+              currentTime,
+              executionTime,
+              previousTime
+            ;
+            if(settings.performance) {
+              currentTime   = new Date().getTime();
+              previousTime  = time || currentTime;
+              executionTime = currentTime - previousTime;
+              time          = currentTime;
+              performance.push({
+                'Name'           : message[0],
+                'Arguments'      : [].slice.call(message, 1) || '',
+                'Element'        : element,
+                'Execution Time' : executionTime
+              });
+            }
+            clearTimeout(module.performance.timer);
+            module.performance.timer = setTimeout(module.performance.display, 500);
+          },
+          display: function() {
+            var
+              title = settings.name + ':',
+              totalTime = 0
+            ;
+            time = false;
+            clearTimeout(module.performance.timer);
+            $.each(performance, function(index, data) {
+              totalTime += data['Execution Time'];
+            });
+            title += ' ' + totalTime + 'ms';
+            if(moduleSelector) {
+              title += ' \'' + moduleSelector + '\'';
+            }
+            if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
+              console.groupCollapsed(title);
+              if(console.table) {
+                console.table(performance);
+              }
+              else {
+                $.each(performance, function(index, data) {
+                  console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
+                });
+              }
+              console.groupEnd();
+            }
+            performance = [];
+          }
+        },
+        invoke: function(query, passedArguments, context) {
+          var
+            object = instance,
+            maxDepth,
+            found,
+            response
+          ;
+          passedArguments = passedArguments || queryArguments;
+          context         = element         || context;
+          if(typeof query == 'string' && object !== undefined) {
+            query    = query.split(/[\. ]/);
+            maxDepth = query.length - 1;
+            $.each(query, function(depth, value) {
+              var camelCaseValue = (depth != maxDepth)
+                ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
+                : query
+              ;
+              if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
+                object = object[camelCaseValue];
+              }
+              else if( object[camelCaseValue] !== undefined ) {
+                found = object[camelCaseValue];
+                return false;
+              }
+              else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
+                object = object[value];
+              }
+              else if( object[value] !== undefined ) {
+                found = object[value];
+                return false;
+              }
+              else {
+                module.error(error.method, query);
+                return false;
+              }
+            });
+          }
+          if ( $.isFunction( found ) ) {
+            response = found.apply(context, passedArguments);
+          }
+          else if(found !== undefined) {
+            response = found;
+          }
+          if(Array.isArray(returnedValue)) {
+            returnedValue.push(response);
+          }
+          else if(returnedValue !== undefined) {
+            returnedValue = [returnedValue, response];
+          }
+          else if(response !== undefined) {
+            returnedValue = response;
+          }
+          return found;
+        }
+      };
+      if(methodInvoked) {
+        if(instance === undefined) {
+          module.initialize();
+        }
+        module.invoke(query);
+      }
+      else {
+        if(instance !== undefined) {
+          instance.invoke('destroy');
+        }
+        module.initialize();
+      }
+    })
+  ;
+  return (returnedValue !== undefined)
+    ? returnedValue
+    : this
+  ;
+};
+
+$.fn.accordion.settings = {
+
+  name            : 'Accordion',
+  namespace       : 'accordion',
+
+  silent          : false,
+  debug           : false,
+  verbose         : false,
+  performance     : true,
+
+  on              : 'click', // event on title that opens accordion
+
+  observeChanges  : true,  // whether accordion should automatically refresh on DOM insertion
+
+  exclusive       : true,  // whether a single accordion content panel should be open at once
+  collapsible     : true,  // whether accordion content can be closed
+  closeNested     : false, // whether nested content should be closed when a panel is closed
+  animateChildren : true,  // whether children opacity should be animated
+
+  duration        : 350, // duration of animation
+  easing          : 'easeOutQuad', // easing equation for animation
+
+  onOpening       : function(){}, // callback before open animation
+  onClosing       : function(){}, // callback before closing animation
+  onChanging      : function(){}, // callback before closing or opening animation
+
+  onOpen          : function(){}, // callback after open animation
+  onClose         : function(){}, // callback after closing animation
+  onChange        : function(){}, // callback after closing or opening animation
+
+  error: {
+    method : 'The method you called is not defined'
+  },
+
+  className   : {
+    active    : 'active',
+    animating : 'animating',
+    transition: 'transition'
+  },
+
+  selector    : {
+    accordion : '.accordion',
+    title     : '.title',
+    trigger   : '.title',
+    content   : '.content'
+  }
+
+};
+
+// Adds easing
+$.extend( $.easing, {
+  easeOutQuad: function (x, t, b, c, d) {
+    return -c *(t/=d)*(t-2) + b;
+  }
+});
+
+})( jQuery, window, document );
+

Разлика између датотеке није приказан због своје велике величине
+ 8 - 0
website/semantic/dist/components/accordion.min.css


Разлика између датотеке није приказан због своје велике величине
+ 10 - 0
website/semantic/dist/components/accordion.min.js


+ 275 - 0
website/semantic/dist/components/ad.css

@@ -0,0 +1,275 @@
+/*!
+ * # Fomantic-UI - Ad
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Copyright 2013 Contributors
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */
+
+
+/*******************************
+         Advertisement
+*******************************/
+
+.ui.ad {
+  display: block;
+  overflow: hidden;
+  margin: 1em 0;
+}
+.ui.ad:first-child {
+  margin: 0;
+}
+.ui.ad:last-child {
+  margin: 0;
+}
+.ui.ad iframe {
+  margin: 0;
+  padding: 0;
+  border: none;
+  overflow: hidden;
+}
+
+/*--------------
+     Common
+---------------*/
+
+
+/* Leaderboard */
+.ui.leaderboard.ad {
+  width: 728px;
+  height: 90px;
+}
+
+/* Medium Rectangle */
+.ui[class*="medium rectangle"].ad {
+  width: 300px;
+  height: 250px;
+}
+
+/* Large Rectangle */
+.ui[class*="large rectangle"].ad {
+  width: 336px;
+  height: 280px;
+}
+
+/* Half Page */
+.ui[class*="half page"].ad {
+  width: 300px;
+  height: 600px;
+}
+
+/*--------------
+     Square
+---------------*/
+
+
+/* Square */
+.ui.square.ad {
+  width: 250px;
+  height: 250px;
+}
+
+/* Small Square */
+.ui[class*="small square"].ad {
+  width: 200px;
+  height: 200px;
+}
+
+/*--------------
+    Rectangle
+---------------*/
+
+
+/* Small Rectangle */
+.ui[class*="small rectangle"].ad {
+  width: 180px;
+  height: 150px;
+}
+
+/* Vertical Rectangle */
+.ui[class*="vertical rectangle"].ad {
+  width: 240px;
+  height: 400px;
+}
+
+/*--------------
+     Button
+---------------*/
+
+.ui.button.ad {
+  width: 120px;
+  height: 90px;
+}
+.ui[class*="square button"].ad {
+  width: 125px;
+  height: 125px;
+}
+.ui[class*="small button"].ad {
+  width: 120px;
+  height: 60px;
+}
+
+/*--------------
+   Skyscrapers
+---------------*/
+
+
+/* Skyscraper */
+.ui.skyscraper.ad {
+  width: 120px;
+  height: 600px;
+}
+
+/* Wide Skyscraper */
+.ui[class*="wide skyscraper"].ad {
+  width: 160px;
+}
+
+/*--------------
+     Banners
+---------------*/
+
+
+/* Banner */
+.ui.banner.ad {
+  width: 468px;
+  height: 60px;
+}
+
+/* Vertical Banner */
+.ui[class*="vertical banner"].ad {
+  width: 120px;
+  height: 240px;
+}
+
+/* Top Banner */
+.ui[class*="top banner"].ad {
+  width: 930px;
+  height: 180px;
+}
+
+/* Half Banner */
+.ui[class*="half banner"].ad {
+  width: 234px;
+  height: 60px;
+}
+
+/*--------------
+    Boards
+---------------*/
+
+
+/* Leaderboard */
+.ui[class*="large leaderboard"].ad {
+  width: 970px;
+  height: 90px;
+}
+
+/* Billboard */
+.ui.billboard.ad {
+  width: 970px;
+  height: 250px;
+}
+
+/*--------------
+    Panorama
+---------------*/
+
+
+/* Panorama */
+.ui.panorama.ad {
+  width: 980px;
+  height: 120px;
+}
+
+/*--------------
+     Netboard
+---------------*/
+
+
+/* Netboard */
+.ui.netboard.ad {
+  width: 580px;
+  height: 400px;
+}
+
+/*--------------
+     Mobile
+---------------*/
+
+
+/* Large Mobile Banner */
+.ui[class*="large mobile banner"].ad {
+  width: 320px;
+  height: 100px;
+}
+
+/* Mobile Leaderboard */
+.ui[class*="mobile leaderboard"].ad {
+  width: 320px;
+  height: 50px;
+}
+
+
+/*******************************
+             Types
+*******************************/
+
+
+/* Mobile Sizes */
+.ui.mobile.ad {
+  display: none;
+}
+@media only screen and (max-width: 767.98px) {
+  .ui.mobile.ad {
+    display: block;
+  }
+}
+
+
+/*******************************
+           Variations
+*******************************/
+
+.ui.centered.ad {
+  margin-left: auto;
+  margin-right: auto;
+}
+.ui.test.ad {
+  position: relative;
+  background: #545454;
+}
+.ui.test.ad:after {
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  width: 100%;
+  text-align: center;
+  -webkit-transform: translateX(-50%) translateY(-50%);
+          transform: translateX(-50%) translateY(-50%);
+  content: 'Ad';
+  color: #FFFFFF;
+  font-size: 1em;
+  font-weight: bold;
+}
+.ui.mobile.test.ad:after {
+  font-size: 0.85714286em;
+}
+.ui.test.ad[data-text]:after {
+  content: attr(data-text);
+}
+
+
+/*******************************
+         Theme Overrides
+*******************************/
+
+
+
+/*******************************
+    User Variable Overrides
+*******************************/
+

+ 10 - 0
website/semantic/dist/components/ad.min.css

@@ -0,0 +1,10 @@
+/*!
+ * # Fomantic-UI - Ad
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Copyright 2013 Contributors
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */.ui.ad{display:block;overflow:hidden;margin:1em 0}.ui.ad:first-child{margin:0}.ui.ad:last-child{margin:0}.ui.ad iframe{margin:0;padding:0;border:none;overflow:hidden}.ui.leaderboard.ad{width:728px;height:90px}.ui[class*="medium rectangle"].ad{width:300px;height:250px}.ui[class*="large rectangle"].ad{width:336px;height:280px}.ui[class*="half page"].ad{width:300px;height:600px}.ui.square.ad{width:250px;height:250px}.ui[class*="small square"].ad{width:200px;height:200px}.ui[class*="small rectangle"].ad{width:180px;height:150px}.ui[class*="vertical rectangle"].ad{width:240px;height:400px}.ui.button.ad{width:120px;height:90px}.ui[class*="square button"].ad{width:125px;height:125px}.ui[class*="small button"].ad{width:120px;height:60px}.ui.skyscraper.ad{width:120px;height:600px}.ui[class*="wide skyscraper"].ad{width:160px}.ui.banner.ad{width:468px;height:60px}.ui[class*="vertical banner"].ad{width:120px;height:240px}.ui[class*="top banner"].ad{width:930px;height:180px}.ui[class*="half banner"].ad{width:234px;height:60px}.ui[class*="large leaderboard"].ad{width:970px;height:90px}.ui.billboard.ad{width:970px;height:250px}.ui.panorama.ad{width:980px;height:120px}.ui.netboard.ad{width:580px;height:400px}.ui[class*="large mobile banner"].ad{width:320px;height:100px}.ui[class*="mobile leaderboard"].ad{width:320px;height:50px}.ui.mobile.ad{display:none}@media only screen and (max-width:767.98px){.ui.mobile.ad{display:block}}.ui.centered.ad{margin-left:auto;margin-right:auto}.ui.test.ad{position:relative;background:#545454}.ui.test.ad:after{position:absolute;top:50%;left:50%;width:100%;text-align:center;-webkit-transform:translateX(-50%) translateY(-50%);transform:translateX(-50%) translateY(-50%);content:'Ad';color:#fff;font-size:1em;font-weight:700}.ui.mobile.test.ad:after{font-size:.85714286em}.ui.test.ad[data-text]:after{content:attr(data-text)}

+ 1177 - 0
website/semantic/dist/components/api.js

@@ -0,0 +1,1177 @@
+/*!
+ * # Fomantic-UI - API
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */
+
+;(function ($, window, document, undefined) {
+
+'use strict';
+
+$.isWindow = $.isWindow || function(obj) {
+  return obj != null && obj === obj.window;
+};
+
+  window = (typeof window != 'undefined' && window.Math == Math)
+    ? window
+    : (typeof self != 'undefined' && self.Math == Math)
+      ? self
+      : Function('return this')()
+;
+
+$.api = $.fn.api = function(parameters) {
+
+  var
+    // use window context if none specified
+    $allModules     = $.isFunction(this)
+        ? $(window)
+        : $(this),
+    moduleSelector = $allModules.selector || '',
+    time           = new Date().getTime(),
+    performance    = [],
+
+    query          = arguments[0],
+    methodInvoked  = (typeof query == 'string'),
+    queryArguments = [].slice.call(arguments, 1),
+
+    returnedValue
+  ;
+
+  $allModules
+    .each(function() {
+      var
+        settings          = ( $.isPlainObject(parameters) )
+          ? $.extend(true, {}, $.fn.api.settings, parameters)
+          : $.extend({}, $.fn.api.settings),
+
+        // internal aliases
+        namespace       = settings.namespace,
+        metadata        = settings.metadata,
+        selector        = settings.selector,
+        error           = settings.error,
+        className       = settings.className,
+
+        // define namespaces for modules
+        eventNamespace  = '.' + namespace,
+        moduleNamespace = 'module-' + namespace,
+
+        // element that creates request
+        $module         = $(this),
+        $form           = $module.closest(selector.form),
+
+        // context used for state
+        $context        = (settings.stateContext)
+          ? $(settings.stateContext)
+          : $module,
+
+        // request details
+        ajaxSettings,
+        requestSettings,
+        url,
+        data,
+        requestStartTime,
+
+        // standard module
+        element         = this,
+        context         = $context[0],
+        instance        = $module.data(moduleNamespace),
+        module
+      ;
+
+      module = {
+
+        initialize: function() {
+          if(!methodInvoked) {
+            module.bind.events();
+          }
+          module.instantiate();
+        },
+
+        instantiate: function() {
+          module.verbose('Storing instance of module', module);
+          instance = module;
+          $module
+            .data(moduleNamespace, instance)
+          ;
+        },
+
+        destroy: function() {
+          module.verbose('Destroying previous module for', element);
+          $module
+            .removeData(moduleNamespace)
+            .off(eventNamespace)
+          ;
+        },
+
+        bind: {
+          events: function() {
+            var
+              triggerEvent = module.get.event()
+            ;
+            if( triggerEvent ) {
+              module.verbose('Attaching API events to element', triggerEvent);
+              $module
+                .on(triggerEvent + eventNamespace, module.event.trigger)
+              ;
+            }
+            else if(settings.on == 'now') {
+              module.debug('Querying API endpoint immediately');
+              module.query();
+            }
+          }
+        },
+
+        decode: {
+          json: function(response) {
+            if(response !== undefined && typeof response == 'string') {
+              try {
+               response = JSON.parse(response);
+              }
+              catch(e) {
+                // isnt json string
+              }
+            }
+            return response;
+          }
+        },
+
+        read: {
+          cachedResponse: function(url) {
+            var
+              response
+            ;
+            if(window.Storage === undefined) {
+              module.error(error.noStorage);
+              return;
+            }
+            response = sessionStorage.getItem(url);
+            module.debug('Using cached response', url, response);
+            response = module.decode.json(response);
+            return response;
+          }
+        },
+        write: {
+          cachedResponse: function(url, response) {
+            if(response && response === '') {
+              module.debug('Response empty, not caching', response);
+              return;
+            }
+            if(window.Storage === undefined) {
+              module.error(error.noStorage);
+              return;
+            }
+            if( $.isPlainObject(response) ) {
+              response = JSON.stringify(response);
+            }
+            sessionStorage.setItem(url, response);
+            module.verbose('Storing cached response for url', url, response);
+          }
+        },
+
+        query: function() {
+
+          if(module.is.disabled()) {
+            module.debug('Element is disabled API request aborted');
+            return;
+          }
+
+          if(module.is.loading()) {
+            if(settings.interruptRequests) {
+              module.debug('Interrupting previous request');
+              module.abort();
+            }
+            else {
+              module.debug('Cancelling request, previous request is still pending');
+              return;
+            }
+          }
+
+          // pass element metadata to url (value, text)
+          if(settings.defaultData) {
+            $.extend(true, settings.urlData, module.get.defaultData());
+          }
+
+          // Add form content
+          if(settings.serializeForm) {
+            settings.data = module.add.formData(settings.data);
+          }
+
+          // call beforesend and get any settings changes
+          requestSettings = module.get.settings();
+
+          // check if before send cancelled request
+          if(requestSettings === false) {
+            module.cancelled = true;
+            module.error(error.beforeSend);
+            return;
+          }
+          else {
+            module.cancelled = false;
+          }
+
+          // get url
+          url = module.get.templatedURL();
+
+          if(!url && !module.is.mocked()) {
+            module.error(error.missingURL);
+            return;
+          }
+
+          // replace variables
+          url = module.add.urlData( url );
+          // missing url parameters
+          if( !url && !module.is.mocked()) {
+            return;
+          }
+
+          requestSettings.url = settings.base + url;
+
+          // look for jQuery ajax parameters in settings
+          ajaxSettings = $.extend(true, {}, settings, {
+            type       : settings.method || settings.type,
+            data       : data,
+            url        : settings.base + url,
+            beforeSend : settings.beforeXHR,
+            success    : function() {},
+            failure    : function() {},
+            complete   : function() {}
+          });
+
+          module.debug('Querying URL', ajaxSettings.url);
+          module.verbose('Using AJAX settings', ajaxSettings);
+          if(settings.cache === 'local' && module.read.cachedResponse(url)) {
+            module.debug('Response returned from local cache');
+            module.request = module.create.request();
+            module.request.resolveWith(context, [ module.read.cachedResponse(url) ]);
+            return;
+          }
+
+          if( !settings.throttle ) {
+            module.debug('Sending request', data, ajaxSettings.method);
+            module.send.request();
+          }
+          else {
+            if(!settings.throttleFirstRequest && !module.timer) {
+              module.debug('Sending request', data, ajaxSettings.method);
+              module.send.request();
+              module.timer = setTimeout(function(){}, settings.throttle);
+            }
+            else {
+              module.debug('Throttling request', settings.throttle);
+              clearTimeout(module.timer);
+              module.timer = setTimeout(function() {
+                if(module.timer) {
+                  delete module.timer;
+                }
+                module.debug('Sending throttled request', data, ajaxSettings.method);
+                module.send.request();
+              }, settings.throttle);
+            }
+          }
+
+        },
+
+        should: {
+          removeError: function() {
+            return ( settings.hideError === true || (settings.hideError === 'auto' && !module.is.form()) );
+          }
+        },
+
+        is: {
+          disabled: function() {
+            return ($module.filter(selector.disabled).length > 0);
+          },
+          expectingJSON: function() {
+            return settings.dataType === 'json' || settings.dataType === 'jsonp';
+          },
+          form: function() {
+            return $module.is('form') || $context.is('form');
+          },
+          mocked: function() {
+            return (settings.mockResponse || settings.mockResponseAsync || settings.response || settings.responseAsync);
+          },
+          input: function() {
+            return $module.is('input');
+          },
+          loading: function() {
+            return (module.request)
+              ? (module.request.state() == 'pending')
+              : false
+            ;
+          },
+          abortedRequest: function(xhr) {
+            if(xhr && xhr.readyState !== undefined && xhr.readyState === 0) {
+              module.verbose('XHR request determined to be aborted');
+              return true;
+            }
+            else {
+              module.verbose('XHR request was not aborted');
+              return false;
+            }
+          },
+          validResponse: function(response) {
+            if( (!module.is.expectingJSON()) || !$.isFunction(settings.successTest) ) {
+              module.verbose('Response is not JSON, skipping validation', settings.successTest, response);
+              return true;
+            }
+            module.debug('Checking JSON returned success', settings.successTest, response);
+            if( settings.successTest(response) ) {
+              module.debug('Response passed success test', response);
+              return true;
+            }
+            else {
+              module.debug('Response failed success test', response);
+              return false;
+            }
+          }
+        },
+
+        was: {
+          cancelled: function() {
+            return (module.cancelled || false);
+          },
+          succesful: function() {
+            module.verbose('This behavior will be deleted due to typo. Use "was successful" instead.');
+            return module.was.successful();
+          },
+          successful: function() {
+            return (module.request && module.request.state() == 'resolved');
+          },
+          failure: function() {
+            return (module.request && module.request.state() == 'rejected');
+          },
+          complete: function() {
+            return (module.request && (module.request.state() == 'resolved' || module.request.state() == 'rejected') );
+          }
+        },
+
+        add: {
+          urlData: function(url, urlData) {
+            var
+              requiredVariables,
+              optionalVariables
+            ;
+            if(url) {
+              requiredVariables = url.match(settings.regExp.required);
+              optionalVariables = url.match(settings.regExp.optional);
+              urlData           = urlData || settings.urlData;
+              if(requiredVariables) {
+                module.debug('Looking for required URL variables', requiredVariables);
+                $.each(requiredVariables, function(index, templatedString) {
+                  var
+                    // allow legacy {$var} style
+                    variable = (templatedString.indexOf('$') !== -1)
+                      ? templatedString.substr(2, templatedString.length - 3)
+                      : templatedString.substr(1, templatedString.length - 2),
+                    value   = ($.isPlainObject(urlData) && urlData[variable] !== undefined)
+                      ? urlData[variable]
+                      : ($module.data(variable) !== undefined)
+                        ? $module.data(variable)
+                        : ($context.data(variable) !== undefined)
+                          ? $context.data(variable)
+                          : urlData[variable]
+                  ;
+                  // remove value
+                  if(value === undefined) {
+                    module.error(error.requiredParameter, variable, url);
+                    url = false;
+                    return false;
+                  }
+                  else {
+                    module.verbose('Found required variable', variable, value);
+                    value = (settings.encodeParameters)
+                      ? module.get.urlEncodedValue(value)
+                      : value
+                    ;
+                    url = url.replace(templatedString, value);
+                  }
+                });
+              }
+              if(optionalVariables) {
+                module.debug('Looking for optional URL variables', requiredVariables);
+                $.each(optionalVariables, function(index, templatedString) {
+                  var
+                    // allow legacy {/$var} style
+                    variable = (templatedString.indexOf('$') !== -1)
+                      ? templatedString.substr(3, templatedString.length - 4)
+                      : templatedString.substr(2, templatedString.length - 3),
+                    value   = ($.isPlainObject(urlData) && urlData[variable] !== undefined)
+                      ? urlData[variable]
+                      : ($module.data(variable) !== undefined)
+                        ? $module.data(variable)
+                        : ($context.data(variable) !== undefined)
+                          ? $context.data(variable)
+                          : urlData[variable]
+                  ;
+                  // optional replacement
+                  if(value !== undefined) {
+                    module.verbose('Optional variable Found', variable, value);
+                    url = url.replace(templatedString, value);
+                  }
+                  else {
+                    module.verbose('Optional variable not found', variable);
+                    // remove preceding slash if set
+                    if(url.indexOf('/' + templatedString) !== -1) {
+                      url = url.replace('/' + templatedString, '');
+                    }
+                    else {
+                      url = url.replace(templatedString, '');
+                    }
+                  }
+                });
+              }
+            }
+            return url;
+          },
+          formData: function(data) {
+            var
+              canSerialize = ($.fn.serializeObject !== undefined),
+              formData     = (canSerialize)
+                ? $form.serializeObject()
+                : $form.serialize(),
+              hasOtherData
+            ;
+            data         = data || settings.data;
+            hasOtherData = $.isPlainObject(data);
+
+            if(hasOtherData) {
+              if(canSerialize) {
+                module.debug('Extending existing data with form data', data, formData);
+                data = $.extend(true, {}, data, formData);
+              }
+              else {
+                module.error(error.missingSerialize);
+                module.debug('Cant extend data. Replacing data with form data', data, formData);
+                data = formData;
+              }
+            }
+            else {
+              module.debug('Adding form data', formData);
+              data = formData;
+            }
+            return data;
+          }
+        },
+
+        send: {
+          request: function() {
+            module.set.loading();
+            module.request = module.create.request();
+            if( module.is.mocked() ) {
+              module.mockedXHR = module.create.mockedXHR();
+            }
+            else {
+              module.xhr = module.create.xhr();
+            }
+            settings.onRequest.call(context, module.request, module.xhr);
+          }
+        },
+
+        event: {
+          trigger: function(event) {
+            module.query();
+            if(event.type == 'submit' || event.type == 'click') {
+              event.preventDefault();
+            }
+          },
+          xhr: {
+            always: function() {
+              // nothing special
+            },
+            done: function(response, textStatus, xhr) {
+              var
+                context            = this,
+                elapsedTime        = (new Date().getTime() - requestStartTime),
+                timeLeft           = (settings.loadingDuration - elapsedTime),
+                translatedResponse = ( $.isFunction(settings.onResponse) )
+                  ? module.is.expectingJSON() && !settings.rawResponse
+                    ? settings.onResponse.call(context, $.extend(true, {}, response))
+                    : settings.onResponse.call(context, response)
+                  : false
+              ;
+              timeLeft = (timeLeft > 0)
+                ? timeLeft
+                : 0
+              ;
+              if(translatedResponse) {
+                module.debug('Modified API response in onResponse callback', settings.onResponse, translatedResponse, response);
+                response = translatedResponse;
+              }
+              if(timeLeft > 0) {
+                module.debug('Response completed early delaying state change by', timeLeft);
+              }
+              setTimeout(function() {
+                if( module.is.validResponse(response) ) {
+                  module.request.resolveWith(context, [response, xhr]);
+                }
+                else {
+                  module.request.rejectWith(context, [xhr, 'invalid']);
+                }
+              }, timeLeft);
+            },
+            fail: function(xhr, status, httpMessage) {
+              var
+                context     = this,
+                elapsedTime = (new Date().getTime() - requestStartTime),
+                timeLeft    = (settings.loadingDuration - elapsedTime)
+              ;
+              timeLeft = (timeLeft > 0)
+                ? timeLeft
+                : 0
+              ;
+              if(timeLeft > 0) {
+                module.debug('Response completed early delaying state change by', timeLeft);
+              }
+              setTimeout(function() {
+                if( module.is.abortedRequest(xhr) ) {
+                  module.request.rejectWith(context, [xhr, 'aborted', httpMessage]);
+                }
+                else {
+                  module.request.rejectWith(context, [xhr, 'error', status, httpMessage]);
+                }
+              }, timeLeft);
+            }
+          },
+          request: {
+            done: function(response, xhr) {
+              module.debug('Successful API Response', response);
+              if(settings.cache === 'local' && url) {
+                module.write.cachedResponse(url, response);
+                module.debug('Saving server response locally', module.cache);
+              }
+              settings.onSuccess.call(context, response, $module, xhr);
+            },
+            complete: function(firstParameter, secondParameter) {
+              var
+                xhr,
+                response
+              ;
+              // have to guess callback parameters based on request success
+              if( module.was.successful() ) {
+                response = firstParameter;
+                xhr      = secondParameter;
+              }
+              else {
+                xhr      = firstParameter;
+                response = module.get.responseFromXHR(xhr);
+              }
+              module.remove.loading();
+              settings.onComplete.call(context, response, $module, xhr);
+            },
+            fail: function(xhr, status, httpMessage) {
+              var
+                // pull response from xhr if available
+                response     = module.get.responseFromXHR(xhr),
+                errorMessage = module.get.errorFromRequest(response, status, httpMessage)
+              ;
+              if(status == 'aborted') {
+                module.debug('XHR Aborted (Most likely caused by page navigation or CORS Policy)', status, httpMessage);
+                settings.onAbort.call(context, status, $module, xhr);
+                return true;
+              }
+              else if(status == 'invalid') {
+                module.debug('JSON did not pass success test. A server-side error has most likely occurred', response);
+              }
+              else if(status == 'error') {
+                if(xhr !== undefined) {
+                  module.debug('XHR produced a server error', status, httpMessage);
+                  // make sure we have an error to display to console
+                  if( (xhr.status < 200 || xhr.status >= 300) && httpMessage !== undefined && httpMessage !== '') {
+                    module.error(error.statusMessage + httpMessage, ajaxSettings.url);
+                  }
+                  settings.onError.call(context, errorMessage, $module, xhr);
+                }
+              }
+
+              if(settings.errorDuration && status !== 'aborted') {
+                module.debug('Adding error state');
+                module.set.error();
+                if( module.should.removeError() ) {
+                  setTimeout(module.remove.error, settings.errorDuration);
+                }
+              }
+              module.debug('API Request failed', errorMessage, xhr);
+              settings.onFailure.call(context, response, $module, xhr);
+            }
+          }
+        },
+
+        create: {
+
+          request: function() {
+            // api request promise
+            return $.Deferred()
+              .always(module.event.request.complete)
+              .done(module.event.request.done)
+              .fail(module.event.request.fail)
+            ;
+          },
+
+          mockedXHR: function () {
+            var
+              // xhr does not simulate these properties of xhr but must return them
+              textStatus     = false,
+              status         = false,
+              httpMessage    = false,
+              responder      = settings.mockResponse      || settings.response,
+              asyncResponder = settings.mockResponseAsync || settings.responseAsync,
+              asyncCallback,
+              response,
+              mockedXHR
+            ;
+
+            mockedXHR = $.Deferred()
+              .always(module.event.xhr.complete)
+              .done(module.event.xhr.done)
+              .fail(module.event.xhr.fail)
+            ;
+
+            if(responder) {
+              if( $.isFunction(responder) ) {
+                module.debug('Using specified synchronous callback', responder);
+                response = responder.call(context, requestSettings);
+              }
+              else {
+                module.debug('Using settings specified response', responder);
+                response = responder;
+              }
+              // simulating response
+              mockedXHR.resolveWith(context, [ response, textStatus, { responseText: response }]);
+            }
+            else if( $.isFunction(asyncResponder) ) {
+              asyncCallback = function(response) {
+                module.debug('Async callback returned response', response);
+
+                if(response) {
+                  mockedXHR.resolveWith(context, [ response, textStatus, { responseText: response }]);
+                }
+                else {
+                  mockedXHR.rejectWith(context, [{ responseText: response }, status, httpMessage]);
+                }
+              };
+              module.debug('Using specified async response callback', asyncResponder);
+              asyncResponder.call(context, requestSettings, asyncCallback);
+            }
+            return mockedXHR;
+          },
+
+          xhr: function() {
+            var
+              xhr
+            ;
+            // ajax request promise
+            xhr = $.ajax(ajaxSettings)
+              .always(module.event.xhr.always)
+              .done(module.event.xhr.done)
+              .fail(module.event.xhr.fail)
+            ;
+            module.verbose('Created server request', xhr, ajaxSettings);
+            return xhr;
+          }
+        },
+
+        set: {
+          error: function() {
+            module.verbose('Adding error state to element', $context);
+            $context.addClass(className.error);
+          },
+          loading: function() {
+            module.verbose('Adding loading state to element', $context);
+            $context.addClass(className.loading);
+            requestStartTime = new Date().getTime();
+          }
+        },
+
+        remove: {
+          error: function() {
+            module.verbose('Removing error state from element', $context);
+            $context.removeClass(className.error);
+          },
+          loading: function() {
+            module.verbose('Removing loading state from element', $context);
+            $context.removeClass(className.loading);
+          }
+        },
+
+        get: {
+          responseFromXHR: function(xhr) {
+            return $.isPlainObject(xhr)
+              ? (module.is.expectingJSON())
+                ? module.decode.json(xhr.responseText)
+                : xhr.responseText
+              : false
+            ;
+          },
+          errorFromRequest: function(response, status, httpMessage) {
+            return ($.isPlainObject(response) && response.error !== undefined)
+              ? response.error // use json error message
+              : (settings.error[status] !== undefined) // use server error message
+                ? settings.error[status]
+                : httpMessage
+            ;
+          },
+          request: function() {
+            return module.request || false;
+          },
+          xhr: function() {
+            return module.xhr || false;
+          },
+          settings: function() {
+            var
+              runSettings
+            ;
+            runSettings = settings.beforeSend.call(context, settings);
+            if(runSettings) {
+              if(runSettings.success !== undefined) {
+                module.debug('Legacy success callback detected', runSettings);
+                module.error(error.legacyParameters, runSettings.success);
+                runSettings.onSuccess = runSettings.success;
+              }
+              if(runSettings.failure !== undefined) {
+                module.debug('Legacy failure callback detected', runSettings);
+                module.error(error.legacyParameters, runSettings.failure);
+                runSettings.onFailure = runSettings.failure;
+              }
+              if(runSettings.complete !== undefined) {
+                module.debug('Legacy complete callback detected', runSettings);
+                module.error(error.legacyParameters, runSettings.complete);
+                runSettings.onComplete = runSettings.complete;
+              }
+            }
+            if(runSettings === undefined) {
+              module.error(error.noReturnedValue);
+            }
+            if(runSettings === false) {
+              return runSettings;
+            }
+            return (runSettings !== undefined)
+              ? $.extend(true, {}, runSettings)
+              : $.extend(true, {}, settings)
+            ;
+          },
+          urlEncodedValue: function(value) {
+            var
+              decodedValue   = window.decodeURIComponent(value),
+              encodedValue   = window.encodeURIComponent(value),
+              alreadyEncoded = (decodedValue !== value)
+            ;
+            if(alreadyEncoded) {
+              module.debug('URL value is already encoded, avoiding double encoding', value);
+              return value;
+            }
+            module.verbose('Encoding value using encodeURIComponent', value, encodedValue);
+            return encodedValue;
+          },
+          defaultData: function() {
+            var
+              data = {}
+            ;
+            if( !$.isWindow(element) ) {
+              if( module.is.input() ) {
+                data.value = $module.val();
+              }
+              else if( module.is.form() ) {
+
+              }
+              else {
+                data.text = $module.text();
+              }
+            }
+            return data;
+          },
+          event: function() {
+            if( $.isWindow(element) || settings.on == 'now' ) {
+              module.debug('API called without element, no events attached');
+              return false;
+            }
+            else if(settings.on == 'auto') {
+              if( $module.is('input') ) {
+                return (element.oninput !== undefined)
+                  ? 'input'
+                  : (element.onpropertychange !== undefined)
+                    ? 'propertychange'
+                    : 'keyup'
+                ;
+              }
+              else if( $module.is('form') ) {
+                return 'submit';
+              }
+              else {
+                return 'click';
+              }
+            }
+            else {
+              return settings.on;
+            }
+          },
+          templatedURL: function(action) {
+            action = action || $module.data(metadata.action) || settings.action || false;
+            url    = $module.data(metadata.url) || settings.url || false;
+            if(url) {
+              module.debug('Using specified url', url);
+              return url;
+            }
+            if(action) {
+              module.debug('Looking up url for action', action, settings.api);
+              if(settings.api[action] === undefined && !module.is.mocked()) {
+                module.error(error.missingAction, settings.action, settings.api);
+                return;
+              }
+              url = settings.api[action];
+            }
+            else if( module.is.form() ) {
+              url = $module.attr('action') || $context.attr('action') || false;
+              module.debug('No url or action specified, defaulting to form action', url);
+            }
+            return url;
+          }
+        },
+
+        abort: function() {
+          var
+            xhr = module.get.xhr()
+          ;
+          if( xhr && xhr.state() !== 'resolved') {
+            module.debug('Cancelling API request');
+            xhr.abort();
+          }
+        },
+
+        // reset state
+        reset: function() {
+          module.remove.error();
+          module.remove.loading();
+        },
+
+        setting: function(name, value) {
+          module.debug('Changing setting', name, value);
+          if( $.isPlainObject(name) ) {
+            $.extend(true, settings, name);
+          }
+          else if(value !== undefined) {
+            if($.isPlainObject(settings[name])) {
+              $.extend(true, settings[name], value);
+            }
+            else {
+              settings[name] = value;
+            }
+          }
+          else {
+            return settings[name];
+          }
+        },
+        internal: function(name, value) {
+          if( $.isPlainObject(name) ) {
+            $.extend(true, module, name);
+          }
+          else if(value !== undefined) {
+            module[name] = value;
+          }
+          else {
+            return module[name];
+          }
+        },
+        debug: function() {
+          if(!settings.silent && settings.debug) {
+            if(settings.performance) {
+              module.performance.log(arguments);
+            }
+            else {
+              module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
+              module.debug.apply(console, arguments);
+            }
+          }
+        },
+        verbose: function() {
+          if(!settings.silent && settings.verbose && settings.debug) {
+            if(settings.performance) {
+              module.performance.log(arguments);
+            }
+            else {
+              module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
+              module.verbose.apply(console, arguments);
+            }
+          }
+        },
+        error: function() {
+          if(!settings.silent) {
+            module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
+            module.error.apply(console, arguments);
+          }
+        },
+        performance: {
+          log: function(message) {
+            var
+              currentTime,
+              executionTime,
+              previousTime
+            ;
+            if(settings.performance) {
+              currentTime   = new Date().getTime();
+              previousTime  = time || currentTime;
+              executionTime = currentTime - previousTime;
+              time          = currentTime;
+              performance.push({
+                'Name'           : message[0],
+                'Arguments'      : [].slice.call(message, 1) || '',
+                //'Element'        : element,
+                'Execution Time' : executionTime
+              });
+            }
+            clearTimeout(module.performance.timer);
+            module.performance.timer = setTimeout(module.performance.display, 500);
+          },
+          display: function() {
+            var
+              title = settings.name + ':',
+              totalTime = 0
+            ;
+            time = false;
+            clearTimeout(module.performance.timer);
+            $.each(performance, function(index, data) {
+              totalTime += data['Execution Time'];
+            });
+            title += ' ' + totalTime + 'ms';
+            if(moduleSelector) {
+              title += ' \'' + moduleSelector + '\'';
+            }
+            if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
+              console.groupCollapsed(title);
+              if(console.table) {
+                console.table(performance);
+              }
+              else {
+                $.each(performance, function(index, data) {
+                  console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
+                });
+              }
+              console.groupEnd();
+            }
+            performance = [];
+          }
+        },
+        invoke: function(query, passedArguments, context) {
+          var
+            object = instance,
+            maxDepth,
+            found,
+            response
+          ;
+          passedArguments = passedArguments || queryArguments;
+          context         = element         || context;
+          if(typeof query == 'string' && object !== undefined) {
+            query    = query.split(/[\. ]/);
+            maxDepth = query.length - 1;
+            $.each(query, function(depth, value) {
+              var camelCaseValue = (depth != maxDepth)
+                ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
+                : query
+              ;
+              if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
+                object = object[camelCaseValue];
+              }
+              else if( object[camelCaseValue] !== undefined ) {
+                found = object[camelCaseValue];
+                return false;
+              }
+              else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
+                object = object[value];
+              }
+              else if( object[value] !== undefined ) {
+                found = object[value];
+                return false;
+              }
+              else {
+                module.error(error.method, query);
+                return false;
+              }
+            });
+          }
+          if ( $.isFunction( found ) ) {
+            response = found.apply(context, passedArguments);
+          }
+          else if(found !== undefined) {
+            response = found;
+          }
+          if(Array.isArray(returnedValue)) {
+            returnedValue.push(response);
+          }
+          else if(returnedValue !== undefined) {
+            returnedValue = [returnedValue, response];
+          }
+          else if(response !== undefined) {
+            returnedValue = response;
+          }
+          return found;
+        }
+      };
+
+      if(methodInvoked) {
+        if(instance === undefined) {
+          module.initialize();
+        }
+        module.invoke(query);
+      }
+      else {
+        if(instance !== undefined) {
+          instance.invoke('destroy');
+        }
+        module.initialize();
+      }
+    })
+  ;
+
+  return (returnedValue !== undefined)
+    ? returnedValue
+    : this
+  ;
+};
+
+$.api.settings = {
+
+  name              : 'API',
+  namespace         : 'api',
+
+  debug             : false,
+  verbose           : false,
+  performance       : true,
+
+  // object containing all templates endpoints
+  api               : {},
+
+  // whether to cache responses
+  cache             : true,
+
+  // whether new requests should abort previous requests
+  interruptRequests : true,
+
+  // event binding
+  on                : 'auto',
+
+  // context for applying state classes
+  stateContext      : false,
+
+  // duration for loading state
+  loadingDuration   : 0,
+
+  // whether to hide errors after a period of time
+  hideError         : 'auto',
+
+  // duration for error state
+  errorDuration     : 2000,
+
+  // whether parameters should be encoded with encodeURIComponent
+  encodeParameters  : true,
+
+  // API action to use
+  action            : false,
+
+  // templated URL to use
+  url               : false,
+
+  // base URL to apply to all endpoints
+  base              : '',
+
+  // data that will
+  urlData           : {},
+
+  // whether to add default data to url data
+  defaultData          : true,
+
+  // whether to serialize closest form
+  serializeForm        : false,
+
+  // how long to wait before request should occur
+  throttle             : 0,
+
+  // whether to throttle first request or only repeated
+  throttleFirstRequest : true,
+
+  // standard ajax settings
+  method            : 'get',
+  data              : {},
+  dataType          : 'json',
+
+  // mock response
+  mockResponse      : false,
+  mockResponseAsync : false,
+
+  // aliases for mock
+  response          : false,
+  responseAsync     : false,
+
+// whether onResponse should work with response value without force converting into an object
+  rawResponse       : false,
+
+  // callbacks before request
+  beforeSend  : function(settings) { return settings; },
+  beforeXHR   : function(xhr) {},
+  onRequest   : function(promise, xhr) {},
+
+  // after request
+  onResponse  : false, // function(response) { },
+
+  // response was successful, if JSON passed validation
+  onSuccess   : function(response, $module) {},
+
+  // request finished without aborting
+  onComplete  : function(response, $module) {},
+
+  // failed JSON success test
+  onFailure   : function(response, $module) {},
+
+  // server error
+  onError     : function(errorMessage, $module) {},
+
+  // request aborted
+  onAbort     : function(errorMessage, $module) {},
+
+  successTest : false,
+
+  // errors
+  error : {
+    beforeSend        : 'The before send function has aborted the request',
+    error             : 'There was an error with your request',
+    exitConditions    : 'API Request Aborted. Exit conditions met',
+    JSONParse         : 'JSON could not be parsed during error handling',
+    legacyParameters  : 'You are using legacy API success callback names',
+    method            : 'The method you called is not defined',
+    missingAction     : 'API action used but no url was defined',
+    missingSerialize  : 'jquery-serialize-object is required to add form data to an existing data object',
+    missingURL        : 'No URL specified for api event',
+    noReturnedValue   : 'The beforeSend callback must return a settings object, beforeSend ignored.',
+    noStorage         : 'Caching responses locally requires session storage',
+    parseError        : 'There was an error parsing your request',
+    requiredParameter : 'Missing a required URL parameter: ',
+    statusMessage     : 'Server gave an error: ',
+    timeout           : 'Your request timed out'
+  },
+
+  regExp  : {
+    required : /\{\$*[A-z0-9]+\}/g,
+    optional : /\{\/\$*[A-z0-9]+\}/g,
+  },
+
+  className: {
+    loading : 'loading',
+    error   : 'error'
+  },
+
+  selector: {
+    disabled : '.disabled',
+    form      : 'form'
+  },
+
+  metadata: {
+    action  : 'action',
+    url     : 'url'
+  }
+};
+
+
+
+})( jQuery, window, document );

Разлика између датотеке није приказан због своје велике величине
+ 10 - 0
website/semantic/dist/components/api.min.js


+ 135 - 0
website/semantic/dist/components/breadcrumb.css

@@ -0,0 +1,135 @@
+/*!
+ * # Fomantic-UI - Breadcrumb
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */
+
+
+/*******************************
+           Breadcrumb
+*******************************/
+
+.ui.breadcrumb {
+  line-height: 1.4285em;
+  display: inline-block;
+  margin: 0 0;
+  vertical-align: middle;
+}
+.ui.breadcrumb:first-child {
+  margin-top: 0;
+}
+.ui.breadcrumb:last-child {
+  margin-bottom: 0;
+}
+
+
+/*******************************
+          Content
+*******************************/
+
+
+/* Divider */
+.ui.breadcrumb .divider {
+  display: inline-block;
+  opacity: 0.7;
+  margin: 0 0.21428571rem 0;
+  font-size: 0.92857143em;
+  color: rgba(0, 0, 0, 0.4);
+  vertical-align: baseline;
+}
+
+/* Link */
+.ui.breadcrumb a {
+  color: #4183C4;
+}
+.ui.breadcrumb a:hover {
+  color: #1e70bf;
+}
+
+/* Icon Divider */
+.ui.breadcrumb .icon.divider {
+  font-size: 0.85714286em;
+  vertical-align: baseline;
+}
+
+/* Section */
+.ui.breadcrumb a.section {
+  cursor: pointer;
+}
+.ui.breadcrumb .section {
+  display: inline-block;
+  margin: 0;
+  padding: 0;
+}
+
+/* Loose Coupling */
+.ui.breadcrumb.segment {
+  display: inline-block;
+  padding: 0.78571429em 1em;
+}
+
+/* Inverted */
+.ui.inverted.breadcrumb {
+  color: #DCDDDE;
+}
+.ui.inverted.breadcrumb > .active.section {
+  color: #FFFFFF;
+}
+.ui.inverted.breadcrumb > .divider {
+  color: rgba(255, 255, 255, 0.7);
+}
+
+
+/*******************************
+            States
+*******************************/
+
+.ui.breadcrumb .active.section {
+  font-weight: bold;
+}
+
+
+/*******************************
+           Variations
+*******************************/
+
+.ui.mini.breadcrumb {
+  font-size: 0.78571429rem;
+}
+.ui.tiny.breadcrumb {
+  font-size: 0.85714286rem;
+}
+.ui.small.breadcrumb {
+  font-size: 0.92857143rem;
+}
+.ui.breadcrumb {
+  font-size: 1rem;
+}
+.ui.large.breadcrumb {
+  font-size: 1.14285714rem;
+}
+.ui.big.breadcrumb {
+  font-size: 1.28571429rem;
+}
+.ui.huge.breadcrumb {
+  font-size: 1.42857143rem;
+}
+.ui.massive.breadcrumb {
+  font-size: 1.71428571rem;
+}
+
+
+/*******************************
+         Theme Overrides
+*******************************/
+
+
+
+/*******************************
+         Site Overrides
+*******************************/
+

+ 9 - 0
website/semantic/dist/components/breadcrumb.min.css

@@ -0,0 +1,9 @@
+/*!
+ * # Fomantic-UI - Breadcrumb
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */.ui.breadcrumb{line-height:1.4285em;display:inline-block;margin:0 0;vertical-align:middle}.ui.breadcrumb:first-child{margin-top:0}.ui.breadcrumb:last-child{margin-bottom:0}.ui.breadcrumb .divider{display:inline-block;opacity:.7;margin:0 .21428571rem 0;font-size:.92857143em;color:rgba(0,0,0,.4);vertical-align:baseline}.ui.breadcrumb a{color:#4183c4}.ui.breadcrumb a:hover{color:#1e70bf}.ui.breadcrumb .icon.divider{font-size:.85714286em;vertical-align:baseline}.ui.breadcrumb a.section{cursor:pointer}.ui.breadcrumb .section{display:inline-block;margin:0;padding:0}.ui.breadcrumb.segment{display:inline-block;padding:.78571429em 1em}.ui.inverted.breadcrumb{color:#dcddde}.ui.inverted.breadcrumb>.active.section{color:#fff}.ui.inverted.breadcrumb>.divider{color:rgba(255,255,255,.7)}.ui.breadcrumb .active.section{font-weight:700}.ui.mini.breadcrumb{font-size:.78571429rem}.ui.tiny.breadcrumb{font-size:.85714286rem}.ui.small.breadcrumb{font-size:.92857143rem}.ui.breadcrumb{font-size:1rem}.ui.large.breadcrumb{font-size:1.14285714rem}.ui.big.breadcrumb{font-size:1.28571429rem}.ui.huge.breadcrumb{font-size:1.42857143rem}.ui.massive.breadcrumb{font-size:1.71428571rem}

+ 4402 - 0
website/semantic/dist/components/button.css

@@ -0,0 +1,4402 @@
+/*!
+ * # Fomantic-UI - Button
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */
+
+
+/*******************************
+            Button
+*******************************/
+
+.ui.button {
+  cursor: pointer;
+  display: inline-block;
+  min-height: 1em;
+  outline: none;
+  border: none;
+  vertical-align: baseline;
+  background: #E0E1E2 none;
+  color: rgba(0, 0, 0, 0.6);
+  font-family: 'Lato', 'Helvetica Neue', Arial, Helvetica, sans-serif;
+  margin: 0 0.25em 0 0;
+  padding: 0.78571429em 1.5em 0.78571429em;
+  text-transform: none;
+  text-shadow: none;
+  font-weight: bold;
+  line-height: 1em;
+  font-style: normal;
+  text-align: center;
+  text-decoration: none;
+  border-radius: 0.28571429rem;
+  -webkit-box-shadow: 0 0 0 1px transparent inset, 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 0 0 1px transparent inset, 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+  -webkit-user-select: none;
+     -moz-user-select: none;
+      -ms-user-select: none;
+          user-select: none;
+  -webkit-transition: opacity 0.1s ease, background-color 0.1s ease, color 0.1s ease, background 0.1s ease, -webkit-box-shadow 0.1s ease;
+  transition: opacity 0.1s ease, background-color 0.1s ease, color 0.1s ease, background 0.1s ease, -webkit-box-shadow 0.1s ease;
+  transition: opacity 0.1s ease, background-color 0.1s ease, color 0.1s ease, box-shadow 0.1s ease, background 0.1s ease;
+  transition: opacity 0.1s ease, background-color 0.1s ease, color 0.1s ease, box-shadow 0.1s ease, background 0.1s ease, -webkit-box-shadow 0.1s ease;
+  will-change: auto;
+  -webkit-tap-highlight-color: transparent;
+}
+
+
+/*******************************
+            States
+*******************************/
+
+
+/*--------------
+      Hover
+---------------*/
+
+.ui.button:hover {
+  background-color: #CACBCD;
+  background-image: none;
+  -webkit-box-shadow: 0 0 0 1px transparent inset, 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 0 0 1px transparent inset, 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+  color: rgba(0, 0, 0, 0.8);
+}
+.ui.button:hover .icon {
+  opacity: 0.85;
+}
+
+/*--------------
+      Focus
+---------------*/
+
+.ui.button:focus {
+  background-color: #CACBCD;
+  color: rgba(0, 0, 0, 0.8);
+  background-image: none;
+  -webkit-box-shadow: '';
+          box-shadow: '';
+}
+.ui.button:focus .icon {
+  opacity: 0.85;
+}
+
+/*--------------
+      Down
+---------------*/
+
+.ui.button:active,
+.ui.active.button:active {
+  background-color: #BABBBC;
+  background-image: '';
+  color: rgba(0, 0, 0, 0.9);
+  -webkit-box-shadow: 0 0 0 1px transparent inset, none;
+          box-shadow: 0 0 0 1px transparent inset, none;
+}
+
+/*--------------
+     Active
+---------------*/
+
+.ui.active.button {
+  background-color: #C0C1C2;
+  background-image: none;
+  -webkit-box-shadow: 0 0 0 1px transparent inset;
+          box-shadow: 0 0 0 1px transparent inset;
+  color: rgba(0, 0, 0, 0.95);
+}
+.ui.active.button:hover {
+  background-color: #C0C1C2;
+  background-image: none;
+  color: rgba(0, 0, 0, 0.95);
+}
+.ui.active.button:active {
+  background-color: #C0C1C2;
+  background-image: none;
+}
+
+/*--------------
+    Loading
+---------------*/
+
+
+/* Specificity hack */
+.ui.loading.loading.loading.loading.loading.loading.button {
+  position: relative;
+  cursor: default;
+  text-shadow: none !important;
+  color: transparent;
+  opacity: 1;
+  pointer-events: auto;
+  -webkit-transition: all 0s linear, opacity 0.1s ease;
+  transition: all 0s linear, opacity 0.1s ease;
+}
+.ui.loading.button:before {
+  position: absolute;
+  content: '';
+  top: 50%;
+  left: 50%;
+  margin: -0.64285714em 0 0 -0.64285714em;
+  width: 1.28571429em;
+  height: 1.28571429em;
+  border-radius: 500rem;
+  border: 0.2em solid rgba(0, 0, 0, 0.15);
+}
+.ui.loading.button:after {
+  position: absolute;
+  content: '';
+  top: 50%;
+  left: 50%;
+  margin: -0.64285714em 0 0 -0.64285714em;
+  width: 1.28571429em;
+  height: 1.28571429em;
+  border-radius: 500rem;
+  -webkit-animation: loader 0.6s infinite linear;
+          animation: loader 0.6s infinite linear;
+  border: 0.2em solid currentColor;
+  color: #FFFFFF;
+  -webkit-box-shadow: 0 0 0 1px transparent;
+          box-shadow: 0 0 0 1px transparent;
+}
+.ui.labeled.icon.loading.button .icon {
+  background-color: transparent;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+}
+.ui.basic.loading.button:not(.inverted):before {
+  border-color: rgba(0, 0, 0, 0.1);
+}
+.ui.basic.loading.button:not(.inverted):after {
+  border-color: #767676;
+}
+
+/*-------------------
+      Disabled
+--------------------*/
+
+.ui.buttons .disabled.button:not(.basic),
+.ui.disabled.button,
+.ui.button:disabled,
+.ui.disabled.button:hover,
+.ui.disabled.active.button {
+  cursor: default;
+  opacity: 0.45 !important;
+  background-image: none;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+  pointer-events: none !important;
+}
+
+/* Basic Group With Disabled */
+.ui.basic.buttons .ui.disabled.button {
+  border-color: rgba(34, 36, 38, 0.5);
+}
+
+
+/*******************************
+             Types
+*******************************/
+
+
+/*-------------------
+       Animated
+--------------------*/
+
+.ui.animated.button {
+  position: relative;
+  overflow: hidden;
+  padding-right: 0 !important;
+  vertical-align: middle;
+  z-index: 1;
+}
+.ui.animated.button .content {
+  will-change: transform, opacity;
+}
+.ui.animated.button .visible.content {
+  position: relative;
+  margin-right: 1.5em;
+}
+.ui.animated.button .hidden.content {
+  position: absolute;
+  width: 100%;
+}
+
+/* Horizontal */
+.ui.animated.button .visible.content,
+.ui.animated.button .hidden.content {
+  -webkit-transition: right 0.3s ease 0s;
+  transition: right 0.3s ease 0s;
+}
+.ui.animated.button .visible.content {
+  left: auto;
+  right: 0;
+}
+.ui.animated.button .hidden.content {
+  top: 50%;
+  left: auto;
+  right: -100%;
+  margin-top: -0.5em;
+}
+.ui.animated.button:focus .visible.content,
+.ui.animated.button:hover .visible.content {
+  left: auto;
+  right: 200%;
+}
+.ui.animated.button:focus .hidden.content,
+.ui.animated.button:hover .hidden.content {
+  left: auto;
+  right: 0;
+}
+
+/* Vertical */
+.ui.vertical.animated.button .visible.content,
+.ui.vertical.animated.button .hidden.content {
+  -webkit-transition: top 0.3s ease, -webkit-transform 0.3s ease;
+  transition: top 0.3s ease, -webkit-transform 0.3s ease;
+  transition: top 0.3s ease, transform 0.3s ease;
+  transition: top 0.3s ease, transform 0.3s ease, -webkit-transform 0.3s ease;
+}
+.ui.vertical.animated.button .visible.content {
+  -webkit-transform: translateY(0%);
+          transform: translateY(0%);
+  right: auto;
+}
+.ui.vertical.animated.button .hidden.content {
+  top: -50%;
+  left: 0;
+  right: auto;
+}
+.ui.vertical.animated.button:focus .visible.content,
+.ui.vertical.animated.button:hover .visible.content {
+  -webkit-transform: translateY(200%);
+          transform: translateY(200%);
+  right: auto;
+}
+.ui.vertical.animated.button:focus .hidden.content,
+.ui.vertical.animated.button:hover .hidden.content {
+  top: 50%;
+  right: auto;
+}
+
+/* Fade */
+.ui.fade.animated.button .visible.content,
+.ui.fade.animated.button .hidden.content {
+  -webkit-transition: opacity 0.3s ease, -webkit-transform 0.3s ease;
+  transition: opacity 0.3s ease, -webkit-transform 0.3s ease;
+  transition: opacity 0.3s ease, transform 0.3s ease;
+  transition: opacity 0.3s ease, transform 0.3s ease, -webkit-transform 0.3s ease;
+}
+.ui.fade.animated.button .visible.content {
+  left: auto;
+  right: auto;
+  opacity: 1;
+  -webkit-transform: scale(1);
+          transform: scale(1);
+}
+.ui.fade.animated.button .hidden.content {
+  opacity: 0;
+  left: 0;
+  right: auto;
+  -webkit-transform: scale(1.5);
+          transform: scale(1.5);
+}
+.ui.fade.animated.button:focus .visible.content,
+.ui.fade.animated.button:hover .visible.content {
+  left: auto;
+  right: auto;
+  opacity: 0;
+  -webkit-transform: scale(0.75);
+          transform: scale(0.75);
+}
+.ui.fade.animated.button:focus .hidden.content,
+.ui.fade.animated.button:hover .hidden.content {
+  left: 0;
+  right: auto;
+  opacity: 1;
+  -webkit-transform: scale(1);
+          transform: scale(1);
+}
+
+/*-------------------
+       Inverted
+--------------------*/
+
+.ui.inverted.button {
+  -webkit-box-shadow: 0 0 0 2px #FFFFFF inset;
+          box-shadow: 0 0 0 2px #FFFFFF inset;
+  background: transparent none;
+  color: #FFFFFF;
+  text-shadow: none !important;
+}
+
+/* Group */
+.ui.inverted.buttons .button {
+  margin: 0 0 0 -2px;
+}
+.ui.inverted.buttons .button:first-child {
+  margin-left: 0;
+}
+.ui.inverted.vertical.buttons .button {
+  margin: 0 0 -2px 0;
+}
+.ui.inverted.vertical.buttons .button:first-child {
+  margin-top: 0;
+}
+
+/* States */
+
+/* Hover */
+.ui.inverted.button:hover {
+  background: #FFFFFF;
+  -webkit-box-shadow: 0 0 0 2px #FFFFFF inset;
+          box-shadow: 0 0 0 2px #FFFFFF inset;
+  color: rgba(0, 0, 0, 0.8);
+}
+
+/* Active / Focus */
+.ui.inverted.button:focus,
+.ui.inverted.button.active {
+  background: #FFFFFF;
+  -webkit-box-shadow: 0 0 0 2px #FFFFFF inset;
+          box-shadow: 0 0 0 2px #FFFFFF inset;
+  color: rgba(0, 0, 0, 0.8);
+}
+
+/* Active Focus */
+.ui.inverted.button.active:focus {
+  background: #DCDDDE;
+  -webkit-box-shadow: 0 0 0 2px #DCDDDE inset;
+          box-shadow: 0 0 0 2px #DCDDDE inset;
+  color: rgba(0, 0, 0, 0.8);
+}
+
+/*-------------------
+    Labeled Button
+--------------------*/
+
+.ui.labeled.button:not(.icon) {
+  display: -webkit-inline-box;
+  display: -ms-inline-flexbox;
+  display: inline-flex;
+  -webkit-box-orient: horizontal;
+  -webkit-box-direction: normal;
+      -ms-flex-direction: row;
+          flex-direction: row;
+  background: none;
+  padding: 0 !important;
+  border: none;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+}
+.ui.labeled.button > .button {
+  margin: 0;
+}
+.ui.labeled.button > .label {
+  display: -webkit-box;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-box-align: center;
+      -ms-flex-align: center;
+          align-items: center;
+  margin: 0 0 0 -1px !important;
+  font-size: 1em;
+  padding: '';
+  border-color: rgba(34, 36, 38, 0.15);
+}
+
+/* Tag */
+.ui.labeled.button > .tag.label:before {
+  width: 1.85em;
+  height: 1.85em;
+}
+
+/* Right */
+.ui.labeled.button:not([class*="left labeled"]) > .button {
+  border-top-right-radius: 0;
+  border-bottom-right-radius: 0;
+}
+.ui.labeled.button:not([class*="left labeled"]) > .label {
+  border-top-left-radius: 0;
+  border-bottom-left-radius: 0;
+}
+
+/* Left Side */
+.ui[class*="left labeled"].button > .button {
+  border-top-left-radius: 0;
+  border-bottom-left-radius: 0;
+}
+.ui[class*="left labeled"].button > .label {
+  border-top-right-radius: 0;
+  border-bottom-right-radius: 0;
+}
+
+/*-------------------
+       Social
+--------------------*/
+
+
+/* Facebook */
+.ui.facebook.button {
+  background-color: #3B5998;
+  color: #FFFFFF;
+  text-shadow: none;
+  background-image: none;
+  -webkit-box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+}
+.ui.facebook.button:hover {
+  background-color: #304d8a;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.facebook.button:active {
+  background-color: #2d4373;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+
+/* Twitter */
+.ui.twitter.button {
+  background-color: #1DA1F2;
+  color: #FFFFFF;
+  text-shadow: none;
+  background-image: none;
+  -webkit-box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+}
+.ui.twitter.button:hover {
+  background-color: #0298f3;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.twitter.button:active {
+  background-color: #0c85d0;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+
+/* Google Plus */
+.ui.google.plus.button {
+  background-color: #DD4B39;
+  color: #FFFFFF;
+  text-shadow: none;
+  background-image: none;
+  -webkit-box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+}
+.ui.google.plus.button:hover {
+  background-color: #e0321c;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.google.plus.button:active {
+  background-color: #c23321;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+
+/* Linked In */
+.ui.linkedin.button {
+  background-color: #0077B5;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.linkedin.button:hover {
+  background-color: #00669c;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.linkedin.button:active {
+  background-color: #005582;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+
+/* YouTube */
+.ui.youtube.button {
+  background-color: #FF0000;
+  color: #FFFFFF;
+  text-shadow: none;
+  background-image: none;
+  -webkit-box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+}
+.ui.youtube.button:hover {
+  background-color: #e60000;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.youtube.button:active {
+  background-color: #cc0000;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+
+/* Instagram */
+.ui.instagram.button {
+  background-color: #49769C;
+  color: #FFFFFF;
+  text-shadow: none;
+  background-image: none;
+  -webkit-box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+}
+.ui.instagram.button:hover {
+  background-color: #3d698e;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.instagram.button:active {
+  background-color: #395c79;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+
+/* Pinterest */
+.ui.pinterest.button {
+  background-color: #BD081C;
+  color: #FFFFFF;
+  text-shadow: none;
+  background-image: none;
+  -webkit-box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+}
+.ui.pinterest.button:hover {
+  background-color: #ac0013;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.pinterest.button:active {
+  background-color: #8c0615;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+
+/* VK */
+.ui.vk.button {
+  background-color: #45668E;
+  color: #FFFFFF;
+  background-image: none;
+  -webkit-box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+}
+.ui.vk.button:hover {
+  background-color: #395980;
+  color: #FFFFFF;
+}
+.ui.vk.button:active {
+  background-color: #344d6c;
+  color: #FFFFFF;
+}
+
+/* WhatsApp */
+.ui.whatsapp.button {
+  background-color: #25D366;
+  color: #FFFFFF;
+  background-image: none;
+  -webkit-box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+}
+.ui.whatsapp.button:hover {
+  background-color: #19c55a;
+  color: #FFFFFF;
+}
+.ui.whatsapp.button:active {
+  background-color: #1da851;
+  color: #FFFFFF;
+}
+
+/* Telegram */
+.ui.telegram.button {
+  background-color: #0088CC;
+  color: #FFFFFF;
+  background-image: none;
+  -webkit-box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+}
+.ui.telegram.button:hover {
+  background-color: #0077b3;
+  color: #FFFFFF;
+}
+.ui.telegram.button:active {
+  background-color: #006699;
+  color: #FFFFFF;
+}
+
+/*--------------
+     Icon
+---------------*/
+
+.ui.button > .icon:not(.button) {
+  height: auto;
+  opacity: 0.8;
+  margin: 0 0.42857143em 0 -0.21428571em;
+  -webkit-transition: opacity 0.1s ease;
+  transition: opacity 0.1s ease;
+  vertical-align: '';
+  color: '';
+}
+.ui.button:not(.icon) > .icon:not(.button):not(.dropdown),
+.ui.button:not(.icon) > .icons:not(.button):not(.dropdown) {
+  margin: 0 0.42857143em 0 -0.21428571em;
+}
+.ui.button:not(.icon) > .right.icon:not(.button):not(.dropdown) {
+  margin: 0 -0.21428571em 0 0.42857143em;
+}
+
+
+/*******************************
+           Variations
+*******************************/
+
+
+/*-------------------
+       Floated
+--------------------*/
+
+.ui[class*="left floated"].buttons,
+.ui[class*="left floated"].button {
+  float: left;
+  margin-left: 0;
+  margin-right: 0.25em;
+}
+.ui[class*="right floated"].buttons,
+.ui[class*="right floated"].button {
+  float: right;
+  margin-right: 0;
+  margin-left: 0.25em;
+}
+
+/*-------------------
+       Compact
+--------------------*/
+
+.ui.compact.buttons .button,
+.ui.compact.button {
+  padding: 0.58928571em 1.125em 0.58928571em;
+}
+.ui.compact.icon.buttons .button,
+.ui.compact.icon.button {
+  padding: 0.58928571em 0.58928571em 0.58928571em;
+}
+.ui.compact.labeled.icon.buttons .button,
+.ui.compact.labeled.icon.button {
+  padding: 0.58928571em 3.69642857em 0.58928571em;
+}
+.ui.compact.labeled.icon.buttons .button > .icon,
+.ui.compact.labeled.icon.button > .icon {
+  padding: 0.58928571em 0 0.58928571em 0;
+}
+
+/*-------------------
+        Sizes
+--------------------*/
+
+.ui.mini.buttons .button,
+.ui.mini.buttons .or,
+.ui.ui.ui.ui.mini.button {
+  font-size: 0.78571429rem;
+}
+.ui.tiny.buttons .button,
+.ui.tiny.buttons .or,
+.ui.ui.ui.ui.tiny.button {
+  font-size: 0.85714286rem;
+}
+.ui.small.buttons .button,
+.ui.small.buttons .or,
+.ui.ui.ui.ui.small.button {
+  font-size: 0.92857143rem;
+}
+.ui.buttons .button,
+.ui.buttons .or,
+.ui.button {
+  font-size: 1rem;
+}
+.ui.large.buttons .button,
+.ui.large.buttons .or,
+.ui.ui.ui.ui.large.button {
+  font-size: 1.14285714rem;
+}
+.ui.big.buttons .button,
+.ui.big.buttons .or,
+.ui.ui.ui.ui.big.button {
+  font-size: 1.28571429rem;
+}
+.ui.huge.buttons .button,
+.ui.huge.buttons .or,
+.ui.ui.ui.ui.huge.button {
+  font-size: 1.42857143rem;
+}
+.ui.massive.buttons .button,
+.ui.massive.buttons .or,
+.ui.ui.ui.ui.massive.button {
+  font-size: 1.71428571rem;
+}
+
+/* Dropdown coupled buttons sizes */
+.ui.mini.buttons .dropdown {
+  font-size: 0.78571429rem;
+}
+.ui.mini.buttons .dropdown .menu > .item {
+  font-size: 0.78571429rem;
+}
+.ui.tiny.buttons .dropdown {
+  font-size: 0.85714286rem;
+}
+.ui.tiny.buttons .dropdown .menu > .item {
+  font-size: 0.85714286rem;
+}
+.ui.small.buttons .dropdown {
+  font-size: 0.92857143rem;
+}
+.ui.small.buttons .dropdown .menu > .item {
+  font-size: 0.92857143rem;
+}
+.ui.large.buttons .dropdown {
+  font-size: 1.14285714rem;
+}
+.ui.large.buttons .dropdown .menu > .item {
+  font-size: 1.14285714rem;
+}
+.ui.big.buttons .dropdown {
+  font-size: 1.28571429rem;
+}
+.ui.big.buttons .dropdown .menu > .item {
+  font-size: 1.28571429rem;
+}
+.ui.huge.buttons .dropdown {
+  font-size: 1.42857143rem;
+}
+.ui.huge.buttons .dropdown .menu > .item {
+  font-size: 1.42857143rem;
+}
+.ui.massive.buttons .dropdown {
+  font-size: 1.71428571rem;
+}
+.ui.massive.buttons .dropdown .menu > .item {
+  font-size: 1.71428571rem;
+}
+
+/*--------------
+    Icon Only
+---------------*/
+
+.ui.icon.buttons .button,
+.ui.icon.button {
+  padding: 0.78571429em 0.78571429em 0.78571429em;
+}
+.ui.icon.buttons .button > .icon,
+.ui.icon.button > .icon {
+  opacity: 0.9;
+  margin: 0 !important;
+  vertical-align: top;
+}
+
+/*-------------------
+        Basic
+--------------------*/
+
+.ui.basic.buttons .button,
+.ui.basic.button {
+  background: transparent none;
+  color: rgba(0, 0, 0, 0.6);
+  font-weight: normal;
+  border-radius: 0.28571429rem;
+  text-transform: none;
+  text-shadow: none !important;
+  -webkit-box-shadow: 0 0 0 1px rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 0 0 1px rgba(34, 36, 38, 0.15) inset;
+}
+.ui.basic.buttons {
+  -webkit-box-shadow: none;
+          box-shadow: none;
+  border: 1px solid rgba(34, 36, 38, 0.15);
+  border-radius: 0.28571429rem;
+}
+.ui.basic.buttons .button {
+  border-radius: 0;
+}
+.ui.basic.buttons .button:hover,
+.ui.basic.button:hover {
+  background: #FFFFFF;
+  color: rgba(0, 0, 0, 0.8);
+  -webkit-box-shadow: 0 0 0 1px rgba(34, 36, 38, 0.35) inset, 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 0 0 1px rgba(34, 36, 38, 0.35) inset, 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+}
+.ui.basic.buttons .button:focus,
+.ui.basic.button:focus {
+  background: #FFFFFF;
+  color: rgba(0, 0, 0, 0.8);
+  -webkit-box-shadow: 0 0 0 1px rgba(34, 36, 38, 0.35) inset, 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 0 0 1px rgba(34, 36, 38, 0.35) inset, 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+}
+.ui.basic.buttons .button:active,
+.ui.basic.button:active {
+  background: #F8F8F8;
+  color: rgba(0, 0, 0, 0.9);
+  -webkit-box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15) inset, 0 1px 4px 0 rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15) inset, 0 1px 4px 0 rgba(34, 36, 38, 0.15) inset;
+}
+.ui.basic.buttons .active.button,
+.ui.basic.active.button {
+  background: rgba(0, 0, 0, 0.05);
+  -webkit-box-shadow: '';
+          box-shadow: '';
+  color: rgba(0, 0, 0, 0.95);
+}
+.ui.basic.buttons .active.button:hover,
+.ui.basic.active.button:hover {
+  background-color: rgba(0, 0, 0, 0.05);
+}
+
+/* Vertical */
+.ui.basic.buttons .button:hover {
+  -webkit-box-shadow: 0 0 0 1px rgba(34, 36, 38, 0.35) inset, 0 0 0 0 rgba(34, 36, 38, 0.15) inset inset;
+          box-shadow: 0 0 0 1px rgba(34, 36, 38, 0.35) inset, 0 0 0 0 rgba(34, 36, 38, 0.15) inset inset;
+}
+.ui.basic.buttons .button:active {
+  -webkit-box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15) inset, 0 1px 4px 0 rgba(34, 36, 38, 0.15) inset inset;
+          box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15) inset, 0 1px 4px 0 rgba(34, 36, 38, 0.15) inset inset;
+}
+.ui.basic.buttons .active.button {
+  -webkit-box-shadow: '';
+          box-shadow: '';
+}
+
+/* Standard Basic Inverted */
+.ui.basic.inverted.buttons .button,
+.ui.basic.inverted.button {
+  background-color: transparent;
+  color: #F9FAFB;
+  -webkit-box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) inset;
+          box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) inset;
+}
+.ui.basic.inverted.buttons .button:hover,
+.ui.basic.inverted.button:hover {
+  color: #FFFFFF;
+  -webkit-box-shadow: 0 0 0 2px #ffffff inset;
+          box-shadow: 0 0 0 2px #ffffff inset;
+}
+.ui.basic.inverted.buttons .button:focus,
+.ui.basic.inverted.button:focus {
+  color: #FFFFFF;
+  -webkit-box-shadow: 0 0 0 2px #ffffff inset;
+          box-shadow: 0 0 0 2px #ffffff inset;
+}
+.ui.basic.inverted.buttons .button:active,
+.ui.basic.inverted.button:active {
+  background-color: rgba(255, 255, 255, 0.08);
+  color: #FFFFFF;
+  -webkit-box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.9) inset;
+          box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.9) inset;
+}
+.ui.basic.inverted.buttons .active.button,
+.ui.basic.inverted.active.button {
+  background-color: rgba(255, 255, 255, 0.08);
+  color: #FFFFFF;
+  text-shadow: none;
+  -webkit-box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.7) inset;
+          box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.7) inset;
+}
+.ui.basic.inverted.buttons .active.button:hover,
+.ui.basic.inverted.active.button:hover {
+  background-color: rgba(255, 255, 255, 0.15);
+  -webkit-box-shadow: 0 0 0 2px #ffffff inset;
+          box-shadow: 0 0 0 2px #ffffff inset;
+}
+
+/* Basic Group */
+.ui.basic.buttons .button {
+  border-left: 1px solid rgba(34, 36, 38, 0.15);
+  -webkit-box-shadow: none;
+          box-shadow: none;
+}
+.ui.basic.vertical.buttons .button {
+  border-left: none;
+  border-left-width: 0;
+  border-top: 1px solid rgba(34, 36, 38, 0.15);
+}
+.ui.basic.vertical.buttons .button:first-child {
+  border-top-width: 0;
+}
+
+/*-------------------
+       Tertiary
+--------------------*/
+
+
+/* Overline Mixin */
+.ui.tertiary.button {
+  -webkit-transition: color 0.1s ease !important;
+  transition: color 0.1s ease !important;
+  border-radius: 0;
+  margin: 0.28571429em 0.25em 0.28571429em 0 !important;
+  padding: 0.5em !important;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+  color: rgba(0, 0, 0, 0.6);
+  background: none;
+}
+.ui.tertiary.button:hover {
+  -webkit-box-shadow: inset 0 -0.2em 0 #666666;
+          box-shadow: inset 0 -0.2em 0 #666666;
+  color: #333333;
+  background: none;
+}
+.ui.tertiary.button:focus {
+  -webkit-box-shadow: inset 0 -0.2em 0 #666666;
+          box-shadow: inset 0 -0.2em 0 #666666;
+  color: #333333;
+  background: none;
+}
+.ui.tertiary.button:active {
+  -webkit-box-shadow: inset 0 -0.2em 0 #999999;
+          box-shadow: inset 0 -0.2em 0 #999999;
+  border-radius: 0.28571429rem 0.28571429rem 0 0;
+  color: #666666;
+  background: none;
+}
+
+/*--------------
+  Labeled Icon
+---------------*/
+
+.ui.labeled.icon.buttons .button,
+.ui.labeled.icon.button {
+  position: relative;
+  padding-left: 4.07142857em !important;
+  padding-right: 1.5em !important;
+}
+
+/* Left Labeled */
+.ui.labeled.icon.buttons > .button > .icon,
+.ui.labeled.icon.button > .icon {
+  position: absolute;
+  top: 0;
+  left: 0;
+  height: 100%;
+  line-height: 1;
+  border-radius: 0;
+  border-top-left-radius: inherit;
+  border-bottom-left-radius: inherit;
+  text-align: center;
+  -webkit-animation: none;
+          animation: none;
+  padding: 0.78571429em 0 0.78571429em 0;
+  margin: 0;
+  width: 2.57142857em;
+  background-color: rgba(0, 0, 0, 0.05);
+  color: '';
+  -webkit-box-shadow: -1px 0 0 0 transparent inset;
+          box-shadow: -1px 0 0 0 transparent inset;
+}
+
+/* Right Labeled */
+.ui[class*="right labeled"].icon.button {
+  padding-right: 4.07142857em !important;
+  padding-left: 1.5em !important;
+}
+.ui[class*="right labeled"].icon.button > .icon {
+  left: auto;
+  right: 0;
+  border-radius: 0;
+  border-top-right-radius: inherit;
+  border-bottom-right-radius: inherit;
+  -webkit-box-shadow: 1px 0 0 0 transparent inset;
+          box-shadow: 1px 0 0 0 transparent inset;
+}
+.ui.labeled.icon.buttons > .button > .icon:before,
+.ui.labeled.icon.button > .icon:before,
+.ui.labeled.icon.buttons > .button > .icon:after,
+.ui.labeled.icon.button > .icon:after {
+  display: block;
+  position: relative;
+  width: 100%;
+  top: 0;
+  text-align: center;
+}
+.ui.labeled.icon.buttons .button > .icon {
+  border-radius: 0;
+}
+.ui.labeled.icon.buttons .button:first-child > .icon {
+  border-top-left-radius: 0.28571429rem;
+  border-bottom-left-radius: 0.28571429rem;
+}
+.ui.labeled.icon.buttons .button:last-child > .icon {
+  border-top-right-radius: 0.28571429rem;
+  border-bottom-right-radius: 0.28571429rem;
+}
+.ui.vertical.labeled.icon.buttons .button:first-child > .icon {
+  border-radius: 0;
+  border-top-left-radius: 0.28571429rem;
+}
+.ui.vertical.labeled.icon.buttons .button:last-child > .icon {
+  border-radius: 0;
+  border-bottom-left-radius: 0.28571429rem;
+}
+
+/* Loading Icon in Labeled Button */
+.ui.labeled.icon.button > .loading.icon:before {
+  -webkit-animation: loader 2s linear infinite;
+          animation: loader 2s linear infinite;
+}
+
+/*--------------
+     Toggle
+---------------*/
+
+
+/* Toggle (Modifies active state to give affordances) */
+.ui.toggle.buttons .active.button,
+.ui.buttons .button.toggle.active,
+.ui.button.toggle.active {
+  background-color: #21BA45;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+  text-shadow: none;
+  color: #FFFFFF;
+}
+.ui.button.toggle.active:hover {
+  background-color: #16ab39;
+  text-shadow: none;
+  color: #FFFFFF;
+}
+
+/*--------------
+    Circular
+---------------*/
+
+.ui.circular.button {
+  border-radius: 10em;
+}
+.ui.circular.button > .icon {
+  width: 1em;
+  vertical-align: baseline;
+}
+
+/*-------------------
+      Or Buttons
+--------------------*/
+
+.ui.buttons .or {
+  position: relative;
+  width: 0.3em;
+  height: 2.57142857em;
+  z-index: 3;
+}
+.ui.buttons .or:before {
+  position: absolute;
+  text-align: center;
+  border-radius: 500rem;
+  content: 'or';
+  top: 50%;
+  left: 50%;
+  background-color: #FFFFFF;
+  text-shadow: none;
+  margin-top: -0.89285714em;
+  margin-left: -0.89285714em;
+  width: 1.78571429em;
+  height: 1.78571429em;
+  line-height: 1.78571429em;
+  color: rgba(0, 0, 0, 0.4);
+  font-style: normal;
+  font-weight: bold;
+  -webkit-box-shadow: 0 0 0 1px transparent inset;
+          box-shadow: 0 0 0 1px transparent inset;
+}
+.ui.buttons .or[data-text]:before {
+  content: attr(data-text);
+}
+
+/* Fluid Or */
+.ui.fluid.buttons .or {
+  width: 0 !important;
+}
+.ui.fluid.buttons .or:after {
+  display: none;
+}
+
+/*-------------------
+       Attached
+--------------------*/
+
+
+/* Singular */
+.ui.attached.button {
+  position: relative;
+  display: block;
+  margin: 0;
+  border-radius: 0;
+  -webkit-box-shadow: 0 0 0 1px rgba(34, 36, 38, 0.15);
+          box-shadow: 0 0 0 1px rgba(34, 36, 38, 0.15);
+}
+
+/* Top / Bottom */
+.ui.attached.top.button {
+  border-radius: 0.28571429rem 0.28571429rem 0 0;
+}
+.ui.attached.bottom.button {
+  border-radius: 0 0 0.28571429rem 0.28571429rem;
+}
+
+/* Left / Right */
+.ui.left.attached.button {
+  display: inline-block;
+  border-left: none;
+  text-align: right;
+  padding-right: 0.75em;
+  border-radius: 0.28571429rem 0 0 0.28571429rem;
+}
+.ui.right.attached.button {
+  display: inline-block;
+  text-align: left;
+  padding-left: 0.75em;
+  border-radius: 0 0.28571429rem 0.28571429rem 0;
+}
+
+/* Plural */
+.ui.attached.buttons {
+  position: relative;
+  display: -webkit-box;
+  display: -ms-flexbox;
+  display: flex;
+  border-radius: 0;
+  width: auto !important;
+  z-index: auto;
+  margin-left: -1px;
+  margin-right: -1px;
+}
+.ui.attached.buttons .button {
+  margin: 0;
+}
+.ui.attached.buttons .button:first-child {
+  border-radius: 0;
+}
+.ui.attached.buttons .button:last-child {
+  border-radius: 0;
+}
+
+/* Top / Bottom */
+.ui[class*="top attached"].buttons {
+  margin-bottom: -1px;
+  border-radius: 0.28571429rem 0.28571429rem 0 0;
+}
+.ui[class*="top attached"].buttons .button:first-child {
+  border-radius: 0.28571429rem 0 0 0;
+}
+.ui[class*="top attached"].buttons .button:last-child {
+  border-radius: 0 0.28571429rem 0 0;
+}
+.ui[class*="bottom attached"].buttons {
+  margin-top: -1px;
+  border-radius: 0 0 0.28571429rem 0.28571429rem;
+}
+.ui[class*="bottom attached"].buttons .button:first-child {
+  border-radius: 0 0 0 0.28571429rem;
+}
+.ui[class*="bottom attached"].buttons .button:last-child {
+  border-radius: 0 0 0.28571429rem 0;
+}
+
+/* Left / Right */
+.ui[class*="left attached"].buttons {
+  display: -webkit-inline-box;
+  display: -ms-inline-flexbox;
+  display: inline-flex;
+  margin-right: 0;
+  margin-left: -1px;
+  border-radius: 0 0.28571429rem 0.28571429rem 0;
+}
+.ui[class*="left attached"].buttons .button:first-child {
+  margin-left: -1px;
+  border-radius: 0 0.28571429rem 0 0;
+}
+.ui[class*="left attached"].buttons .button:last-child {
+  margin-left: -1px;
+  border-radius: 0 0 0.28571429rem 0;
+}
+.ui[class*="right attached"].buttons {
+  display: -webkit-inline-box;
+  display: -ms-inline-flexbox;
+  display: inline-flex;
+  margin-left: 0;
+  margin-right: -1px;
+  border-radius: 0.28571429rem 0 0 0.28571429rem;
+}
+.ui[class*="right attached"].buttons .button:first-child {
+  margin-left: -1px;
+  border-radius: 0.28571429rem 0 0 0;
+}
+.ui[class*="right attached"].buttons .button:last-child {
+  margin-left: -1px;
+  border-radius: 0 0 0 0.28571429rem;
+}
+
+/*-------------------
+        Fluid
+--------------------*/
+
+.ui.fluid.buttons,
+.ui.fluid.button {
+  width: 100%;
+}
+.ui.fluid.button {
+  display: block;
+}
+.ui.two.buttons {
+  width: 100%;
+}
+.ui.two.buttons > .button {
+  width: 50%;
+}
+.ui.three.buttons {
+  width: 100%;
+}
+.ui.three.buttons > .button {
+  width: 33.333%;
+}
+.ui.four.buttons {
+  width: 100%;
+}
+.ui.four.buttons > .button {
+  width: 25%;
+}
+.ui.five.buttons {
+  width: 100%;
+}
+.ui.five.buttons > .button {
+  width: 20%;
+}
+.ui.six.buttons {
+  width: 100%;
+}
+.ui.six.buttons > .button {
+  width: 16.666%;
+}
+.ui.seven.buttons {
+  width: 100%;
+}
+.ui.seven.buttons > .button {
+  width: 14.285%;
+}
+.ui.eight.buttons {
+  width: 100%;
+}
+.ui.eight.buttons > .button {
+  width: 12.5%;
+}
+.ui.nine.buttons {
+  width: 100%;
+}
+.ui.nine.buttons > .button {
+  width: 11.11%;
+}
+.ui.ten.buttons {
+  width: 100%;
+}
+.ui.ten.buttons > .button {
+  width: 10%;
+}
+.ui.eleven.buttons {
+  width: 100%;
+}
+.ui.eleven.buttons > .button {
+  width: 9.09%;
+}
+.ui.twelve.buttons {
+  width: 100%;
+}
+.ui.twelve.buttons > .button {
+  width: 8.3333%;
+}
+
+/* Fluid Vertical Buttons */
+.ui.fluid.vertical.buttons,
+.ui.fluid.vertical.buttons > .button {
+  display: -webkit-box;
+  display: -ms-flexbox;
+  display: flex;
+  width: auto;
+  -webkit-box-pack: center;
+      -ms-flex-pack: center;
+          justify-content: center;
+}
+.ui.two.vertical.buttons > .button {
+  height: 50%;
+}
+.ui.three.vertical.buttons > .button {
+  height: 33.333%;
+}
+.ui.four.vertical.buttons > .button {
+  height: 25%;
+}
+.ui.five.vertical.buttons > .button {
+  height: 20%;
+}
+.ui.six.vertical.buttons > .button {
+  height: 16.666%;
+}
+.ui.seven.vertical.buttons > .button {
+  height: 14.285%;
+}
+.ui.eight.vertical.buttons > .button {
+  height: 12.5%;
+}
+.ui.nine.vertical.buttons > .button {
+  height: 11.11%;
+}
+.ui.ten.vertical.buttons > .button {
+  height: 10%;
+}
+.ui.eleven.vertical.buttons > .button {
+  height: 9.09%;
+}
+.ui.twelve.vertical.buttons > .button {
+  height: 8.3333%;
+}
+
+/*-------------------
+       Colors
+--------------------*/
+
+.ui.primary.buttons .button,
+.ui.primary.button {
+  background-color: #2185D0;
+  color: #FFFFFF;
+  text-shadow: none;
+  background-image: none;
+}
+.ui.primary.button {
+  -webkit-box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+}
+.ui.primary.buttons .button:hover,
+.ui.primary.button:hover {
+  background-color: #1678c2;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.primary.buttons .button:focus,
+.ui.primary.button:focus {
+  background-color: #0d71bb;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.primary.buttons .button:active,
+.ui.primary.button:active {
+  background-color: #1a69a4;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.primary.buttons .active.button,
+.ui.primary.buttons .active.button:active,
+.ui.primary.active.button,
+.ui.primary.button .active.button:active {
+  background-color: #1279c6;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+
+/* Basic */
+.ui.basic.primary.buttons .button,
+.ui.basic.primary.button {
+  background: transparent;
+  -webkit-box-shadow: 0 0 0 1px #2185D0 inset;
+          box-shadow: 0 0 0 1px #2185D0 inset;
+  color: #2185D0;
+}
+.ui.basic.primary.buttons .button:hover,
+.ui.basic.primary.button:hover {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #1678c2 inset;
+          box-shadow: 0 0 0 1px #1678c2 inset;
+  color: #1678c2;
+}
+.ui.basic.primary.buttons .button:focus,
+.ui.basic.primary.button:focus {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #0d71bb inset;
+          box-shadow: 0 0 0 1px #0d71bb inset;
+  color: #1678c2;
+}
+.ui.basic.primary.buttons .active.button,
+.ui.basic.primary.active.button {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #1279c6 inset;
+          box-shadow: 0 0 0 1px #1279c6 inset;
+  color: #1a69a4;
+}
+.ui.basic.primary.buttons .button:active,
+.ui.basic.primary.button:active {
+  -webkit-box-shadow: 0 0 0 1px #1a69a4 inset;
+          box-shadow: 0 0 0 1px #1a69a4 inset;
+  color: #1a69a4;
+}
+.ui.buttons:not(.vertical) > .basic.primary.button:not(:first-child) {
+  margin-left: -1px;
+}
+
+/* Inverted */
+.ui.inverted.primary.buttons .button,
+.ui.inverted.primary.button {
+  background-color: transparent;
+  -webkit-box-shadow: 0 0 0 2px #54C8FF inset;
+          box-shadow: 0 0 0 2px #54C8FF inset;
+  color: #54C8FF;
+}
+.ui.inverted.primary.buttons .button:hover,
+.ui.inverted.primary.button:hover,
+.ui.inverted.primary.buttons .button:focus,
+.ui.inverted.primary.button:focus,
+.ui.inverted.primary.buttons .button.active,
+.ui.inverted.primary.button.active,
+.ui.inverted.primary.buttons .button:active,
+.ui.inverted.primary.button:active {
+  -webkit-box-shadow: none ;
+          box-shadow: none ;
+  color: #FFFFFF;
+}
+.ui.inverted.primary.buttons .button:hover,
+.ui.inverted.primary.button:hover {
+  background-color: #21b8ff;
+}
+.ui.inverted.primary.buttons .button:focus,
+.ui.inverted.primary.button:focus {
+  background-color: #2bbbff;
+}
+.ui.inverted.primary.buttons .active.button,
+.ui.inverted.primary.active.button {
+  background-color: #3ac0ff;
+}
+.ui.inverted.primary.buttons .button:active,
+.ui.inverted.primary.button:active {
+  background-color: #21b8ff;
+}
+
+/* Inverted Basic */
+.ui.inverted.primary.basic.buttons .button,
+.ui.inverted.primary.buttons .basic.button,
+.ui.inverted.primary.basic.button {
+  background-color: transparent;
+  -webkit-box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) inset;
+          box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) inset;
+  color: #FFFFFF;
+}
+.ui.inverted.primary.basic.buttons .button:hover,
+.ui.inverted.primary.buttons .basic.button:hover,
+.ui.inverted.primary.basic.button:hover {
+  -webkit-box-shadow: 0 0 0 2px #21b8ff inset;
+          box-shadow: 0 0 0 2px #21b8ff inset;
+  color: #54C8FF;
+}
+.ui.inverted.primary.basic.buttons .button:focus,
+.ui.inverted.primary.basic.buttons .button:focus,
+.ui.inverted.primary.basic.button:focus {
+  -webkit-box-shadow: 0 0 0 2px #2bbbff inset;
+          box-shadow: 0 0 0 2px #2bbbff inset;
+  color: #54C8FF;
+}
+.ui.inverted.primary.basic.buttons .active.button,
+.ui.inverted.primary.buttons .basic.active.button,
+.ui.inverted.primary.basic.active.button {
+  -webkit-box-shadow: 0 0 0 2px #3ac0ff inset;
+          box-shadow: 0 0 0 2px #3ac0ff inset;
+  color: #54C8FF;
+}
+.ui.inverted.primary.basic.buttons .button:active,
+.ui.inverted.primary.buttons .basic.button:active,
+.ui.inverted.primary.basic.button:active {
+  -webkit-box-shadow: 0 0 0 2px #21b8ff inset;
+          box-shadow: 0 0 0 2px #21b8ff inset;
+  color: #54C8FF;
+}
+
+/* Tertiary */
+.ui.tertiary.primary.buttons .button,
+.ui.tertiary.primary.buttons .tertiary.button,
+.ui.tertiary.primary.button {
+  background: transparent;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+  color: #2185D0;
+}
+.ui.tertiary.primary.buttons .button:hover,
+.ui.tertiary.primary.buttons button:hover,
+.ui.tertiary.primary.button:hover {
+  -webkit-box-shadow: inset 0 -0.2em 0 #2b75ac;
+          box-shadow: inset 0 -0.2em 0 #2b75ac;
+  color: #2b75ac;
+}
+.ui.tertiary.primary.buttons .button:focus,
+.ui.tertiary.primary.buttons .tertiary.button:focus,
+.ui.tertiary.primary.button:focus {
+  -webkit-box-shadow: inset 0 -0.2em 0 #216ea7;
+          box-shadow: inset 0 -0.2em 0 #216ea7;
+  color: #216ea7;
+}
+.ui.tertiary.primary.buttons .active.button,
+.ui.tertiary.primary.buttons .tertiary.active.button,
+.ui.tertiary.primary.active.button,
+.ui.tertiary.primary.buttons .button:active,
+.ui.tertiary.primary.buttons .tertiary.button:active,
+.ui.tertiary.primary.button:active {
+  -webkit-box-shadow: inset 0 -0.2em 0 #007bd8;
+          box-shadow: inset 0 -0.2em 0 #007bd8;
+  color: #1279c6;
+}
+.ui.secondary.buttons .button,
+.ui.secondary.button {
+  background-color: #1B1C1D;
+  color: #FFFFFF;
+  text-shadow: none;
+  background-image: none;
+}
+.ui.secondary.button {
+  -webkit-box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+}
+.ui.secondary.buttons .button:hover,
+.ui.secondary.button:hover {
+  background-color: #27292a;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.secondary.buttons .button:focus,
+.ui.secondary.button:focus {
+  background-color: #2e3032;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.secondary.buttons .button:active,
+.ui.secondary.button:active {
+  background-color: #343637;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.secondary.buttons .active.button,
+.ui.secondary.buttons .active.button:active,
+.ui.secondary.active.button,
+.ui.secondary.button .active.button:active {
+  background-color: #27292a;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+
+/* Basic */
+.ui.basic.secondary.buttons .button,
+.ui.basic.secondary.button {
+  background: transparent;
+  -webkit-box-shadow: 0 0 0 1px #1B1C1D inset;
+          box-shadow: 0 0 0 1px #1B1C1D inset;
+  color: #1B1C1D;
+}
+.ui.basic.secondary.buttons .button:hover,
+.ui.basic.secondary.button:hover {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #27292a inset;
+          box-shadow: 0 0 0 1px #27292a inset;
+  color: #27292a;
+}
+.ui.basic.secondary.buttons .button:focus,
+.ui.basic.secondary.button:focus {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #2e3032 inset;
+          box-shadow: 0 0 0 1px #2e3032 inset;
+  color: #27292a;
+}
+.ui.basic.secondary.buttons .active.button,
+.ui.basic.secondary.active.button {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #27292a inset;
+          box-shadow: 0 0 0 1px #27292a inset;
+  color: #343637;
+}
+.ui.basic.secondary.buttons .button:active,
+.ui.basic.secondary.button:active {
+  -webkit-box-shadow: 0 0 0 1px #343637 inset;
+          box-shadow: 0 0 0 1px #343637 inset;
+  color: #343637;
+}
+.ui.buttons:not(.vertical) > .basic.secondary.button:not(:first-child) {
+  margin-left: -1px;
+}
+
+/* Inverted */
+.ui.inverted.secondary.buttons .button,
+.ui.inverted.secondary.button {
+  background-color: transparent;
+  -webkit-box-shadow: 0 0 0 2px #545454 inset;
+          box-shadow: 0 0 0 2px #545454 inset;
+  color: #545454;
+}
+.ui.inverted.secondary.buttons .button:hover,
+.ui.inverted.secondary.button:hover,
+.ui.inverted.secondary.buttons .button:focus,
+.ui.inverted.secondary.button:focus,
+.ui.inverted.secondary.buttons .button.active,
+.ui.inverted.secondary.button.active,
+.ui.inverted.secondary.buttons .button:active,
+.ui.inverted.secondary.button:active {
+  -webkit-box-shadow: none ;
+          box-shadow: none ;
+  color: #FFFFFF;
+}
+.ui.inverted.secondary.buttons .button:hover,
+.ui.inverted.secondary.button:hover {
+  background-color: #6e6e6e;
+}
+.ui.inverted.secondary.buttons .button:focus,
+.ui.inverted.secondary.button:focus {
+  background-color: #686868;
+}
+.ui.inverted.secondary.buttons .active.button,
+.ui.inverted.secondary.active.button {
+  background-color: #616161;
+}
+.ui.inverted.secondary.buttons .button:active,
+.ui.inverted.secondary.button:active {
+  background-color: #6e6e6e;
+}
+
+/* Inverted Basic */
+.ui.inverted.secondary.basic.buttons .button,
+.ui.inverted.secondary.buttons .basic.button,
+.ui.inverted.secondary.basic.button {
+  background-color: transparent;
+  -webkit-box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) inset;
+          box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) inset;
+  color: #FFFFFF;
+}
+.ui.inverted.secondary.basic.buttons .button:hover,
+.ui.inverted.secondary.buttons .basic.button:hover,
+.ui.inverted.secondary.basic.button:hover {
+  -webkit-box-shadow: 0 0 0 2px #6e6e6e inset;
+          box-shadow: 0 0 0 2px #6e6e6e inset;
+  color: #545454;
+}
+.ui.inverted.secondary.basic.buttons .button:focus,
+.ui.inverted.secondary.basic.buttons .button:focus,
+.ui.inverted.secondary.basic.button:focus {
+  -webkit-box-shadow: 0 0 0 2px #686868 inset;
+          box-shadow: 0 0 0 2px #686868 inset;
+  color: #545454;
+}
+.ui.inverted.secondary.basic.buttons .active.button,
+.ui.inverted.secondary.buttons .basic.active.button,
+.ui.inverted.secondary.basic.active.button {
+  -webkit-box-shadow: 0 0 0 2px #616161 inset;
+          box-shadow: 0 0 0 2px #616161 inset;
+  color: #545454;
+}
+.ui.inverted.secondary.basic.buttons .button:active,
+.ui.inverted.secondary.buttons .basic.button:active,
+.ui.inverted.secondary.basic.button:active {
+  -webkit-box-shadow: 0 0 0 2px #6e6e6e inset;
+          box-shadow: 0 0 0 2px #6e6e6e inset;
+  color: #545454;
+}
+
+/* Tertiary */
+.ui.tertiary.secondary.buttons .button,
+.ui.tertiary.secondary.buttons .tertiary.button,
+.ui.tertiary.secondary.button {
+  background: transparent;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+  color: #1B1C1D;
+}
+.ui.tertiary.secondary.buttons .button:hover,
+.ui.tertiary.secondary.buttons button:hover,
+.ui.tertiary.secondary.button:hover {
+  -webkit-box-shadow: inset 0 -0.2em 0 #292929;
+          box-shadow: inset 0 -0.2em 0 #292929;
+  color: #292929;
+}
+.ui.tertiary.secondary.buttons .button:focus,
+.ui.tertiary.secondary.buttons .tertiary.button:focus,
+.ui.tertiary.secondary.button:focus {
+  -webkit-box-shadow: inset 0 -0.2em 0 #303030;
+          box-shadow: inset 0 -0.2em 0 #303030;
+  color: #303030;
+}
+.ui.tertiary.secondary.buttons .active.button,
+.ui.tertiary.secondary.buttons .tertiary.active.button,
+.ui.tertiary.secondary.active.button,
+.ui.tertiary.secondary.buttons .button:active,
+.ui.tertiary.secondary.buttons .tertiary.button:active,
+.ui.tertiary.secondary.button:active {
+  -webkit-box-shadow: inset 0 -0.2em 0 #1f2933;
+          box-shadow: inset 0 -0.2em 0 #1f2933;
+  color: #27292a;
+}
+.ui.red.buttons .button,
+.ui.red.button {
+  background-color: #DB2828;
+  color: #FFFFFF;
+  text-shadow: none;
+  background-image: none;
+}
+.ui.red.button {
+  -webkit-box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+}
+.ui.red.buttons .button:hover,
+.ui.red.button:hover {
+  background-color: #d01919;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.red.buttons .button:focus,
+.ui.red.button:focus {
+  background-color: #ca1010;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.red.buttons .button:active,
+.ui.red.button:active {
+  background-color: #b21e1e;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.red.buttons .active.button,
+.ui.red.buttons .active.button:active,
+.ui.red.active.button,
+.ui.red.button .active.button:active {
+  background-color: #d41515;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+
+/* Basic */
+.ui.basic.red.buttons .button,
+.ui.basic.red.button {
+  background: transparent;
+  -webkit-box-shadow: 0 0 0 1px #DB2828 inset;
+          box-shadow: 0 0 0 1px #DB2828 inset;
+  color: #DB2828;
+}
+.ui.basic.red.buttons .button:hover,
+.ui.basic.red.button:hover {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #d01919 inset;
+          box-shadow: 0 0 0 1px #d01919 inset;
+  color: #d01919;
+}
+.ui.basic.red.buttons .button:focus,
+.ui.basic.red.button:focus {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #ca1010 inset;
+          box-shadow: 0 0 0 1px #ca1010 inset;
+  color: #d01919;
+}
+.ui.basic.red.buttons .active.button,
+.ui.basic.red.active.button {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #d41515 inset;
+          box-shadow: 0 0 0 1px #d41515 inset;
+  color: #b21e1e;
+}
+.ui.basic.red.buttons .button:active,
+.ui.basic.red.button:active {
+  -webkit-box-shadow: 0 0 0 1px #b21e1e inset;
+          box-shadow: 0 0 0 1px #b21e1e inset;
+  color: #b21e1e;
+}
+.ui.buttons:not(.vertical) > .basic.red.button:not(:first-child) {
+  margin-left: -1px;
+}
+
+/* Inverted */
+.ui.inverted.red.buttons .button,
+.ui.inverted.red.button {
+  background-color: transparent;
+  -webkit-box-shadow: 0 0 0 2px #FF695E inset;
+          box-shadow: 0 0 0 2px #FF695E inset;
+  color: #FF695E;
+}
+.ui.inverted.red.buttons .button:hover,
+.ui.inverted.red.button:hover,
+.ui.inverted.red.buttons .button:focus,
+.ui.inverted.red.button:focus,
+.ui.inverted.red.buttons .button.active,
+.ui.inverted.red.button.active,
+.ui.inverted.red.buttons .button:active,
+.ui.inverted.red.button:active {
+  -webkit-box-shadow: none ;
+          box-shadow: none ;
+  color: #FFFFFF;
+}
+.ui.inverted.red.buttons .button:hover,
+.ui.inverted.red.button:hover {
+  background-color: #ff392b;
+}
+.ui.inverted.red.buttons .button:focus,
+.ui.inverted.red.button:focus {
+  background-color: #ff4335;
+}
+.ui.inverted.red.buttons .active.button,
+.ui.inverted.red.active.button {
+  background-color: #ff5144;
+}
+.ui.inverted.red.buttons .button:active,
+.ui.inverted.red.button:active {
+  background-color: #ff392b;
+}
+
+/* Inverted Basic */
+.ui.inverted.red.basic.buttons .button,
+.ui.inverted.red.buttons .basic.button,
+.ui.inverted.red.basic.button {
+  background-color: transparent;
+  -webkit-box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) inset;
+          box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) inset;
+  color: #FFFFFF;
+}
+.ui.inverted.red.basic.buttons .button:hover,
+.ui.inverted.red.buttons .basic.button:hover,
+.ui.inverted.red.basic.button:hover {
+  -webkit-box-shadow: 0 0 0 2px #ff392b inset;
+          box-shadow: 0 0 0 2px #ff392b inset;
+  color: #FF695E;
+}
+.ui.inverted.red.basic.buttons .button:focus,
+.ui.inverted.red.basic.buttons .button:focus,
+.ui.inverted.red.basic.button:focus {
+  -webkit-box-shadow: 0 0 0 2px #ff4335 inset;
+          box-shadow: 0 0 0 2px #ff4335 inset;
+  color: #FF695E;
+}
+.ui.inverted.red.basic.buttons .active.button,
+.ui.inverted.red.buttons .basic.active.button,
+.ui.inverted.red.basic.active.button {
+  -webkit-box-shadow: 0 0 0 2px #ff5144 inset;
+          box-shadow: 0 0 0 2px #ff5144 inset;
+  color: #FF695E;
+}
+.ui.inverted.red.basic.buttons .button:active,
+.ui.inverted.red.buttons .basic.button:active,
+.ui.inverted.red.basic.button:active {
+  -webkit-box-shadow: 0 0 0 2px #ff392b inset;
+          box-shadow: 0 0 0 2px #ff392b inset;
+  color: #FF695E;
+}
+
+/* Tertiary */
+.ui.tertiary.red.buttons .button,
+.ui.tertiary.red.buttons .tertiary.button,
+.ui.tertiary.red.button {
+  background: transparent;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+  color: #DB2828;
+}
+.ui.tertiary.red.buttons .button:hover,
+.ui.tertiary.red.buttons button:hover,
+.ui.tertiary.red.button:hover {
+  -webkit-box-shadow: inset 0 -0.2em 0 #b93131;
+          box-shadow: inset 0 -0.2em 0 #b93131;
+  color: #b93131;
+}
+.ui.tertiary.red.buttons .button:focus,
+.ui.tertiary.red.buttons .tertiary.button:focus,
+.ui.tertiary.red.button:focus {
+  -webkit-box-shadow: inset 0 -0.2em 0 #b52626;
+          box-shadow: inset 0 -0.2em 0 #b52626;
+  color: #b52626;
+}
+.ui.tertiary.red.buttons .active.button,
+.ui.tertiary.red.buttons .tertiary.active.button,
+.ui.tertiary.red.active.button,
+.ui.tertiary.red.buttons .button:active,
+.ui.tertiary.red.buttons .tertiary.button:active,
+.ui.tertiary.red.button:active {
+  -webkit-box-shadow: inset 0 -0.2em 0 #ea0000;
+          box-shadow: inset 0 -0.2em 0 #ea0000;
+  color: #d41515;
+}
+.ui.orange.buttons .button,
+.ui.orange.button {
+  background-color: #F2711C;
+  color: #FFFFFF;
+  text-shadow: none;
+  background-image: none;
+}
+.ui.orange.button {
+  -webkit-box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+}
+.ui.orange.buttons .button:hover,
+.ui.orange.button:hover {
+  background-color: #f26202;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.orange.buttons .button:focus,
+.ui.orange.button:focus {
+  background-color: #e55b00;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.orange.buttons .button:active,
+.ui.orange.button:active {
+  background-color: #cf590c;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.orange.buttons .active.button,
+.ui.orange.buttons .active.button:active,
+.ui.orange.active.button,
+.ui.orange.button .active.button:active {
+  background-color: #f56100;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+
+/* Basic */
+.ui.basic.orange.buttons .button,
+.ui.basic.orange.button {
+  background: transparent;
+  -webkit-box-shadow: 0 0 0 1px #F2711C inset;
+          box-shadow: 0 0 0 1px #F2711C inset;
+  color: #F2711C;
+}
+.ui.basic.orange.buttons .button:hover,
+.ui.basic.orange.button:hover {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #f26202 inset;
+          box-shadow: 0 0 0 1px #f26202 inset;
+  color: #f26202;
+}
+.ui.basic.orange.buttons .button:focus,
+.ui.basic.orange.button:focus {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #e55b00 inset;
+          box-shadow: 0 0 0 1px #e55b00 inset;
+  color: #f26202;
+}
+.ui.basic.orange.buttons .active.button,
+.ui.basic.orange.active.button {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #f56100 inset;
+          box-shadow: 0 0 0 1px #f56100 inset;
+  color: #cf590c;
+}
+.ui.basic.orange.buttons .button:active,
+.ui.basic.orange.button:active {
+  -webkit-box-shadow: 0 0 0 1px #cf590c inset;
+          box-shadow: 0 0 0 1px #cf590c inset;
+  color: #cf590c;
+}
+.ui.buttons:not(.vertical) > .basic.orange.button:not(:first-child) {
+  margin-left: -1px;
+}
+
+/* Inverted */
+.ui.inverted.orange.buttons .button,
+.ui.inverted.orange.button {
+  background-color: transparent;
+  -webkit-box-shadow: 0 0 0 2px #FF851B inset;
+          box-shadow: 0 0 0 2px #FF851B inset;
+  color: #FF851B;
+}
+.ui.inverted.orange.buttons .button:hover,
+.ui.inverted.orange.button:hover,
+.ui.inverted.orange.buttons .button:focus,
+.ui.inverted.orange.button:focus,
+.ui.inverted.orange.buttons .button.active,
+.ui.inverted.orange.button.active,
+.ui.inverted.orange.buttons .button:active,
+.ui.inverted.orange.button:active {
+  -webkit-box-shadow: none ;
+          box-shadow: none ;
+  color: #FFFFFF;
+}
+.ui.inverted.orange.buttons .button:hover,
+.ui.inverted.orange.button:hover {
+  background-color: #e76b00;
+}
+.ui.inverted.orange.buttons .button:focus,
+.ui.inverted.orange.button:focus {
+  background-color: #f17000;
+}
+.ui.inverted.orange.buttons .active.button,
+.ui.inverted.orange.active.button {
+  background-color: #ff7701;
+}
+.ui.inverted.orange.buttons .button:active,
+.ui.inverted.orange.button:active {
+  background-color: #e76b00;
+}
+
+/* Inverted Basic */
+.ui.inverted.orange.basic.buttons .button,
+.ui.inverted.orange.buttons .basic.button,
+.ui.inverted.orange.basic.button {
+  background-color: transparent;
+  -webkit-box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) inset;
+          box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) inset;
+  color: #FFFFFF;
+}
+.ui.inverted.orange.basic.buttons .button:hover,
+.ui.inverted.orange.buttons .basic.button:hover,
+.ui.inverted.orange.basic.button:hover {
+  -webkit-box-shadow: 0 0 0 2px #e76b00 inset;
+          box-shadow: 0 0 0 2px #e76b00 inset;
+  color: #FF851B;
+}
+.ui.inverted.orange.basic.buttons .button:focus,
+.ui.inverted.orange.basic.buttons .button:focus,
+.ui.inverted.orange.basic.button:focus {
+  -webkit-box-shadow: 0 0 0 2px #f17000 inset;
+          box-shadow: 0 0 0 2px #f17000 inset;
+  color: #FF851B;
+}
+.ui.inverted.orange.basic.buttons .active.button,
+.ui.inverted.orange.buttons .basic.active.button,
+.ui.inverted.orange.basic.active.button {
+  -webkit-box-shadow: 0 0 0 2px #ff7701 inset;
+          box-shadow: 0 0 0 2px #ff7701 inset;
+  color: #FF851B;
+}
+.ui.inverted.orange.basic.buttons .button:active,
+.ui.inverted.orange.buttons .basic.button:active,
+.ui.inverted.orange.basic.button:active {
+  -webkit-box-shadow: 0 0 0 2px #e76b00 inset;
+          box-shadow: 0 0 0 2px #e76b00 inset;
+  color: #FF851B;
+}
+
+/* Tertiary */
+.ui.tertiary.orange.buttons .button,
+.ui.tertiary.orange.buttons .tertiary.button,
+.ui.tertiary.orange.button {
+  background: transparent;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+  color: #F2711C;
+}
+.ui.tertiary.orange.buttons .button:hover,
+.ui.tertiary.orange.buttons button:hover,
+.ui.tertiary.orange.button:hover {
+  -webkit-box-shadow: inset 0 -0.2em 0 #da671b;
+          box-shadow: inset 0 -0.2em 0 #da671b;
+  color: #da671b;
+}
+.ui.tertiary.orange.buttons .button:focus,
+.ui.tertiary.orange.buttons .tertiary.button:focus,
+.ui.tertiary.orange.button:focus {
+  -webkit-box-shadow: inset 0 -0.2em 0 #ce6017;
+          box-shadow: inset 0 -0.2em 0 #ce6017;
+  color: #ce6017;
+}
+.ui.tertiary.orange.buttons .active.button,
+.ui.tertiary.orange.buttons .tertiary.active.button,
+.ui.tertiary.orange.active.button,
+.ui.tertiary.orange.buttons .button:active,
+.ui.tertiary.orange.buttons .tertiary.button:active,
+.ui.tertiary.orange.button:active {
+  -webkit-box-shadow: inset 0 -0.2em 0 #f56100;
+          box-shadow: inset 0 -0.2em 0 #f56100;
+  color: #f56100;
+}
+.ui.yellow.buttons .button,
+.ui.yellow.button {
+  background-color: #FBBD08;
+  color: #FFFFFF;
+  text-shadow: none;
+  background-image: none;
+}
+.ui.yellow.button {
+  -webkit-box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+}
+.ui.yellow.buttons .button:hover,
+.ui.yellow.button:hover {
+  background-color: #eaae00;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.yellow.buttons .button:focus,
+.ui.yellow.button:focus {
+  background-color: #daa300;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.yellow.buttons .button:active,
+.ui.yellow.button:active {
+  background-color: #cd9903;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.yellow.buttons .active.button,
+.ui.yellow.buttons .active.button:active,
+.ui.yellow.active.button,
+.ui.yellow.button .active.button:active {
+  background-color: #eaae00;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+
+/* Basic */
+.ui.basic.yellow.buttons .button,
+.ui.basic.yellow.button {
+  background: transparent;
+  -webkit-box-shadow: 0 0 0 1px #FBBD08 inset;
+          box-shadow: 0 0 0 1px #FBBD08 inset;
+  color: #FBBD08;
+}
+.ui.basic.yellow.buttons .button:hover,
+.ui.basic.yellow.button:hover {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #eaae00 inset;
+          box-shadow: 0 0 0 1px #eaae00 inset;
+  color: #eaae00;
+}
+.ui.basic.yellow.buttons .button:focus,
+.ui.basic.yellow.button:focus {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #daa300 inset;
+          box-shadow: 0 0 0 1px #daa300 inset;
+  color: #eaae00;
+}
+.ui.basic.yellow.buttons .active.button,
+.ui.basic.yellow.active.button {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #eaae00 inset;
+          box-shadow: 0 0 0 1px #eaae00 inset;
+  color: #cd9903;
+}
+.ui.basic.yellow.buttons .button:active,
+.ui.basic.yellow.button:active {
+  -webkit-box-shadow: 0 0 0 1px #cd9903 inset;
+          box-shadow: 0 0 0 1px #cd9903 inset;
+  color: #cd9903;
+}
+.ui.buttons:not(.vertical) > .basic.yellow.button:not(:first-child) {
+  margin-left: -1px;
+}
+
+/* Inverted */
+.ui.inverted.yellow.buttons .button,
+.ui.inverted.yellow.button {
+  background-color: transparent;
+  -webkit-box-shadow: 0 0 0 2px #FFE21F inset;
+          box-shadow: 0 0 0 2px #FFE21F inset;
+  color: #FFE21F;
+}
+.ui.inverted.yellow.buttons .button:hover,
+.ui.inverted.yellow.button:hover,
+.ui.inverted.yellow.buttons .button:focus,
+.ui.inverted.yellow.button:focus,
+.ui.inverted.yellow.buttons .button.active,
+.ui.inverted.yellow.button.active,
+.ui.inverted.yellow.buttons .button:active,
+.ui.inverted.yellow.button:active {
+  -webkit-box-shadow: none ;
+          box-shadow: none ;
+  color: rgba(0, 0, 0, 0.6);
+}
+.ui.inverted.yellow.buttons .button:hover,
+.ui.inverted.yellow.button:hover {
+  background-color: #ebcd00;
+}
+.ui.inverted.yellow.buttons .button:focus,
+.ui.inverted.yellow.button:focus {
+  background-color: #f5d500;
+}
+.ui.inverted.yellow.buttons .active.button,
+.ui.inverted.yellow.active.button {
+  background-color: #ffdf05;
+}
+.ui.inverted.yellow.buttons .button:active,
+.ui.inverted.yellow.button:active {
+  background-color: #ebcd00;
+}
+
+/* Inverted Basic */
+.ui.inverted.yellow.basic.buttons .button,
+.ui.inverted.yellow.buttons .basic.button,
+.ui.inverted.yellow.basic.button {
+  background-color: transparent;
+  -webkit-box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) inset;
+          box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) inset;
+  color: #FFFFFF;
+}
+.ui.inverted.yellow.basic.buttons .button:hover,
+.ui.inverted.yellow.buttons .basic.button:hover,
+.ui.inverted.yellow.basic.button:hover {
+  -webkit-box-shadow: 0 0 0 2px #ebcd00 inset;
+          box-shadow: 0 0 0 2px #ebcd00 inset;
+  color: #FFE21F;
+}
+.ui.inverted.yellow.basic.buttons .button:focus,
+.ui.inverted.yellow.basic.buttons .button:focus,
+.ui.inverted.yellow.basic.button:focus {
+  -webkit-box-shadow: 0 0 0 2px #f5d500 inset;
+          box-shadow: 0 0 0 2px #f5d500 inset;
+  color: #FFE21F;
+}
+.ui.inverted.yellow.basic.buttons .active.button,
+.ui.inverted.yellow.buttons .basic.active.button,
+.ui.inverted.yellow.basic.active.button {
+  -webkit-box-shadow: 0 0 0 2px #ffdf05 inset;
+          box-shadow: 0 0 0 2px #ffdf05 inset;
+  color: #FFE21F;
+}
+.ui.inverted.yellow.basic.buttons .button:active,
+.ui.inverted.yellow.buttons .basic.button:active,
+.ui.inverted.yellow.basic.button:active {
+  -webkit-box-shadow: 0 0 0 2px #ebcd00 inset;
+          box-shadow: 0 0 0 2px #ebcd00 inset;
+  color: #FFE21F;
+}
+
+/* Tertiary */
+.ui.tertiary.yellow.buttons .button,
+.ui.tertiary.yellow.buttons .tertiary.button,
+.ui.tertiary.yellow.button {
+  background: transparent;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+  color: #FBBD08;
+}
+.ui.tertiary.yellow.buttons .button:hover,
+.ui.tertiary.yellow.buttons button:hover,
+.ui.tertiary.yellow.button:hover {
+  -webkit-box-shadow: inset 0 -0.2em 0 #d2a217;
+          box-shadow: inset 0 -0.2em 0 #d2a217;
+  color: #d2a217;
+}
+.ui.tertiary.yellow.buttons .button:focus,
+.ui.tertiary.yellow.buttons .tertiary.button:focus,
+.ui.tertiary.yellow.button:focus {
+  -webkit-box-shadow: inset 0 -0.2em 0 #c49816;
+          box-shadow: inset 0 -0.2em 0 #c49816;
+  color: #c49816;
+}
+.ui.tertiary.yellow.buttons .active.button,
+.ui.tertiary.yellow.buttons .tertiary.active.button,
+.ui.tertiary.yellow.active.button,
+.ui.tertiary.yellow.buttons .button:active,
+.ui.tertiary.yellow.buttons .tertiary.button:active,
+.ui.tertiary.yellow.button:active {
+  -webkit-box-shadow: inset 0 -0.2em 0 #eaae00;
+          box-shadow: inset 0 -0.2em 0 #eaae00;
+  color: #eaae00;
+}
+.ui.olive.buttons .button,
+.ui.olive.button {
+  background-color: #B5CC18;
+  color: #FFFFFF;
+  text-shadow: none;
+  background-image: none;
+}
+.ui.olive.button {
+  -webkit-box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+}
+.ui.olive.buttons .button:hover,
+.ui.olive.button:hover {
+  background-color: #a7bd0d;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.olive.buttons .button:focus,
+.ui.olive.button:focus {
+  background-color: #a0b605;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.olive.buttons .button:active,
+.ui.olive.button:active {
+  background-color: #8d9e13;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.olive.buttons .active.button,
+.ui.olive.buttons .active.button:active,
+.ui.olive.active.button,
+.ui.olive.button .active.button:active {
+  background-color: #aac109;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+
+/* Basic */
+.ui.basic.olive.buttons .button,
+.ui.basic.olive.button {
+  background: transparent;
+  -webkit-box-shadow: 0 0 0 1px #B5CC18 inset;
+          box-shadow: 0 0 0 1px #B5CC18 inset;
+  color: #B5CC18;
+}
+.ui.basic.olive.buttons .button:hover,
+.ui.basic.olive.button:hover {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #a7bd0d inset;
+          box-shadow: 0 0 0 1px #a7bd0d inset;
+  color: #a7bd0d;
+}
+.ui.basic.olive.buttons .button:focus,
+.ui.basic.olive.button:focus {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #a0b605 inset;
+          box-shadow: 0 0 0 1px #a0b605 inset;
+  color: #a7bd0d;
+}
+.ui.basic.olive.buttons .active.button,
+.ui.basic.olive.active.button {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #aac109 inset;
+          box-shadow: 0 0 0 1px #aac109 inset;
+  color: #8d9e13;
+}
+.ui.basic.olive.buttons .button:active,
+.ui.basic.olive.button:active {
+  -webkit-box-shadow: 0 0 0 1px #8d9e13 inset;
+          box-shadow: 0 0 0 1px #8d9e13 inset;
+  color: #8d9e13;
+}
+.ui.buttons:not(.vertical) > .basic.olive.button:not(:first-child) {
+  margin-left: -1px;
+}
+
+/* Inverted */
+.ui.inverted.olive.buttons .button,
+.ui.inverted.olive.button {
+  background-color: transparent;
+  -webkit-box-shadow: 0 0 0 2px #D9E778 inset;
+          box-shadow: 0 0 0 2px #D9E778 inset;
+  color: #D9E778;
+}
+.ui.inverted.olive.buttons .button:hover,
+.ui.inverted.olive.button:hover,
+.ui.inverted.olive.buttons .button:focus,
+.ui.inverted.olive.button:focus,
+.ui.inverted.olive.buttons .button.active,
+.ui.inverted.olive.button.active,
+.ui.inverted.olive.buttons .button:active,
+.ui.inverted.olive.button:active {
+  -webkit-box-shadow: none ;
+          box-shadow: none ;
+  color: rgba(0, 0, 0, 0.6);
+}
+.ui.inverted.olive.buttons .button:hover,
+.ui.inverted.olive.button:hover {
+  background-color: #d2e745;
+}
+.ui.inverted.olive.buttons .button:focus,
+.ui.inverted.olive.button:focus {
+  background-color: #daef47;
+}
+.ui.inverted.olive.buttons .active.button,
+.ui.inverted.olive.active.button {
+  background-color: #daed59;
+}
+.ui.inverted.olive.buttons .button:active,
+.ui.inverted.olive.button:active {
+  background-color: #cddf4d;
+}
+
+/* Inverted Basic */
+.ui.inverted.olive.basic.buttons .button,
+.ui.inverted.olive.buttons .basic.button,
+.ui.inverted.olive.basic.button {
+  background-color: transparent;
+  -webkit-box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) inset;
+          box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) inset;
+  color: #FFFFFF;
+}
+.ui.inverted.olive.basic.buttons .button:hover,
+.ui.inverted.olive.buttons .basic.button:hover,
+.ui.inverted.olive.basic.button:hover {
+  -webkit-box-shadow: 0 0 0 2px #d2e745 inset;
+          box-shadow: 0 0 0 2px #d2e745 inset;
+  color: #D9E778;
+}
+.ui.inverted.olive.basic.buttons .button:focus,
+.ui.inverted.olive.basic.buttons .button:focus,
+.ui.inverted.olive.basic.button:focus {
+  -webkit-box-shadow: 0 0 0 2px #daef47 inset;
+          box-shadow: 0 0 0 2px #daef47 inset;
+  color: #D9E778;
+}
+.ui.inverted.olive.basic.buttons .active.button,
+.ui.inverted.olive.buttons .basic.active.button,
+.ui.inverted.olive.basic.active.button {
+  -webkit-box-shadow: 0 0 0 2px #daed59 inset;
+          box-shadow: 0 0 0 2px #daed59 inset;
+  color: #D9E778;
+}
+.ui.inverted.olive.basic.buttons .button:active,
+.ui.inverted.olive.buttons .basic.button:active,
+.ui.inverted.olive.basic.button:active {
+  -webkit-box-shadow: 0 0 0 2px #cddf4d inset;
+          box-shadow: 0 0 0 2px #cddf4d inset;
+  color: #D9E778;
+}
+
+/* Tertiary */
+.ui.tertiary.olive.buttons .button,
+.ui.tertiary.olive.buttons .tertiary.button,
+.ui.tertiary.olive.button {
+  background: transparent;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+  color: #B5CC18;
+}
+.ui.tertiary.olive.buttons .button:hover,
+.ui.tertiary.olive.buttons button:hover,
+.ui.tertiary.olive.button:hover {
+  -webkit-box-shadow: inset 0 -0.2em 0 #98a922;
+          box-shadow: inset 0 -0.2em 0 #98a922;
+  color: #98a922;
+}
+.ui.tertiary.olive.buttons .button:focus,
+.ui.tertiary.olive.buttons .tertiary.button:focus,
+.ui.tertiary.olive.button:focus {
+  -webkit-box-shadow: inset 0 -0.2em 0 #92a418;
+          box-shadow: inset 0 -0.2em 0 #92a418;
+  color: #92a418;
+}
+.ui.tertiary.olive.buttons .active.button,
+.ui.tertiary.olive.buttons .tertiary.active.button,
+.ui.tertiary.olive.active.button,
+.ui.tertiary.olive.buttons .button:active,
+.ui.tertiary.olive.buttons .tertiary.button:active,
+.ui.tertiary.olive.button:active {
+  -webkit-box-shadow: inset 0 -0.2em 0 #b1cb00;
+          box-shadow: inset 0 -0.2em 0 #b1cb00;
+  color: #aac109;
+}
+.ui.green.buttons .button,
+.ui.green.button {
+  background-color: #21BA45;
+  color: #FFFFFF;
+  text-shadow: none;
+  background-image: none;
+}
+.ui.green.button {
+  -webkit-box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+}
+.ui.green.buttons .button:hover,
+.ui.green.button:hover {
+  background-color: #16ab39;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.green.buttons .button:focus,
+.ui.green.button:focus {
+  background-color: #0ea432;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.green.buttons .button:active,
+.ui.green.button:active {
+  background-color: #198f35;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.green.buttons .active.button,
+.ui.green.buttons .active.button:active,
+.ui.green.active.button,
+.ui.green.button .active.button:active {
+  background-color: #13ae38;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+
+/* Basic */
+.ui.basic.green.buttons .button,
+.ui.basic.green.button {
+  background: transparent;
+  -webkit-box-shadow: 0 0 0 1px #21BA45 inset;
+          box-shadow: 0 0 0 1px #21BA45 inset;
+  color: #21BA45;
+}
+.ui.basic.green.buttons .button:hover,
+.ui.basic.green.button:hover {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #16ab39 inset;
+          box-shadow: 0 0 0 1px #16ab39 inset;
+  color: #16ab39;
+}
+.ui.basic.green.buttons .button:focus,
+.ui.basic.green.button:focus {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #0ea432 inset;
+          box-shadow: 0 0 0 1px #0ea432 inset;
+  color: #16ab39;
+}
+.ui.basic.green.buttons .active.button,
+.ui.basic.green.active.button {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #13ae38 inset;
+          box-shadow: 0 0 0 1px #13ae38 inset;
+  color: #198f35;
+}
+.ui.basic.green.buttons .button:active,
+.ui.basic.green.button:active {
+  -webkit-box-shadow: 0 0 0 1px #198f35 inset;
+          box-shadow: 0 0 0 1px #198f35 inset;
+  color: #198f35;
+}
+.ui.buttons:not(.vertical) > .basic.green.button:not(:first-child) {
+  margin-left: -1px;
+}
+
+/* Inverted */
+.ui.inverted.green.buttons .button,
+.ui.inverted.green.button {
+  background-color: transparent;
+  -webkit-box-shadow: 0 0 0 2px #2ECC40 inset;
+          box-shadow: 0 0 0 2px #2ECC40 inset;
+  color: #2ECC40;
+}
+.ui.inverted.green.buttons .button:hover,
+.ui.inverted.green.button:hover,
+.ui.inverted.green.buttons .button:focus,
+.ui.inverted.green.button:focus,
+.ui.inverted.green.buttons .button.active,
+.ui.inverted.green.button.active,
+.ui.inverted.green.buttons .button:active,
+.ui.inverted.green.button:active {
+  -webkit-box-shadow: none ;
+          box-shadow: none ;
+  color: #FFFFFF;
+}
+.ui.inverted.green.buttons .button:hover,
+.ui.inverted.green.button:hover {
+  background-color: #1ea92e;
+}
+.ui.inverted.green.buttons .button:focus,
+.ui.inverted.green.button:focus {
+  background-color: #19b82b;
+}
+.ui.inverted.green.buttons .active.button,
+.ui.inverted.green.active.button {
+  background-color: #1fc231;
+}
+.ui.inverted.green.buttons .button:active,
+.ui.inverted.green.button:active {
+  background-color: #25a233;
+}
+
+/* Inverted Basic */
+.ui.inverted.green.basic.buttons .button,
+.ui.inverted.green.buttons .basic.button,
+.ui.inverted.green.basic.button {
+  background-color: transparent;
+  -webkit-box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) inset;
+          box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) inset;
+  color: #FFFFFF;
+}
+.ui.inverted.green.basic.buttons .button:hover,
+.ui.inverted.green.buttons .basic.button:hover,
+.ui.inverted.green.basic.button:hover {
+  -webkit-box-shadow: 0 0 0 2px #1ea92e inset;
+          box-shadow: 0 0 0 2px #1ea92e inset;
+  color: #2ECC40;
+}
+.ui.inverted.green.basic.buttons .button:focus,
+.ui.inverted.green.basic.buttons .button:focus,
+.ui.inverted.green.basic.button:focus {
+  -webkit-box-shadow: 0 0 0 2px #19b82b inset;
+          box-shadow: 0 0 0 2px #19b82b inset;
+  color: #2ECC40;
+}
+.ui.inverted.green.basic.buttons .active.button,
+.ui.inverted.green.buttons .basic.active.button,
+.ui.inverted.green.basic.active.button {
+  -webkit-box-shadow: 0 0 0 2px #1fc231 inset;
+          box-shadow: 0 0 0 2px #1fc231 inset;
+  color: #2ECC40;
+}
+.ui.inverted.green.basic.buttons .button:active,
+.ui.inverted.green.buttons .basic.button:active,
+.ui.inverted.green.basic.button:active {
+  -webkit-box-shadow: 0 0 0 2px #25a233 inset;
+          box-shadow: 0 0 0 2px #25a233 inset;
+  color: #2ECC40;
+}
+
+/* Tertiary */
+.ui.tertiary.green.buttons .button,
+.ui.tertiary.green.buttons .tertiary.button,
+.ui.tertiary.green.button {
+  background: transparent;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+  color: #21BA45;
+}
+.ui.tertiary.green.buttons .button:hover,
+.ui.tertiary.green.buttons button:hover,
+.ui.tertiary.green.button:hover {
+  -webkit-box-shadow: inset 0 -0.2em 0 #2a9844;
+          box-shadow: inset 0 -0.2em 0 #2a9844;
+  color: #2a9844;
+}
+.ui.tertiary.green.buttons .button:focus,
+.ui.tertiary.green.buttons .tertiary.button:focus,
+.ui.tertiary.green.button:focus {
+  -webkit-box-shadow: inset 0 -0.2em 0 #20923b;
+          box-shadow: inset 0 -0.2em 0 #20923b;
+  color: #20923b;
+}
+.ui.tertiary.green.buttons .active.button,
+.ui.tertiary.green.buttons .tertiary.active.button,
+.ui.tertiary.green.active.button,
+.ui.tertiary.green.buttons .button:active,
+.ui.tertiary.green.buttons .tertiary.button:active,
+.ui.tertiary.green.button:active {
+  -webkit-box-shadow: inset 0 -0.2em 0 #00c22e;
+          box-shadow: inset 0 -0.2em 0 #00c22e;
+  color: #13ae38;
+}
+.ui.teal.buttons .button,
+.ui.teal.button {
+  background-color: #00B5AD;
+  color: #FFFFFF;
+  text-shadow: none;
+  background-image: none;
+}
+.ui.teal.button {
+  -webkit-box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+}
+.ui.teal.buttons .button:hover,
+.ui.teal.button:hover {
+  background-color: #009c95;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.teal.buttons .button:focus,
+.ui.teal.button:focus {
+  background-color: #008c86;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.teal.buttons .button:active,
+.ui.teal.button:active {
+  background-color: #00827c;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.teal.buttons .active.button,
+.ui.teal.buttons .active.button:active,
+.ui.teal.active.button,
+.ui.teal.button .active.button:active {
+  background-color: #009c95;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+
+/* Basic */
+.ui.basic.teal.buttons .button,
+.ui.basic.teal.button {
+  background: transparent;
+  -webkit-box-shadow: 0 0 0 1px #00B5AD inset;
+          box-shadow: 0 0 0 1px #00B5AD inset;
+  color: #00B5AD;
+}
+.ui.basic.teal.buttons .button:hover,
+.ui.basic.teal.button:hover {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #009c95 inset;
+          box-shadow: 0 0 0 1px #009c95 inset;
+  color: #009c95;
+}
+.ui.basic.teal.buttons .button:focus,
+.ui.basic.teal.button:focus {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #008c86 inset;
+          box-shadow: 0 0 0 1px #008c86 inset;
+  color: #009c95;
+}
+.ui.basic.teal.buttons .active.button,
+.ui.basic.teal.active.button {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #009c95 inset;
+          box-shadow: 0 0 0 1px #009c95 inset;
+  color: #00827c;
+}
+.ui.basic.teal.buttons .button:active,
+.ui.basic.teal.button:active {
+  -webkit-box-shadow: 0 0 0 1px #00827c inset;
+          box-shadow: 0 0 0 1px #00827c inset;
+  color: #00827c;
+}
+.ui.buttons:not(.vertical) > .basic.teal.button:not(:first-child) {
+  margin-left: -1px;
+}
+
+/* Inverted */
+.ui.inverted.teal.buttons .button,
+.ui.inverted.teal.button {
+  background-color: transparent;
+  -webkit-box-shadow: 0 0 0 2px #6DFFFF inset;
+          box-shadow: 0 0 0 2px #6DFFFF inset;
+  color: #6DFFFF;
+}
+.ui.inverted.teal.buttons .button:hover,
+.ui.inverted.teal.button:hover,
+.ui.inverted.teal.buttons .button:focus,
+.ui.inverted.teal.button:focus,
+.ui.inverted.teal.buttons .button.active,
+.ui.inverted.teal.button.active,
+.ui.inverted.teal.buttons .button:active,
+.ui.inverted.teal.button:active {
+  -webkit-box-shadow: none ;
+          box-shadow: none ;
+  color: rgba(0, 0, 0, 0.6);
+}
+.ui.inverted.teal.buttons .button:hover,
+.ui.inverted.teal.button:hover {
+  background-color: #3affff;
+}
+.ui.inverted.teal.buttons .button:focus,
+.ui.inverted.teal.button:focus {
+  background-color: #44ffff;
+}
+.ui.inverted.teal.buttons .active.button,
+.ui.inverted.teal.active.button {
+  background-color: #54ffff;
+}
+.ui.inverted.teal.buttons .button:active,
+.ui.inverted.teal.button:active {
+  background-color: #3affff;
+}
+
+/* Inverted Basic */
+.ui.inverted.teal.basic.buttons .button,
+.ui.inverted.teal.buttons .basic.button,
+.ui.inverted.teal.basic.button {
+  background-color: transparent;
+  -webkit-box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) inset;
+          box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) inset;
+  color: #FFFFFF;
+}
+.ui.inverted.teal.basic.buttons .button:hover,
+.ui.inverted.teal.buttons .basic.button:hover,
+.ui.inverted.teal.basic.button:hover {
+  -webkit-box-shadow: 0 0 0 2px #3affff inset;
+          box-shadow: 0 0 0 2px #3affff inset;
+  color: #6DFFFF;
+}
+.ui.inverted.teal.basic.buttons .button:focus,
+.ui.inverted.teal.basic.buttons .button:focus,
+.ui.inverted.teal.basic.button:focus {
+  -webkit-box-shadow: 0 0 0 2px #44ffff inset;
+          box-shadow: 0 0 0 2px #44ffff inset;
+  color: #6DFFFF;
+}
+.ui.inverted.teal.basic.buttons .active.button,
+.ui.inverted.teal.buttons .basic.active.button,
+.ui.inverted.teal.basic.active.button {
+  -webkit-box-shadow: 0 0 0 2px #54ffff inset;
+          box-shadow: 0 0 0 2px #54ffff inset;
+  color: #6DFFFF;
+}
+.ui.inverted.teal.basic.buttons .button:active,
+.ui.inverted.teal.buttons .basic.button:active,
+.ui.inverted.teal.basic.button:active {
+  -webkit-box-shadow: 0 0 0 2px #3affff inset;
+          box-shadow: 0 0 0 2px #3affff inset;
+  color: #6DFFFF;
+}
+
+/* Tertiary */
+.ui.tertiary.teal.buttons .button,
+.ui.tertiary.teal.buttons .tertiary.button,
+.ui.tertiary.teal.button {
+  background: transparent;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+  color: #00B5AD;
+}
+.ui.tertiary.teal.buttons .button:hover,
+.ui.tertiary.teal.buttons button:hover,
+.ui.tertiary.teal.button:hover {
+  -webkit-box-shadow: inset 0 -0.2em 0 #108c86;
+          box-shadow: inset 0 -0.2em 0 #108c86;
+  color: #108c86;
+}
+.ui.tertiary.teal.buttons .button:focus,
+.ui.tertiary.teal.buttons .tertiary.button:focus,
+.ui.tertiary.teal.button:focus {
+  -webkit-box-shadow: inset 0 -0.2em 0 #0e7e79;
+          box-shadow: inset 0 -0.2em 0 #0e7e79;
+  color: #0e7e79;
+}
+.ui.tertiary.teal.buttons .active.button,
+.ui.tertiary.teal.buttons .tertiary.active.button,
+.ui.tertiary.teal.active.button,
+.ui.tertiary.teal.buttons .button:active,
+.ui.tertiary.teal.buttons .tertiary.button:active,
+.ui.tertiary.teal.button:active {
+  -webkit-box-shadow: inset 0 -0.2em 0 #009c95;
+          box-shadow: inset 0 -0.2em 0 #009c95;
+  color: #009c95;
+}
+.ui.blue.buttons .button,
+.ui.blue.button {
+  background-color: #2185D0;
+  color: #FFFFFF;
+  text-shadow: none;
+  background-image: none;
+}
+.ui.blue.button {
+  -webkit-box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+}
+.ui.blue.buttons .button:hover,
+.ui.blue.button:hover {
+  background-color: #1678c2;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.blue.buttons .button:focus,
+.ui.blue.button:focus {
+  background-color: #0d71bb;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.blue.buttons .button:active,
+.ui.blue.button:active {
+  background-color: #1a69a4;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.blue.buttons .active.button,
+.ui.blue.buttons .active.button:active,
+.ui.blue.active.button,
+.ui.blue.button .active.button:active {
+  background-color: #1279c6;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+
+/* Basic */
+.ui.basic.blue.buttons .button,
+.ui.basic.blue.button {
+  background: transparent;
+  -webkit-box-shadow: 0 0 0 1px #2185D0 inset;
+          box-shadow: 0 0 0 1px #2185D0 inset;
+  color: #2185D0;
+}
+.ui.basic.blue.buttons .button:hover,
+.ui.basic.blue.button:hover {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #1678c2 inset;
+          box-shadow: 0 0 0 1px #1678c2 inset;
+  color: #1678c2;
+}
+.ui.basic.blue.buttons .button:focus,
+.ui.basic.blue.button:focus {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #0d71bb inset;
+          box-shadow: 0 0 0 1px #0d71bb inset;
+  color: #1678c2;
+}
+.ui.basic.blue.buttons .active.button,
+.ui.basic.blue.active.button {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #1279c6 inset;
+          box-shadow: 0 0 0 1px #1279c6 inset;
+  color: #1a69a4;
+}
+.ui.basic.blue.buttons .button:active,
+.ui.basic.blue.button:active {
+  -webkit-box-shadow: 0 0 0 1px #1a69a4 inset;
+          box-shadow: 0 0 0 1px #1a69a4 inset;
+  color: #1a69a4;
+}
+.ui.buttons:not(.vertical) > .basic.blue.button:not(:first-child) {
+  margin-left: -1px;
+}
+
+/* Inverted */
+.ui.inverted.blue.buttons .button,
+.ui.inverted.blue.button {
+  background-color: transparent;
+  -webkit-box-shadow: 0 0 0 2px #54C8FF inset;
+          box-shadow: 0 0 0 2px #54C8FF inset;
+  color: #54C8FF;
+}
+.ui.inverted.blue.buttons .button:hover,
+.ui.inverted.blue.button:hover,
+.ui.inverted.blue.buttons .button:focus,
+.ui.inverted.blue.button:focus,
+.ui.inverted.blue.buttons .button.active,
+.ui.inverted.blue.button.active,
+.ui.inverted.blue.buttons .button:active,
+.ui.inverted.blue.button:active {
+  -webkit-box-shadow: none ;
+          box-shadow: none ;
+  color: #FFFFFF;
+}
+.ui.inverted.blue.buttons .button:hover,
+.ui.inverted.blue.button:hover {
+  background-color: #21b8ff;
+}
+.ui.inverted.blue.buttons .button:focus,
+.ui.inverted.blue.button:focus {
+  background-color: #2bbbff;
+}
+.ui.inverted.blue.buttons .active.button,
+.ui.inverted.blue.active.button {
+  background-color: #3ac0ff;
+}
+.ui.inverted.blue.buttons .button:active,
+.ui.inverted.blue.button:active {
+  background-color: #21b8ff;
+}
+
+/* Inverted Basic */
+.ui.inverted.blue.basic.buttons .button,
+.ui.inverted.blue.buttons .basic.button,
+.ui.inverted.blue.basic.button {
+  background-color: transparent;
+  -webkit-box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) inset;
+          box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) inset;
+  color: #FFFFFF;
+}
+.ui.inverted.blue.basic.buttons .button:hover,
+.ui.inverted.blue.buttons .basic.button:hover,
+.ui.inverted.blue.basic.button:hover {
+  -webkit-box-shadow: 0 0 0 2px #21b8ff inset;
+          box-shadow: 0 0 0 2px #21b8ff inset;
+  color: #54C8FF;
+}
+.ui.inverted.blue.basic.buttons .button:focus,
+.ui.inverted.blue.basic.buttons .button:focus,
+.ui.inverted.blue.basic.button:focus {
+  -webkit-box-shadow: 0 0 0 2px #2bbbff inset;
+          box-shadow: 0 0 0 2px #2bbbff inset;
+  color: #54C8FF;
+}
+.ui.inverted.blue.basic.buttons .active.button,
+.ui.inverted.blue.buttons .basic.active.button,
+.ui.inverted.blue.basic.active.button {
+  -webkit-box-shadow: 0 0 0 2px #3ac0ff inset;
+          box-shadow: 0 0 0 2px #3ac0ff inset;
+  color: #54C8FF;
+}
+.ui.inverted.blue.basic.buttons .button:active,
+.ui.inverted.blue.buttons .basic.button:active,
+.ui.inverted.blue.basic.button:active {
+  -webkit-box-shadow: 0 0 0 2px #21b8ff inset;
+          box-shadow: 0 0 0 2px #21b8ff inset;
+  color: #54C8FF;
+}
+
+/* Tertiary */
+.ui.tertiary.blue.buttons .button,
+.ui.tertiary.blue.buttons .tertiary.button,
+.ui.tertiary.blue.button {
+  background: transparent;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+  color: #2185D0;
+}
+.ui.tertiary.blue.buttons .button:hover,
+.ui.tertiary.blue.buttons button:hover,
+.ui.tertiary.blue.button:hover {
+  -webkit-box-shadow: inset 0 -0.2em 0 #2b75ac;
+          box-shadow: inset 0 -0.2em 0 #2b75ac;
+  color: #2b75ac;
+}
+.ui.tertiary.blue.buttons .button:focus,
+.ui.tertiary.blue.buttons .tertiary.button:focus,
+.ui.tertiary.blue.button:focus {
+  -webkit-box-shadow: inset 0 -0.2em 0 #216ea7;
+          box-shadow: inset 0 -0.2em 0 #216ea7;
+  color: #216ea7;
+}
+.ui.tertiary.blue.buttons .active.button,
+.ui.tertiary.blue.buttons .tertiary.active.button,
+.ui.tertiary.blue.active.button,
+.ui.tertiary.blue.buttons .button:active,
+.ui.tertiary.blue.buttons .tertiary.button:active,
+.ui.tertiary.blue.button:active {
+  -webkit-box-shadow: inset 0 -0.2em 0 #007bd8;
+          box-shadow: inset 0 -0.2em 0 #007bd8;
+  color: #1279c6;
+}
+.ui.violet.buttons .button,
+.ui.violet.button {
+  background-color: #6435C9;
+  color: #FFFFFF;
+  text-shadow: none;
+  background-image: none;
+}
+.ui.violet.button {
+  -webkit-box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+}
+.ui.violet.buttons .button:hover,
+.ui.violet.button:hover {
+  background-color: #5829bb;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.violet.buttons .button:focus,
+.ui.violet.button:focus {
+  background-color: #4f20b5;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.violet.buttons .button:active,
+.ui.violet.button:active {
+  background-color: #502aa1;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.violet.buttons .active.button,
+.ui.violet.buttons .active.button:active,
+.ui.violet.active.button,
+.ui.violet.button .active.button:active {
+  background-color: #5626bf;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+
+/* Basic */
+.ui.basic.violet.buttons .button,
+.ui.basic.violet.button {
+  background: transparent;
+  -webkit-box-shadow: 0 0 0 1px #6435C9 inset;
+          box-shadow: 0 0 0 1px #6435C9 inset;
+  color: #6435C9;
+}
+.ui.basic.violet.buttons .button:hover,
+.ui.basic.violet.button:hover {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #5829bb inset;
+          box-shadow: 0 0 0 1px #5829bb inset;
+  color: #5829bb;
+}
+.ui.basic.violet.buttons .button:focus,
+.ui.basic.violet.button:focus {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #4f20b5 inset;
+          box-shadow: 0 0 0 1px #4f20b5 inset;
+  color: #5829bb;
+}
+.ui.basic.violet.buttons .active.button,
+.ui.basic.violet.active.button {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #5626bf inset;
+          box-shadow: 0 0 0 1px #5626bf inset;
+  color: #502aa1;
+}
+.ui.basic.violet.buttons .button:active,
+.ui.basic.violet.button:active {
+  -webkit-box-shadow: 0 0 0 1px #502aa1 inset;
+          box-shadow: 0 0 0 1px #502aa1 inset;
+  color: #502aa1;
+}
+.ui.buttons:not(.vertical) > .basic.violet.button:not(:first-child) {
+  margin-left: -1px;
+}
+
+/* Inverted */
+.ui.inverted.violet.buttons .button,
+.ui.inverted.violet.button {
+  background-color: transparent;
+  -webkit-box-shadow: 0 0 0 2px #A291FB inset;
+          box-shadow: 0 0 0 2px #A291FB inset;
+  color: #A291FB;
+}
+.ui.inverted.violet.buttons .button:hover,
+.ui.inverted.violet.button:hover,
+.ui.inverted.violet.buttons .button:focus,
+.ui.inverted.violet.button:focus,
+.ui.inverted.violet.buttons .button.active,
+.ui.inverted.violet.button.active,
+.ui.inverted.violet.buttons .button:active,
+.ui.inverted.violet.button:active {
+  -webkit-box-shadow: none ;
+          box-shadow: none ;
+  color: #FFFFFF;
+}
+.ui.inverted.violet.buttons .button:hover,
+.ui.inverted.violet.button:hover {
+  background-color: #745aff;
+}
+.ui.inverted.violet.buttons .button:focus,
+.ui.inverted.violet.button:focus {
+  background-color: #7d64ff;
+}
+.ui.inverted.violet.buttons .active.button,
+.ui.inverted.violet.active.button {
+  background-color: #8a73ff;
+}
+.ui.inverted.violet.buttons .button:active,
+.ui.inverted.violet.button:active {
+  background-color: #7860f9;
+}
+
+/* Inverted Basic */
+.ui.inverted.violet.basic.buttons .button,
+.ui.inverted.violet.buttons .basic.button,
+.ui.inverted.violet.basic.button {
+  background-color: transparent;
+  -webkit-box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) inset;
+          box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) inset;
+  color: #FFFFFF;
+}
+.ui.inverted.violet.basic.buttons .button:hover,
+.ui.inverted.violet.buttons .basic.button:hover,
+.ui.inverted.violet.basic.button:hover {
+  -webkit-box-shadow: 0 0 0 2px #745aff inset;
+          box-shadow: 0 0 0 2px #745aff inset;
+  color: #A291FB;
+}
+.ui.inverted.violet.basic.buttons .button:focus,
+.ui.inverted.violet.basic.buttons .button:focus,
+.ui.inverted.violet.basic.button:focus {
+  -webkit-box-shadow: 0 0 0 2px #7d64ff inset;
+          box-shadow: 0 0 0 2px #7d64ff inset;
+  color: #A291FB;
+}
+.ui.inverted.violet.basic.buttons .active.button,
+.ui.inverted.violet.buttons .basic.active.button,
+.ui.inverted.violet.basic.active.button {
+  -webkit-box-shadow: 0 0 0 2px #8a73ff inset;
+          box-shadow: 0 0 0 2px #8a73ff inset;
+  color: #A291FB;
+}
+.ui.inverted.violet.basic.buttons .button:active,
+.ui.inverted.violet.buttons .basic.button:active,
+.ui.inverted.violet.basic.button:active {
+  -webkit-box-shadow: 0 0 0 2px #7860f9 inset;
+          box-shadow: 0 0 0 2px #7860f9 inset;
+  color: #A291FB;
+}
+
+/* Tertiary */
+.ui.tertiary.violet.buttons .button,
+.ui.tertiary.violet.buttons .tertiary.button,
+.ui.tertiary.violet.button {
+  background: transparent;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+  color: #6435C9;
+}
+.ui.tertiary.violet.buttons .button:hover,
+.ui.tertiary.violet.buttons button:hover,
+.ui.tertiary.violet.button:hover {
+  -webkit-box-shadow: inset 0 -0.2em 0 #6040a5;
+          box-shadow: inset 0 -0.2em 0 #6040a5;
+  color: #6040a5;
+}
+.ui.tertiary.violet.buttons .button:focus,
+.ui.tertiary.violet.buttons .tertiary.button:focus,
+.ui.tertiary.violet.button:focus {
+  -webkit-box-shadow: inset 0 -0.2em 0 #5735a0;
+          box-shadow: inset 0 -0.2em 0 #5735a0;
+  color: #5735a0;
+}
+.ui.tertiary.violet.buttons .active.button,
+.ui.tertiary.violet.buttons .tertiary.active.button,
+.ui.tertiary.violet.active.button,
+.ui.tertiary.violet.buttons .button:active,
+.ui.tertiary.violet.buttons .tertiary.button:active,
+.ui.tertiary.violet.button:active {
+  -webkit-box-shadow: inset 0 -0.2em 0 #4e0fd6;
+          box-shadow: inset 0 -0.2em 0 #4e0fd6;
+  color: #5626bf;
+}
+.ui.purple.buttons .button,
+.ui.purple.button {
+  background-color: #A333C8;
+  color: #FFFFFF;
+  text-shadow: none;
+  background-image: none;
+}
+.ui.purple.button {
+  -webkit-box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+}
+.ui.purple.buttons .button:hover,
+.ui.purple.button:hover {
+  background-color: #9627ba;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.purple.buttons .button:focus,
+.ui.purple.button:focus {
+  background-color: #8f1eb4;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.purple.buttons .button:active,
+.ui.purple.button:active {
+  background-color: #82299f;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.purple.buttons .active.button,
+.ui.purple.buttons .active.button:active,
+.ui.purple.active.button,
+.ui.purple.button .active.button:active {
+  background-color: #9724be;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+
+/* Basic */
+.ui.basic.purple.buttons .button,
+.ui.basic.purple.button {
+  background: transparent;
+  -webkit-box-shadow: 0 0 0 1px #A333C8 inset;
+          box-shadow: 0 0 0 1px #A333C8 inset;
+  color: #A333C8;
+}
+.ui.basic.purple.buttons .button:hover,
+.ui.basic.purple.button:hover {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #9627ba inset;
+          box-shadow: 0 0 0 1px #9627ba inset;
+  color: #9627ba;
+}
+.ui.basic.purple.buttons .button:focus,
+.ui.basic.purple.button:focus {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #8f1eb4 inset;
+          box-shadow: 0 0 0 1px #8f1eb4 inset;
+  color: #9627ba;
+}
+.ui.basic.purple.buttons .active.button,
+.ui.basic.purple.active.button {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #9724be inset;
+          box-shadow: 0 0 0 1px #9724be inset;
+  color: #82299f;
+}
+.ui.basic.purple.buttons .button:active,
+.ui.basic.purple.button:active {
+  -webkit-box-shadow: 0 0 0 1px #82299f inset;
+          box-shadow: 0 0 0 1px #82299f inset;
+  color: #82299f;
+}
+.ui.buttons:not(.vertical) > .basic.purple.button:not(:first-child) {
+  margin-left: -1px;
+}
+
+/* Inverted */
+.ui.inverted.purple.buttons .button,
+.ui.inverted.purple.button {
+  background-color: transparent;
+  -webkit-box-shadow: 0 0 0 2px #DC73FF inset;
+          box-shadow: 0 0 0 2px #DC73FF inset;
+  color: #DC73FF;
+}
+.ui.inverted.purple.buttons .button:hover,
+.ui.inverted.purple.button:hover,
+.ui.inverted.purple.buttons .button:focus,
+.ui.inverted.purple.button:focus,
+.ui.inverted.purple.buttons .button.active,
+.ui.inverted.purple.button.active,
+.ui.inverted.purple.buttons .button:active,
+.ui.inverted.purple.button:active {
+  -webkit-box-shadow: none ;
+          box-shadow: none ;
+  color: #FFFFFF;
+}
+.ui.inverted.purple.buttons .button:hover,
+.ui.inverted.purple.button:hover {
+  background-color: #cf40ff;
+}
+.ui.inverted.purple.buttons .button:focus,
+.ui.inverted.purple.button:focus {
+  background-color: #d24aff;
+}
+.ui.inverted.purple.buttons .active.button,
+.ui.inverted.purple.active.button {
+  background-color: #d65aff;
+}
+.ui.inverted.purple.buttons .button:active,
+.ui.inverted.purple.button:active {
+  background-color: #cf40ff;
+}
+
+/* Inverted Basic */
+.ui.inverted.purple.basic.buttons .button,
+.ui.inverted.purple.buttons .basic.button,
+.ui.inverted.purple.basic.button {
+  background-color: transparent;
+  -webkit-box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) inset;
+          box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) inset;
+  color: #FFFFFF;
+}
+.ui.inverted.purple.basic.buttons .button:hover,
+.ui.inverted.purple.buttons .basic.button:hover,
+.ui.inverted.purple.basic.button:hover {
+  -webkit-box-shadow: 0 0 0 2px #cf40ff inset;
+          box-shadow: 0 0 0 2px #cf40ff inset;
+  color: #DC73FF;
+}
+.ui.inverted.purple.basic.buttons .button:focus,
+.ui.inverted.purple.basic.buttons .button:focus,
+.ui.inverted.purple.basic.button:focus {
+  -webkit-box-shadow: 0 0 0 2px #d24aff inset;
+          box-shadow: 0 0 0 2px #d24aff inset;
+  color: #DC73FF;
+}
+.ui.inverted.purple.basic.buttons .active.button,
+.ui.inverted.purple.buttons .basic.active.button,
+.ui.inverted.purple.basic.active.button {
+  -webkit-box-shadow: 0 0 0 2px #d65aff inset;
+          box-shadow: 0 0 0 2px #d65aff inset;
+  color: #DC73FF;
+}
+.ui.inverted.purple.basic.buttons .button:active,
+.ui.inverted.purple.buttons .basic.button:active,
+.ui.inverted.purple.basic.button:active {
+  -webkit-box-shadow: 0 0 0 2px #cf40ff inset;
+          box-shadow: 0 0 0 2px #cf40ff inset;
+  color: #DC73FF;
+}
+
+/* Tertiary */
+.ui.tertiary.purple.buttons .button,
+.ui.tertiary.purple.buttons .tertiary.button,
+.ui.tertiary.purple.button {
+  background: transparent;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+  color: #A333C8;
+}
+.ui.tertiary.purple.buttons .button:hover,
+.ui.tertiary.purple.buttons button:hover,
+.ui.tertiary.purple.button:hover {
+  -webkit-box-shadow: inset 0 -0.2em 0 #8a3ea4;
+          box-shadow: inset 0 -0.2em 0 #8a3ea4;
+  color: #8a3ea4;
+}
+.ui.tertiary.purple.buttons .button:focus,
+.ui.tertiary.purple.buttons .tertiary.button:focus,
+.ui.tertiary.purple.button:focus {
+  -webkit-box-shadow: inset 0 -0.2em 0 #84339f;
+          box-shadow: inset 0 -0.2em 0 #84339f;
+  color: #84339f;
+}
+.ui.tertiary.purple.buttons .active.button,
+.ui.tertiary.purple.buttons .tertiary.active.button,
+.ui.tertiary.purple.active.button,
+.ui.tertiary.purple.buttons .button:active,
+.ui.tertiary.purple.buttons .tertiary.button:active,
+.ui.tertiary.purple.button:active {
+  -webkit-box-shadow: inset 0 -0.2em 0 #a30dd4;
+          box-shadow: inset 0 -0.2em 0 #a30dd4;
+  color: #9724be;
+}
+.ui.pink.buttons .button,
+.ui.pink.button {
+  background-color: #E03997;
+  color: #FFFFFF;
+  text-shadow: none;
+  background-image: none;
+}
+.ui.pink.button {
+  -webkit-box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+}
+.ui.pink.buttons .button:hover,
+.ui.pink.button:hover {
+  background-color: #e61a8d;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.pink.buttons .button:focus,
+.ui.pink.button:focus {
+  background-color: #e10f85;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.pink.buttons .button:active,
+.ui.pink.button:active {
+  background-color: #c71f7e;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.pink.buttons .active.button,
+.ui.pink.buttons .active.button:active,
+.ui.pink.active.button,
+.ui.pink.button .active.button:active {
+  background-color: #ea158d;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+
+/* Basic */
+.ui.basic.pink.buttons .button,
+.ui.basic.pink.button {
+  background: transparent;
+  -webkit-box-shadow: 0 0 0 1px #E03997 inset;
+          box-shadow: 0 0 0 1px #E03997 inset;
+  color: #E03997;
+}
+.ui.basic.pink.buttons .button:hover,
+.ui.basic.pink.button:hover {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #e61a8d inset;
+          box-shadow: 0 0 0 1px #e61a8d inset;
+  color: #e61a8d;
+}
+.ui.basic.pink.buttons .button:focus,
+.ui.basic.pink.button:focus {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #e10f85 inset;
+          box-shadow: 0 0 0 1px #e10f85 inset;
+  color: #e61a8d;
+}
+.ui.basic.pink.buttons .active.button,
+.ui.basic.pink.active.button {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #ea158d inset;
+          box-shadow: 0 0 0 1px #ea158d inset;
+  color: #c71f7e;
+}
+.ui.basic.pink.buttons .button:active,
+.ui.basic.pink.button:active {
+  -webkit-box-shadow: 0 0 0 1px #c71f7e inset;
+          box-shadow: 0 0 0 1px #c71f7e inset;
+  color: #c71f7e;
+}
+.ui.buttons:not(.vertical) > .basic.pink.button:not(:first-child) {
+  margin-left: -1px;
+}
+
+/* Inverted */
+.ui.inverted.pink.buttons .button,
+.ui.inverted.pink.button {
+  background-color: transparent;
+  -webkit-box-shadow: 0 0 0 2px #FF8EDF inset;
+          box-shadow: 0 0 0 2px #FF8EDF inset;
+  color: #FF8EDF;
+}
+.ui.inverted.pink.buttons .button:hover,
+.ui.inverted.pink.button:hover,
+.ui.inverted.pink.buttons .button:focus,
+.ui.inverted.pink.button:focus,
+.ui.inverted.pink.buttons .button.active,
+.ui.inverted.pink.button.active,
+.ui.inverted.pink.buttons .button:active,
+.ui.inverted.pink.button:active {
+  -webkit-box-shadow: none ;
+          box-shadow: none ;
+  color: #FFFFFF;
+}
+.ui.inverted.pink.buttons .button:hover,
+.ui.inverted.pink.button:hover {
+  background-color: #ff5bd1;
+}
+.ui.inverted.pink.buttons .button:focus,
+.ui.inverted.pink.button:focus {
+  background-color: #ff65d3;
+}
+.ui.inverted.pink.buttons .active.button,
+.ui.inverted.pink.active.button {
+  background-color: #ff74d8;
+}
+.ui.inverted.pink.buttons .button:active,
+.ui.inverted.pink.button:active {
+  background-color: #ff5bd1;
+}
+
+/* Inverted Basic */
+.ui.inverted.pink.basic.buttons .button,
+.ui.inverted.pink.buttons .basic.button,
+.ui.inverted.pink.basic.button {
+  background-color: transparent;
+  -webkit-box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) inset;
+          box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) inset;
+  color: #FFFFFF;
+}
+.ui.inverted.pink.basic.buttons .button:hover,
+.ui.inverted.pink.buttons .basic.button:hover,
+.ui.inverted.pink.basic.button:hover {
+  -webkit-box-shadow: 0 0 0 2px #ff5bd1 inset;
+          box-shadow: 0 0 0 2px #ff5bd1 inset;
+  color: #FF8EDF;
+}
+.ui.inverted.pink.basic.buttons .button:focus,
+.ui.inverted.pink.basic.buttons .button:focus,
+.ui.inverted.pink.basic.button:focus {
+  -webkit-box-shadow: 0 0 0 2px #ff65d3 inset;
+          box-shadow: 0 0 0 2px #ff65d3 inset;
+  color: #FF8EDF;
+}
+.ui.inverted.pink.basic.buttons .active.button,
+.ui.inverted.pink.buttons .basic.active.button,
+.ui.inverted.pink.basic.active.button {
+  -webkit-box-shadow: 0 0 0 2px #ff74d8 inset;
+          box-shadow: 0 0 0 2px #ff74d8 inset;
+  color: #FF8EDF;
+}
+.ui.inverted.pink.basic.buttons .button:active,
+.ui.inverted.pink.buttons .basic.button:active,
+.ui.inverted.pink.basic.button:active {
+  -webkit-box-shadow: 0 0 0 2px #ff5bd1 inset;
+          box-shadow: 0 0 0 2px #ff5bd1 inset;
+  color: #FF8EDF;
+}
+
+/* Tertiary */
+.ui.tertiary.pink.buttons .button,
+.ui.tertiary.pink.buttons .tertiary.button,
+.ui.tertiary.pink.button {
+  background: transparent;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+  color: #E03997;
+}
+.ui.tertiary.pink.buttons .button:hover,
+.ui.tertiary.pink.buttons button:hover,
+.ui.tertiary.pink.button:hover {
+  -webkit-box-shadow: inset 0 -0.2em 0 #cc3389;
+          box-shadow: inset 0 -0.2em 0 #cc3389;
+  color: #cc3389;
+}
+.ui.tertiary.pink.buttons .button:focus,
+.ui.tertiary.pink.buttons .tertiary.button:focus,
+.ui.tertiary.pink.button:focus {
+  -webkit-box-shadow: inset 0 -0.2em 0 #c92782;
+          box-shadow: inset 0 -0.2em 0 #c92782;
+  color: #c92782;
+}
+.ui.tertiary.pink.buttons .active.button,
+.ui.tertiary.pink.buttons .tertiary.active.button,
+.ui.tertiary.pink.active.button,
+.ui.tertiary.pink.buttons .button:active,
+.ui.tertiary.pink.buttons .tertiary.button:active,
+.ui.tertiary.pink.button:active {
+  -webkit-box-shadow: inset 0 -0.2em 0 #ff0090;
+          box-shadow: inset 0 -0.2em 0 #ff0090;
+  color: #ea158d;
+}
+.ui.brown.buttons .button,
+.ui.brown.button {
+  background-color: #A5673F;
+  color: #FFFFFF;
+  text-shadow: none;
+  background-image: none;
+}
+.ui.brown.button {
+  -webkit-box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+}
+.ui.brown.buttons .button:hover,
+.ui.brown.button:hover {
+  background-color: #975b33;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.brown.buttons .button:focus,
+.ui.brown.button:focus {
+  background-color: #90532b;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.brown.buttons .button:active,
+.ui.brown.button:active {
+  background-color: #805031;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.brown.buttons .active.button,
+.ui.brown.buttons .active.button:active,
+.ui.brown.active.button,
+.ui.brown.button .active.button:active {
+  background-color: #995a31;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+
+/* Basic */
+.ui.basic.brown.buttons .button,
+.ui.basic.brown.button {
+  background: transparent;
+  -webkit-box-shadow: 0 0 0 1px #A5673F inset;
+          box-shadow: 0 0 0 1px #A5673F inset;
+  color: #A5673F;
+}
+.ui.basic.brown.buttons .button:hover,
+.ui.basic.brown.button:hover {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #975b33 inset;
+          box-shadow: 0 0 0 1px #975b33 inset;
+  color: #975b33;
+}
+.ui.basic.brown.buttons .button:focus,
+.ui.basic.brown.button:focus {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #90532b inset;
+          box-shadow: 0 0 0 1px #90532b inset;
+  color: #975b33;
+}
+.ui.basic.brown.buttons .active.button,
+.ui.basic.brown.active.button {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #995a31 inset;
+          box-shadow: 0 0 0 1px #995a31 inset;
+  color: #805031;
+}
+.ui.basic.brown.buttons .button:active,
+.ui.basic.brown.button:active {
+  -webkit-box-shadow: 0 0 0 1px #805031 inset;
+          box-shadow: 0 0 0 1px #805031 inset;
+  color: #805031;
+}
+.ui.buttons:not(.vertical) > .basic.brown.button:not(:first-child) {
+  margin-left: -1px;
+}
+
+/* Inverted */
+.ui.inverted.brown.buttons .button,
+.ui.inverted.brown.button {
+  background-color: transparent;
+  -webkit-box-shadow: 0 0 0 2px #D67C1C inset;
+          box-shadow: 0 0 0 2px #D67C1C inset;
+  color: #D67C1C;
+}
+.ui.inverted.brown.buttons .button:hover,
+.ui.inverted.brown.button:hover,
+.ui.inverted.brown.buttons .button:focus,
+.ui.inverted.brown.button:focus,
+.ui.inverted.brown.buttons .button.active,
+.ui.inverted.brown.button.active,
+.ui.inverted.brown.buttons .button:active,
+.ui.inverted.brown.button:active {
+  -webkit-box-shadow: none ;
+          box-shadow: none ;
+  color: #FFFFFF;
+}
+.ui.inverted.brown.buttons .button:hover,
+.ui.inverted.brown.button:hover {
+  background-color: #b0620f;
+}
+.ui.inverted.brown.buttons .button:focus,
+.ui.inverted.brown.button:focus {
+  background-color: #c16808;
+}
+.ui.inverted.brown.buttons .active.button,
+.ui.inverted.brown.active.button {
+  background-color: #cc6f0d;
+}
+.ui.inverted.brown.buttons .button:active,
+.ui.inverted.brown.button:active {
+  background-color: #a96216;
+}
+
+/* Inverted Basic */
+.ui.inverted.brown.basic.buttons .button,
+.ui.inverted.brown.buttons .basic.button,
+.ui.inverted.brown.basic.button {
+  background-color: transparent;
+  -webkit-box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) inset;
+          box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) inset;
+  color: #FFFFFF;
+}
+.ui.inverted.brown.basic.buttons .button:hover,
+.ui.inverted.brown.buttons .basic.button:hover,
+.ui.inverted.brown.basic.button:hover {
+  -webkit-box-shadow: 0 0 0 2px #b0620f inset;
+          box-shadow: 0 0 0 2px #b0620f inset;
+  color: #D67C1C;
+}
+.ui.inverted.brown.basic.buttons .button:focus,
+.ui.inverted.brown.basic.buttons .button:focus,
+.ui.inverted.brown.basic.button:focus {
+  -webkit-box-shadow: 0 0 0 2px #c16808 inset;
+          box-shadow: 0 0 0 2px #c16808 inset;
+  color: #D67C1C;
+}
+.ui.inverted.brown.basic.buttons .active.button,
+.ui.inverted.brown.buttons .basic.active.button,
+.ui.inverted.brown.basic.active.button {
+  -webkit-box-shadow: 0 0 0 2px #cc6f0d inset;
+          box-shadow: 0 0 0 2px #cc6f0d inset;
+  color: #D67C1C;
+}
+.ui.inverted.brown.basic.buttons .button:active,
+.ui.inverted.brown.buttons .basic.button:active,
+.ui.inverted.brown.basic.button:active {
+  -webkit-box-shadow: 0 0 0 2px #a96216 inset;
+          box-shadow: 0 0 0 2px #a96216 inset;
+  color: #D67C1C;
+}
+
+/* Tertiary */
+.ui.tertiary.brown.buttons .button,
+.ui.tertiary.brown.buttons .tertiary.button,
+.ui.tertiary.brown.button {
+  background: transparent;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+  color: #A5673F;
+}
+.ui.tertiary.brown.buttons .button:hover,
+.ui.tertiary.brown.buttons button:hover,
+.ui.tertiary.brown.button:hover {
+  -webkit-box-shadow: inset 0 -0.2em 0 #835f48;
+          box-shadow: inset 0 -0.2em 0 #835f48;
+  color: #835f48;
+}
+.ui.tertiary.brown.buttons .button:focus,
+.ui.tertiary.brown.buttons .tertiary.button:focus,
+.ui.tertiary.brown.button:focus {
+  -webkit-box-shadow: inset 0 -0.2em 0 #7d573e;
+          box-shadow: inset 0 -0.2em 0 #7d573e;
+  color: #7d573e;
+}
+.ui.tertiary.brown.buttons .active.button,
+.ui.tertiary.brown.buttons .tertiary.active.button,
+.ui.tertiary.brown.active.button,
+.ui.tertiary.brown.buttons .button:active,
+.ui.tertiary.brown.buttons .tertiary.button:active,
+.ui.tertiary.brown.button:active {
+  -webkit-box-shadow: inset 0 -0.2em 0 #ae561d;
+          box-shadow: inset 0 -0.2em 0 #ae561d;
+  color: #995a31;
+}
+.ui.grey.buttons .button,
+.ui.grey.button {
+  background-color: #767676;
+  color: #FFFFFF;
+  text-shadow: none;
+  background-image: none;
+}
+.ui.grey.button {
+  -webkit-box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+}
+.ui.grey.buttons .button:hover,
+.ui.grey.button:hover {
+  background-color: #838383;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.grey.buttons .button:focus,
+.ui.grey.button:focus {
+  background-color: #8a8a8a;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.grey.buttons .button:active,
+.ui.grey.button:active {
+  background-color: #909090;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.grey.buttons .active.button,
+.ui.grey.buttons .active.button:active,
+.ui.grey.active.button,
+.ui.grey.button .active.button:active {
+  background-color: #696969;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+
+/* Basic */
+.ui.basic.grey.buttons .button,
+.ui.basic.grey.button {
+  background: transparent;
+  -webkit-box-shadow: 0 0 0 1px #767676 inset;
+          box-shadow: 0 0 0 1px #767676 inset;
+  color: #767676;
+}
+.ui.basic.grey.buttons .button:hover,
+.ui.basic.grey.button:hover {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #838383 inset;
+          box-shadow: 0 0 0 1px #838383 inset;
+  color: #838383;
+}
+.ui.basic.grey.buttons .button:focus,
+.ui.basic.grey.button:focus {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #8a8a8a inset;
+          box-shadow: 0 0 0 1px #8a8a8a inset;
+  color: #838383;
+}
+.ui.basic.grey.buttons .active.button,
+.ui.basic.grey.active.button {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #696969 inset;
+          box-shadow: 0 0 0 1px #696969 inset;
+  color: #909090;
+}
+.ui.basic.grey.buttons .button:active,
+.ui.basic.grey.button:active {
+  -webkit-box-shadow: 0 0 0 1px #909090 inset;
+          box-shadow: 0 0 0 1px #909090 inset;
+  color: #909090;
+}
+.ui.buttons:not(.vertical) > .basic.grey.button:not(:first-child) {
+  margin-left: -1px;
+}
+
+/* Inverted */
+.ui.inverted.grey.buttons .button,
+.ui.inverted.grey.button {
+  background-color: transparent;
+  -webkit-box-shadow: 0 0 0 2px #D4D4D5 inset;
+          box-shadow: 0 0 0 2px #D4D4D5 inset;
+  color: #FFFFFF;
+}
+.ui.inverted.grey.buttons .button:hover,
+.ui.inverted.grey.button:hover,
+.ui.inverted.grey.buttons .button:focus,
+.ui.inverted.grey.button:focus,
+.ui.inverted.grey.buttons .button.active,
+.ui.inverted.grey.button.active,
+.ui.inverted.grey.buttons .button:active,
+.ui.inverted.grey.button:active {
+  -webkit-box-shadow: none ;
+          box-shadow: none ;
+  color: rgba(0, 0, 0, 0.6);
+}
+.ui.inverted.grey.buttons .button:hover,
+.ui.inverted.grey.button:hover {
+  background-color: #c2c4c5;
+}
+.ui.inverted.grey.buttons .button:focus,
+.ui.inverted.grey.button:focus {
+  background-color: #c7c9cb;
+}
+.ui.inverted.grey.buttons .active.button,
+.ui.inverted.grey.active.button {
+  background-color: #cfd0d2;
+}
+.ui.inverted.grey.buttons .button:active,
+.ui.inverted.grey.button:active {
+  background-color: #c2c4c5;
+}
+
+/* Inverted Basic */
+.ui.inverted.grey.basic.buttons .button,
+.ui.inverted.grey.buttons .basic.button,
+.ui.inverted.grey.basic.button {
+  background-color: transparent;
+  -webkit-box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) inset;
+          box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) inset;
+  color: #FFFFFF;
+}
+.ui.inverted.grey.basic.buttons .button:hover,
+.ui.inverted.grey.buttons .basic.button:hover,
+.ui.inverted.grey.basic.button:hover {
+  -webkit-box-shadow: 0 0 0 2px #c2c4c5 inset;
+          box-shadow: 0 0 0 2px #c2c4c5 inset;
+  color: #FFFFFF;
+}
+.ui.inverted.grey.basic.buttons .button:focus,
+.ui.inverted.grey.basic.buttons .button:focus,
+.ui.inverted.grey.basic.button:focus {
+  -webkit-box-shadow: 0 0 0 2px #c7c9cb inset;
+          box-shadow: 0 0 0 2px #c7c9cb inset;
+  color: #DCDDDE;
+}
+.ui.inverted.grey.basic.buttons .active.button,
+.ui.inverted.grey.buttons .basic.active.button,
+.ui.inverted.grey.basic.active.button {
+  -webkit-box-shadow: 0 0 0 2px #cfd0d2 inset;
+          box-shadow: 0 0 0 2px #cfd0d2 inset;
+  color: #FFFFFF;
+}
+.ui.inverted.grey.basic.buttons .button:active,
+.ui.inverted.grey.buttons .basic.button:active,
+.ui.inverted.grey.basic.button:active {
+  -webkit-box-shadow: 0 0 0 2px #c2c4c5 inset;
+          box-shadow: 0 0 0 2px #c2c4c5 inset;
+  color: #FFFFFF;
+}
+
+/* Tertiary */
+.ui.tertiary.grey.buttons .button,
+.ui.tertiary.grey.buttons .tertiary.button,
+.ui.tertiary.grey.button {
+  background: transparent;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+  color: #767676;
+}
+.ui.tertiary.grey.buttons .button:hover,
+.ui.tertiary.grey.buttons button:hover,
+.ui.tertiary.grey.button:hover {
+  -webkit-box-shadow: inset 0 -0.2em 0 #838383;
+          box-shadow: inset 0 -0.2em 0 #838383;
+  color: #838383;
+}
+.ui.tertiary.grey.buttons .button:focus,
+.ui.tertiary.grey.buttons .tertiary.button:focus,
+.ui.tertiary.grey.button:focus {
+  -webkit-box-shadow: inset 0 -0.2em 0 #8a8a8a;
+          box-shadow: inset 0 -0.2em 0 #8a8a8a;
+  color: #8a8a8a;
+}
+.ui.tertiary.grey.buttons .active.button,
+.ui.tertiary.grey.buttons .tertiary.active.button,
+.ui.tertiary.grey.active.button,
+.ui.tertiary.grey.buttons .button:active,
+.ui.tertiary.grey.buttons .tertiary.button:active,
+.ui.tertiary.grey.button:active {
+  -webkit-box-shadow: inset 0 -0.2em 0 #7e5454;
+          box-shadow: inset 0 -0.2em 0 #7e5454;
+  color: #696969;
+}
+.ui.black.buttons .button,
+.ui.black.button {
+  background-color: #1B1C1D;
+  color: #FFFFFF;
+  text-shadow: none;
+  background-image: none;
+}
+.ui.black.button {
+  -webkit-box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+}
+.ui.black.buttons .button:hover,
+.ui.black.button:hover {
+  background-color: #27292a;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.black.buttons .button:focus,
+.ui.black.button:focus {
+  background-color: #2f3032;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.black.buttons .button:active,
+.ui.black.button:active {
+  background-color: #343637;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.black.buttons .active.button,
+.ui.black.buttons .active.button:active,
+.ui.black.active.button,
+.ui.black.button .active.button:active {
+  background-color: #0f0f10;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+
+/* Basic */
+.ui.basic.black.buttons .button,
+.ui.basic.black.button {
+  background: transparent;
+  -webkit-box-shadow: 0 0 0 1px #1B1C1D inset;
+          box-shadow: 0 0 0 1px #1B1C1D inset;
+  color: #1B1C1D;
+}
+.ui.basic.black.buttons .button:hover,
+.ui.basic.black.button:hover {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #27292a inset;
+          box-shadow: 0 0 0 1px #27292a inset;
+  color: #27292a;
+}
+.ui.basic.black.buttons .button:focus,
+.ui.basic.black.button:focus {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #2f3032 inset;
+          box-shadow: 0 0 0 1px #2f3032 inset;
+  color: #27292a;
+}
+.ui.basic.black.buttons .active.button,
+.ui.basic.black.active.button {
+  background: transparent ;
+  -webkit-box-shadow: 0 0 0 1px #0f0f10 inset;
+          box-shadow: 0 0 0 1px #0f0f10 inset;
+  color: #343637;
+}
+.ui.basic.black.buttons .button:active,
+.ui.basic.black.button:active {
+  -webkit-box-shadow: 0 0 0 1px #343637 inset;
+          box-shadow: 0 0 0 1px #343637 inset;
+  color: #343637;
+}
+.ui.buttons:not(.vertical) > .basic.black.button:not(:first-child) {
+  margin-left: -1px;
+}
+
+/* Inverted */
+.ui.inverted.black.buttons .button,
+.ui.inverted.black.button {
+  background-color: transparent;
+  -webkit-box-shadow: 0 0 0 2px #D4D4D5 inset;
+          box-shadow: 0 0 0 2px #D4D4D5 inset;
+  color: #FFFFFF;
+}
+.ui.inverted.black.buttons .button:hover,
+.ui.inverted.black.button:hover,
+.ui.inverted.black.buttons .button:focus,
+.ui.inverted.black.button:focus,
+.ui.inverted.black.buttons .button.active,
+.ui.inverted.black.button.active,
+.ui.inverted.black.buttons .button:active,
+.ui.inverted.black.button:active {
+  -webkit-box-shadow: none ;
+          box-shadow: none ;
+  color: #FFFFFF;
+}
+.ui.inverted.black.buttons .button:hover,
+.ui.inverted.black.button:hover {
+  background-color: #000000;
+}
+.ui.inverted.black.buttons .button:focus,
+.ui.inverted.black.button:focus {
+  background-color: #000000;
+}
+.ui.inverted.black.buttons .active.button,
+.ui.inverted.black.active.button {
+  background-color: #000000;
+}
+.ui.inverted.black.buttons .button:active,
+.ui.inverted.black.button:active {
+  background-color: #000000;
+}
+
+/* Inverted Basic */
+.ui.inverted.black.basic.buttons .button,
+.ui.inverted.black.buttons .basic.button,
+.ui.inverted.black.basic.button {
+  background-color: transparent;
+  -webkit-box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) inset;
+          box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5) inset;
+  color: #FFFFFF;
+}
+.ui.inverted.black.basic.buttons .button:hover,
+.ui.inverted.black.buttons .basic.button:hover,
+.ui.inverted.black.basic.button:hover {
+  -webkit-box-shadow: 0 0 0 2px #000000 inset;
+          box-shadow: 0 0 0 2px #000000 inset;
+  color: #FFFFFF;
+}
+.ui.inverted.black.basic.buttons .button:focus,
+.ui.inverted.black.basic.buttons .button:focus,
+.ui.inverted.black.basic.button:focus {
+  -webkit-box-shadow: 0 0 0 2px #000000 inset;
+          box-shadow: 0 0 0 2px #000000 inset;
+  color: #545454;
+}
+.ui.inverted.black.basic.buttons .active.button,
+.ui.inverted.black.buttons .basic.active.button,
+.ui.inverted.black.basic.active.button {
+  -webkit-box-shadow: 0 0 0 2px #000000 inset;
+          box-shadow: 0 0 0 2px #000000 inset;
+  color: #FFFFFF;
+}
+.ui.inverted.black.basic.buttons .button:active,
+.ui.inverted.black.buttons .basic.button:active,
+.ui.inverted.black.basic.button:active {
+  -webkit-box-shadow: 0 0 0 2px #000000 inset;
+          box-shadow: 0 0 0 2px #000000 inset;
+  color: #FFFFFF;
+}
+
+/* Tertiary */
+.ui.tertiary.black.buttons .button,
+.ui.tertiary.black.buttons .tertiary.button,
+.ui.tertiary.black.button {
+  background: transparent;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+  color: #1B1C1D;
+}
+.ui.tertiary.black.buttons .button:hover,
+.ui.tertiary.black.buttons button:hover,
+.ui.tertiary.black.button:hover {
+  -webkit-box-shadow: inset 0 -0.2em 0 #8b8f93;
+          box-shadow: inset 0 -0.2em 0 #8b8f93;
+  color: #8b8f93;
+}
+.ui.tertiary.black.buttons .button:focus,
+.ui.tertiary.black.buttons .tertiary.button:focus,
+.ui.tertiary.black.button:focus {
+  -webkit-box-shadow: inset 0 -0.2em 0 #93969a;
+          box-shadow: inset 0 -0.2em 0 #93969a;
+  color: #93969a;
+}
+.ui.tertiary.black.buttons .active.button,
+.ui.tertiary.black.buttons .tertiary.active.button,
+.ui.tertiary.black.active.button,
+.ui.tertiary.black.buttons .button:active,
+.ui.tertiary.black.buttons .tertiary.button:active,
+.ui.tertiary.black.button:active {
+  -webkit-box-shadow: inset 0 -0.2em 0 #404245;
+          box-shadow: inset 0 -0.2em 0 #404245;
+  color: #0f0f10;
+}
+
+/*---------------
+    Positive
+----------------*/
+
+
+/* Standard */
+.ui.positive.buttons .button,
+.ui.positive.button {
+  background-color: #21BA45;
+  color: #FFFFFF;
+  text-shadow: none;
+  background-image: none;
+}
+.ui.positive.button {
+  -webkit-box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+}
+.ui.positive.buttons .button:hover,
+.ui.positive.button:hover {
+  background-color: #16ab39;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.positive.buttons .button:focus,
+.ui.positive.button:focus {
+  background-color: #0ea432;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.positive.buttons .button:active,
+.ui.positive.button:active {
+  background-color: #198f35;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.positive.buttons .active.button,
+.ui.positive.buttons .active.button:active,
+.ui.positive.active.button,
+.ui.positive.button .active.button:active {
+  background-color: #13ae38;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+
+/* Basic */
+.ui.basic.positive.buttons .button,
+.ui.basic.positive.button {
+  background: transparent;
+  -webkit-box-shadow: 0 0 0 1px #21BA45 inset;
+          box-shadow: 0 0 0 1px #21BA45 inset;
+  color: #21BA45;
+}
+.ui.basic.positive.buttons .button:hover,
+.ui.basic.positive.button:hover {
+  background: transparent;
+  -webkit-box-shadow: 0 0 0 1px #16ab39 inset;
+          box-shadow: 0 0 0 1px #16ab39 inset;
+  color: #16ab39;
+}
+.ui.basic.positive.buttons .button:focus,
+.ui.basic.positive.button:focus {
+  background: transparent;
+  -webkit-box-shadow: 0 0 0 1px #0ea432 inset;
+          box-shadow: 0 0 0 1px #0ea432 inset;
+  color: #16ab39;
+}
+.ui.basic.positive.buttons .active.button,
+.ui.basic.positive.active.button {
+  background: transparent;
+  -webkit-box-shadow: 0 0 0 1px #13ae38 inset;
+          box-shadow: 0 0 0 1px #13ae38 inset;
+  color: #198f35;
+}
+.ui.basic.positive.buttons .button:active,
+.ui.basic.positive.button:active {
+  -webkit-box-shadow: 0 0 0 1px #198f35 inset;
+          box-shadow: 0 0 0 1px #198f35 inset;
+  color: #198f35;
+}
+.ui.buttons:not(.vertical) > .basic.positive.button:not(:first-child) {
+  margin-left: -1px;
+}
+
+/*---------------
+     Negative
+----------------*/
+
+
+/* Standard */
+.ui.negative.buttons .button,
+.ui.negative.button {
+  background-color: #DB2828;
+  color: #FFFFFF;
+  text-shadow: none;
+  background-image: none;
+}
+.ui.negative.button {
+  -webkit-box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+}
+.ui.negative.buttons .button:hover,
+.ui.negative.button:hover {
+  background-color: #d01919;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.negative.buttons .button:focus,
+.ui.negative.button:focus {
+  background-color: #ca1010;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.negative.buttons .button:active,
+.ui.negative.button:active {
+  background-color: #b21e1e;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+.ui.negative.buttons .active.button,
+.ui.negative.buttons .active.button:active,
+.ui.negative.active.button,
+.ui.negative.button .active.button:active {
+  background-color: #d41515;
+  color: #FFFFFF;
+  text-shadow: none;
+}
+
+/* Basic */
+.ui.basic.negative.buttons .button,
+.ui.basic.negative.button {
+  background: transparent;
+  -webkit-box-shadow: 0 0 0 1px #DB2828 inset;
+          box-shadow: 0 0 0 1px #DB2828 inset;
+  color: #DB2828;
+}
+.ui.basic.negative.buttons .button:hover,
+.ui.basic.negative.button:hover {
+  background: transparent;
+  -webkit-box-shadow: 0 0 0 1px #d01919 inset;
+          box-shadow: 0 0 0 1px #d01919 inset;
+  color: #d01919;
+}
+.ui.basic.negative.buttons .button:focus,
+.ui.basic.negative.button:focus {
+  background: transparent;
+  -webkit-box-shadow: 0 0 0 1px #ca1010 inset;
+          box-shadow: 0 0 0 1px #ca1010 inset;
+  color: #d01919;
+}
+.ui.basic.negative.buttons .active.button,
+.ui.basic.negative.active.button {
+  background: transparent;
+  -webkit-box-shadow: 0 0 0 1px #d41515 inset;
+          box-shadow: 0 0 0 1px #d41515 inset;
+  color: #b21e1e;
+}
+.ui.basic.negative.buttons .button:active,
+.ui.basic.negative.button:active {
+  -webkit-box-shadow: 0 0 0 1px #b21e1e inset;
+          box-shadow: 0 0 0 1px #b21e1e inset;
+  color: #b21e1e;
+}
+.ui.buttons:not(.vertical) > .basic.negative.button:not(:first-child) {
+  margin-left: -1px;
+}
+
+
+/*******************************
+            Groups
+*******************************/
+
+.ui.buttons {
+  display: -webkit-inline-box;
+  display: -ms-inline-flexbox;
+  display: inline-flex;
+  -webkit-box-orient: horizontal;
+  -webkit-box-direction: normal;
+      -ms-flex-direction: row;
+          flex-direction: row;
+  font-size: 0;
+  vertical-align: baseline;
+  margin: 0 0.25em 0 0;
+}
+.ui.buttons:not(.basic):not(.inverted) {
+  -webkit-box-shadow: none;
+          box-shadow: none;
+}
+
+/* Clearfix */
+.ui.buttons:after {
+  content: ".";
+  display: block;
+  height: 0;
+  clear: both;
+  visibility: hidden;
+}
+
+/* Standard Group */
+.ui.buttons .button {
+  -webkit-box-flex: 1;
+      -ms-flex: 1 0 auto;
+          flex: 1 0 auto;
+  border-radius: 0;
+  margin: 0 0 0 0;
+}
+.ui.buttons:not(.basic):not(.inverted) > .button:not(.basic):not(.inverted) {
+  -webkit-box-shadow: 0 0 0 1px transparent inset, 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 0 0 1px transparent inset, 0 0 0 0 rgba(34, 36, 38, 0.15) inset;
+}
+.ui.buttons .button:first-child {
+  border-left: none;
+  margin-left: 0;
+  border-top-left-radius: 0.28571429rem;
+  border-bottom-left-radius: 0.28571429rem;
+}
+.ui.buttons .button:last-child {
+  border-top-right-radius: 0.28571429rem;
+  border-bottom-right-radius: 0.28571429rem;
+}
+
+/* Vertical  Style */
+.ui.vertical.buttons {
+  display: -webkit-inline-box;
+  display: -ms-inline-flexbox;
+  display: inline-flex;
+  -webkit-box-orient: vertical;
+  -webkit-box-direction: normal;
+      -ms-flex-direction: column;
+          flex-direction: column;
+}
+.ui.vertical.buttons .button {
+  display: block;
+  float: none;
+  width: 100%;
+  margin: 0 0 0 0;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+  border-radius: 0;
+}
+.ui.vertical.buttons .button:first-child {
+  border-top-left-radius: 0.28571429rem;
+  border-top-right-radius: 0.28571429rem;
+}
+.ui.vertical.buttons .button:last-child {
+  margin-bottom: 0;
+  border-bottom-left-radius: 0.28571429rem;
+  border-bottom-right-radius: 0.28571429rem;
+}
+.ui.vertical.buttons .button:only-child {
+  border-radius: 0.28571429rem;
+}
+
+
+/*******************************
+         Theme Overrides
+*******************************/
+
+
+
+/*******************************
+         Site Overrides
+*******************************/
+

Разлика између датотеке није приказан због своје велике величине
+ 8 - 0
website/semantic/dist/components/button.min.css


+ 169 - 0
website/semantic/dist/components/calendar.css

@@ -0,0 +1,169 @@
+/*!
+ * # Fomantic-UI - Calendar
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */
+
+
+/*******************************
+            Popup
+*******************************/
+
+.ui.calendar .ui.popup {
+  max-width: none;
+  padding: 0;
+  border: none;
+  -webkit-user-select: none;
+     -moz-user-select: none;
+      -ms-user-select: none;
+          user-select: none;
+}
+
+
+/*******************************
+            Calendar
+*******************************/
+
+.ui.calendar .calendar:focus {
+  outline: 0;
+}
+
+
+/*******************************
+            Grid
+*******************************/
+
+.ui.calendar .ui.popup .ui.grid {
+  display: block;
+  white-space: nowrap;
+}
+.ui.calendar .ui.popup .ui.grid > .column {
+  width: auto;
+}
+
+
+/*******************************
+            Table
+*******************************/
+
+.ui.calendar .ui.table.year,
+.ui.calendar .ui.table.month,
+.ui.calendar .ui.table.minute {
+  min-width: 15em;
+}
+.ui.calendar .ui.table.day {
+  min-width: 18em;
+}
+.ui.calendar .ui.table.day.andweek {
+  min-width: 22em;
+}
+.ui.calendar .ui.table.hour {
+  min-width: 20em;
+}
+.ui.calendar .ui.table tr th,
+.ui.calendar .ui.table tr td {
+  padding: 0.5em;
+  white-space: nowrap;
+}
+.ui.calendar .ui.table tr th {
+  border-left: none;
+}
+.ui.calendar .ui.table tr th .icon {
+  margin: 0;
+}
+.ui.calendar .ui.table tr:first-child th {
+  position: relative;
+  padding-left: 0;
+  padding-right: 0;
+}
+.ui.calendar .ui.table.day tr:first-child th {
+  border: none;
+}
+.ui.calendar .ui.table.day tr:nth-child(2) th {
+  padding-top: 0.2em;
+  padding-bottom: 0.3em;
+}
+.ui.calendar .ui.table tr td {
+  padding-left: 0.1em;
+  padding-right: 0.1em;
+}
+.ui.calendar .ui.table tr .link {
+  cursor: pointer;
+}
+.ui.calendar .ui.table tr .prev.link {
+  width: 14.28571429%;
+  position: absolute;
+  left: 0;
+}
+.ui.calendar .ui.table tr .next.link {
+  width: 14.28571429%;
+  position: absolute;
+  right: 0;
+}
+.ui.calendar .ui.table tr .disabled {
+  pointer-events: auto;
+  cursor: default;
+  color: rgba(40, 40, 40, 0.3);
+}
+.ui.calendar .ui.table tr .adjacent:not(.disabled) {
+  color: rgba(0, 0, 0, 0.6);
+  background: rgba(0, 0, 0, 0.03);
+}
+
+/*--------------
+     States
+---------------*/
+
+.ui.calendar .ui.table tr td.today {
+  font-weight: bold;
+}
+.ui.calendar .ui.table tr td.range {
+  background: rgba(0, 0, 0, 0.05);
+  color: rgba(0, 0, 0, 0.95);
+  -webkit-box-shadow: none;
+          box-shadow: none;
+}
+.ui.calendar .ui.table.inverted tr td.range {
+  background: rgba(255, 255, 255, 0.08);
+  color: #ffffff;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+}
+.ui.calendar:not(.disabled) .calendar:focus .ui.table tbody tr td.focus,
+.ui.calendar:not(.disabled) .calendar.active .ui.table tbody tr td.focus {
+  -webkit-box-shadow: inset 0 0 0 1px #85B7D9;
+          box-shadow: inset 0 0 0 1px #85B7D9;
+}
+.ui.calendar:not(.disabled) .calendar:focus .ui.table.inverted tbody tr td.focus,
+.ui.calendar:not(.disabled) .calendar.active .ui.table.inverted tbody tr td.focus {
+  -webkit-box-shadow: inset 0 0 0 1px #85B7D9;
+          box-shadow: inset 0 0 0 1px #85B7D9;
+}
+
+
+/*******************************
+            States
+*******************************/
+
+
+/*--------------------
+        Disabled
+---------------------*/
+
+.ui.disabled.calendar {
+  opacity: 0.45;
+}
+.ui.disabled.calendar > .input,
+.ui.disabled.calendar .ui.table tr .link {
+  pointer-events: none;
+}
+
+
+/*******************************
+         Theme Overrides
+*******************************/
+

+ 1562 - 0
website/semantic/dist/components/calendar.js

@@ -0,0 +1,1562 @@
+/*!
+ * # Fomantic-UI - Calendar
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */
+
+;(function ($, window, document, undefined) {
+
+'use strict';
+
+$.isFunction = $.isFunction || function(obj) {
+  return typeof obj === "function" && typeof obj.nodeType !== "number";
+};
+
+window = (typeof window != 'undefined' && window.Math == Math)
+  ? window
+  : (typeof self != 'undefined' && self.Math == Math)
+    ? self
+    : Function('return this')()
+;
+
+$.fn.calendar = function(parameters) {
+  var
+    $allModules    = $(this),
+
+    moduleSelector = $allModules.selector || '',
+
+    time           = new Date().getTime(),
+    performance    = [],
+
+    query          = arguments[0],
+    methodInvoked  = (typeof query == 'string'),
+    queryArguments = [].slice.call(arguments, 1),
+    returnedValue,
+    timeGapTable = {
+      '5': {'row': 4, 'column': 3 },
+      '10': {'row': 3, 'column': 2 },
+      '15': {'row': 2, 'column': 2 },
+      '20': {'row': 3, 'column': 1 },
+      '30': {'row': 2, 'column': 1 }
+    }
+  ;
+
+  $allModules
+    .each(function () {
+      var
+        settings = ( $.isPlainObject(parameters) )
+          ? $.extend(true, {}, $.fn.calendar.settings, parameters)
+          : $.extend({}, $.fn.calendar.settings),
+
+        className = settings.className,
+        namespace = settings.namespace,
+        selector = settings.selector,
+        formatter = settings.formatter,
+        parser = settings.parser,
+        metadata = settings.metadata,
+        timeGap = timeGapTable[settings.minTimeGap],
+        error = settings.error,
+
+        eventNamespace = '.' + namespace,
+        moduleNamespace = 'module-' + namespace,
+
+        $module = $(this),
+        $input = $module.find(selector.input),
+        $container = $module.find(selector.popup),
+        $activator = $module.find(selector.activator),
+
+        element = this,
+        instance = $module.data(moduleNamespace),
+
+        isTouch,
+        isTouchDown = false,
+        focusDateUsedForRange = false,
+        module
+      ;
+
+      module = {
+
+        initialize: function () {
+          module.debug('Initializing calendar for', element, $module);
+
+          isTouch = module.get.isTouch();
+          module.setup.config();
+          module.setup.popup();
+          module.setup.inline();
+          module.setup.input();
+          module.setup.date();
+          module.create.calendar();
+
+          module.bind.events();
+          module.instantiate();
+        },
+
+        instantiate: function () {
+          module.verbose('Storing instance of calendar');
+          instance = module;
+          $module.data(moduleNamespace, instance);
+        },
+
+        destroy: function () {
+          module.verbose('Destroying previous calendar for', element);
+          $module.removeData(moduleNamespace);
+          module.unbind.events();
+        },
+
+        setup: {
+          config: function () {
+            if (module.get.minDate() !== null) {
+              module.set.minDate($module.data(metadata.minDate));
+            }
+            if (module.get.maxDate() !== null) {
+              module.set.maxDate($module.data(metadata.maxDate));
+            }
+          },
+          popup: function () {
+            if (settings.inline) {
+              return;
+            }
+            if (!$activator.length) {
+              $activator = $module.children().first();
+              if (!$activator.length) {
+                return;
+              }
+            }
+            if ($.fn.popup === undefined) {
+              module.error(error.popup);
+              return;
+            }
+            if (!$container.length) {
+              //prepend the popup element to the activator's parent so that it has less chance of messing with
+              //the styling (eg input action button needs to be the last child to have correct border radius)
+              var $activatorParent = $activator.parent(),
+                  domPositionFunction = $activatorParent.closest(selector.append).length !== 0 ? 'appendTo' : 'prependTo';
+              $container = $('<div/>').addClass(className.popup)[domPositionFunction]($activatorParent);
+            }
+            $container.addClass(className.calendar);
+            var onVisible = settings.onVisible;
+            var onHidden = settings.onHidden;
+            if (!$input.length) {
+              //no input, $container has to handle focus/blur
+              $container.attr('tabindex', '0');
+              onVisible = function () {
+                module.focus();
+                return settings.onVisible.apply($container, arguments);
+              };
+              onHidden = function () {
+                module.blur();
+                return settings.onHidden.apply($container, arguments);
+              };
+            }
+            var onShow = function () {
+              //reset the focus date onShow
+              module.set.focusDate(module.get.date());
+              module.set.mode(settings.startMode);
+              return settings.onShow.apply($container, arguments);
+            };
+            var on = settings.on || ($input.length ? 'focus' : 'click');
+            var options = $.extend({}, settings.popupOptions, {
+              popup: $container,
+              on: on,
+              hoverable: on === 'hover',
+              onShow: onShow,
+              onVisible: onVisible,
+              onHide: settings.onHide,
+              onHidden: onHidden
+            });
+            module.popup(options);
+          },
+          inline: function () {
+            if ($activator.length && !settings.inline) {
+              return;
+            }
+            $container = $('<div/>').addClass(className.calendar).appendTo($module);
+            if (!$input.length) {
+              $container.attr('tabindex', '0');
+            }
+          },
+          input: function () {
+            if (settings.touchReadonly && $input.length && isTouch) {
+              $input.prop('readonly', true);
+            }
+          },
+          date: function () {
+            if (settings.initialDate) {
+              var date = parser.date(settings.initialDate, settings);
+              module.set.date(date, settings.formatInput, false);
+            } else if ($module.data(metadata.date) !== undefined) {
+              var date = parser.date($module.data(metadata.date), settings);
+              module.set.date(date, settings.formatInput, false);
+            } else if ($input.length) {
+              var val = $input.val();
+              var date = parser.date(val, settings);
+              module.set.date(date, settings.formatInput, false);
+            }
+          }
+        },
+
+        create: {
+          calendar: function () {
+            var i, r, c, p, row, cell, pageGrid;
+
+            var mode = module.get.mode();
+            var today = new Date();
+            var date = module.get.date();
+            var focusDate = module.get.focusDate();
+            var display = focusDate || date || settings.initialDate || today;
+            display = module.helper.dateInRange(display);
+
+            if (!focusDate) {
+              focusDate = display;
+              module.set.focusDate(focusDate, false, false);
+            }
+
+            var isYear = mode === 'year';
+            var isMonth = mode === 'month';
+            var isDay = mode === 'day';
+            var isHour = mode === 'hour';
+            var isMinute = mode === 'minute';
+            var isTimeOnly = settings.type === 'time';
+
+            var multiMonth = Math.max(settings.multiMonth, 1);
+            var monthOffset = !isDay ? 0 : module.get.monthOffset();
+
+            var minute = display.getMinutes();
+            var hour = display.getHours();
+            var day = display.getDate();
+            var startMonth = display.getMonth() + monthOffset;
+            var year = display.getFullYear();
+
+            var columns = isDay ? settings.showWeekNumbers ? 8 : 7 : isHour ? 4 : timeGap['column'];
+            var rows = isDay || isHour ? 6 : timeGap['row'];
+            var pages = isDay ? multiMonth : 1;
+
+            var container = $container;
+            var tooltipPosition = container.hasClass("left") ? "right center" : "left center";
+            container.empty();
+            if (pages > 1) {
+              pageGrid = $('<div/>').addClass(className.grid).appendTo(container);
+            }
+
+            for (p = 0; p < pages; p++) {
+              if (pages > 1) {
+                var pageColumn = $('<div/>').addClass(className.column).appendTo(pageGrid);
+                container = pageColumn;
+              }
+
+              var month = startMonth + p;
+              var firstMonthDayColumn = (new Date(year, month, 1).getDay() - settings.firstDayOfWeek % 7 + 7) % 7;
+              if (!settings.constantHeight && isDay) {
+                var requiredCells = new Date(year, month + 1, 0).getDate() + firstMonthDayColumn;
+                rows = Math.ceil(requiredCells / 7);
+              }
+
+              var yearChange = isYear ? 10 : isMonth ? 1 : 0;
+              var monthChange = isDay ? 1 : 0;
+              var dayChange = isHour || isMinute ? 1 : 0;
+              var prevNextDay = isHour || isMinute ? day : 1;
+              var prevDate = new Date(year - yearChange, month - monthChange, prevNextDay - dayChange, hour);
+              var nextDate = new Date(year + yearChange, month + monthChange, prevNextDay + dayChange, hour);
+
+              var prevLast = isYear ? new Date(Math.ceil(year / 10) * 10 - 9, 0, 0) :
+                isMonth ? new Date(year, 0, 0) : isDay ? new Date(year, month, 0) : new Date(year, month, day, -1);
+              var nextFirst = isYear ? new Date(Math.ceil(year / 10) * 10 + 1, 0, 1) :
+                isMonth ? new Date(year + 1, 0, 1) : isDay ? new Date(year, month + 1, 1) : new Date(year, month, day + 1);
+
+              var tempMode = mode;
+              if (isDay && settings.showWeekNumbers){
+                tempMode += ' andweek';
+              }
+              var table = $('<table/>').addClass(className.table).addClass(tempMode).appendTo(container);
+              var textColumns = columns;
+              //no header for time-only mode
+              if (!isTimeOnly) {
+                var thead = $('<thead/>').appendTo(table);
+
+                row = $('<tr/>').appendTo(thead);
+                cell = $('<th/>').attr('colspan', '' + columns).appendTo(row);
+
+                var headerDate = isYear || isMonth ? new Date(year, 0, 1) :
+                  isDay ? new Date(year, month, 1) : new Date(year, month, day, hour, minute);
+                var headerText = $('<span/>').addClass(className.link).appendTo(cell);
+                headerText.text(formatter.header(headerDate, mode, settings));
+                var newMode = isMonth ? (settings.disableYear ? 'day' : 'year') :
+                  isDay ? (settings.disableMonth ? 'year' : 'month') : 'day';
+                headerText.data(metadata.mode, newMode);
+
+                if (p === 0) {
+                  var prev = $('<span/>').addClass(className.prev).appendTo(cell);
+                  prev.data(metadata.focusDate, prevDate);
+                  prev.toggleClass(className.disabledCell, !module.helper.isDateInRange(prevLast, mode));
+                  $('<i/>').addClass(className.prevIcon).appendTo(prev);
+                }
+
+                if (p === pages - 1) {
+                  var next = $('<span/>').addClass(className.next).appendTo(cell);
+                  next.data(metadata.focusDate, nextDate);
+                  next.toggleClass(className.disabledCell, !module.helper.isDateInRange(nextFirst, mode));
+                  $('<i/>').addClass(className.nextIcon).appendTo(next);
+                }
+                if (isDay) {
+                  row = $('<tr/>').appendTo(thead);
+                  if(settings.showWeekNumbers) {
+                      cell = $('<th/>').appendTo(row);
+                      cell.text(settings.text.weekNo);
+                      cell.addClass(className.weekCell);
+                      textColumns--;
+                  }
+                  for (i = 0; i < textColumns; i++) {
+                    cell = $('<th/>').appendTo(row);
+                    cell.text(formatter.dayColumnHeader((i + settings.firstDayOfWeek) % 7, settings));
+                  }
+                }
+              }
+
+              var tbody = $('<tbody/>').appendTo(table);
+              i = isYear ? Math.ceil(year / 10) * 10 - 9 : isDay ? 1 - firstMonthDayColumn : 0;
+              for (r = 0; r < rows; r++) {
+                row = $('<tr/>').appendTo(tbody);
+                if(isDay && settings.showWeekNumbers){
+                    cell = $('<th/>').appendTo(row);
+                    cell.text(module.get.weekOfYear(year,month,i+1-settings.firstDayOfWeek));
+                    cell.addClass(className.weekCell);
+                }
+                for (c = 0; c < textColumns; c++, i++) {
+                  var cellDate = isYear ? new Date(i, month, 1, hour, minute) :
+                    isMonth ? new Date(year, i, 1, hour, minute) : isDay ? new Date(year, month, i, hour, minute) :
+                      isHour ? new Date(year, month, day, i) : new Date(year, month, day, hour, i * settings.minTimeGap);
+                  var cellText = isYear ? i :
+                    isMonth ? settings.text.monthsShort[i] : isDay ? cellDate.getDate() :
+                      formatter.time(cellDate, settings, true);
+                  cell = $('<td/>').addClass(className.cell).appendTo(row);
+                  cell.text(cellText);
+                  cell.data(metadata.date, cellDate);
+                  var adjacent = isDay && cellDate.getMonth() !== ((month + 12) % 12);
+                  var disabled = (!settings.selectAdjacentDays && adjacent) || !module.helper.isDateInRange(cellDate, mode) || settings.isDisabled(cellDate, mode) || module.helper.isDisabled(cellDate, mode) || !module.helper.isEnabled(cellDate, mode);
+                  if (disabled) {
+                    var disabledDate = module.helper.findDayAsObject(cellDate, mode, settings.disabledDates);
+                    if (disabledDate !== null && disabledDate[metadata.message]) {
+                      cell.attr("data-tooltip", disabledDate[metadata.message]);
+                      cell.attr("data-position", tooltipPosition);
+                    }
+                  } else {
+                    var eventDate = module.helper.findDayAsObject(cellDate, mode, settings.eventDates);
+                    if (eventDate !== null) {
+                      cell.addClass(eventDate[metadata.class] || settings.eventClass);
+                      if (eventDate[metadata.message]) {
+                        cell.attr("data-tooltip", eventDate[metadata.message]);
+                        cell.attr("data-position", tooltipPosition);
+                      }
+                    }
+                  }
+                  var active = module.helper.dateEqual(cellDate, date, mode);
+                  var isToday = module.helper.dateEqual(cellDate, today, mode);
+                  cell.toggleClass(className.adjacentCell, adjacent);
+                  cell.toggleClass(className.disabledCell, disabled);
+                  cell.toggleClass(className.activeCell, active && !adjacent);
+                  if (!isHour && !isMinute) {
+                    cell.toggleClass(className.todayCell, !adjacent && isToday);
+                  }
+
+                  // Allow for external modifications of each cell
+                  var cellOptions = {
+                    mode: mode,
+                    adjacent: adjacent,
+                    disabled: disabled,
+                    active: active,
+                    today: isToday
+                  };
+                  formatter.cell(cell, cellDate, cellOptions);
+
+                  if (module.helper.dateEqual(cellDate, focusDate, mode)) {
+                    //ensure that the focus date is exactly equal to the cell date
+                    //so that, if selected, the correct value is set
+                    module.set.focusDate(cellDate, false, false);
+                  }
+                }
+              }
+
+              if (settings.today) {
+                var todayRow = $('<tr/>').appendTo(tbody);
+                var todayButton = $('<td/>').attr('colspan', '' + columns).addClass(className.today).appendTo(todayRow);
+                todayButton.text(formatter.today(settings));
+                todayButton.data(metadata.date, today);
+              }
+
+              module.update.focus(false, table);
+            }
+          }
+        },
+
+        update: {
+          focus: function (updateRange, container) {
+            container = container || $container;
+            var mode = module.get.mode();
+            var date = module.get.date();
+            var focusDate = module.get.focusDate();
+            var startDate = module.get.startDate();
+            var endDate = module.get.endDate();
+            var rangeDate = (updateRange ? focusDate : null) || date || (!isTouch ? focusDate : null);
+
+            container.find('td').each(function () {
+              var cell = $(this);
+              var cellDate = cell.data(metadata.date);
+              if (!cellDate) {
+                return;
+              }
+              var disabled = cell.hasClass(className.disabledCell);
+              var active = cell.hasClass(className.activeCell);
+              var adjacent = cell.hasClass(className.adjacentCell);
+              var focused = module.helper.dateEqual(cellDate, focusDate, mode);
+              var inRange = !rangeDate ? false :
+                ((!!startDate && module.helper.isDateInRange(cellDate, mode, startDate, rangeDate)) ||
+                (!!endDate && module.helper.isDateInRange(cellDate, mode, rangeDate, endDate)));
+              cell.toggleClass(className.focusCell, focused && (!isTouch || isTouchDown) && (!adjacent || (settings.selectAdjacentDays && adjacent)) && !disabled);
+              cell.toggleClass(className.rangeCell, inRange && !active && !disabled);
+            });
+          }
+        },
+
+        refresh: function () {
+          module.create.calendar();
+        },
+
+        bind: {
+          events: function () {
+            module.debug('Binding events');
+            $container.on('mousedown' + eventNamespace, module.event.mousedown);
+            $container.on('touchstart' + eventNamespace, module.event.mousedown);
+            $container.on('mouseup' + eventNamespace, module.event.mouseup);
+            $container.on('touchend' + eventNamespace, module.event.mouseup);
+            $container.on('mouseover' + eventNamespace, module.event.mouseover);
+            if ($input.length) {
+              $input.on('input' + eventNamespace, module.event.inputChange);
+              $input.on('focus' + eventNamespace, module.event.inputFocus);
+              $input.on('blur' + eventNamespace, module.event.inputBlur);
+              $input.on('click' + eventNamespace, module.event.inputClick);
+              $input.on('keydown' + eventNamespace, module.event.keydown);
+            } else {
+              $container.on('keydown' + eventNamespace, module.event.keydown);
+            }
+          }
+        },
+
+        unbind: {
+          events: function () {
+            module.debug('Unbinding events');
+            $container.off(eventNamespace);
+            if ($input.length) {
+              $input.off(eventNamespace);
+            }
+          }
+        },
+
+        event: {
+          mouseover: function (event) {
+            var target = $(event.target);
+            var date = target.data(metadata.date);
+            var mousedown = event.buttons === 1;
+            if (date) {
+              module.set.focusDate(date, false, true, mousedown);
+            }
+          },
+          mousedown: function (event) {
+            if ($input.length) {
+              //prevent the mousedown on the calendar causing the input to lose focus
+              event.preventDefault();
+            }
+            isTouchDown = event.type.indexOf('touch') >= 0;
+            var target = $(event.target);
+            var date = target.data(metadata.date);
+            if (date) {
+              module.set.focusDate(date, false, true, true);
+            }
+          },
+          mouseup: function (event) {
+            //ensure input has focus so that it receives keydown events for calendar navigation
+            module.focus();
+            event.preventDefault();
+            event.stopPropagation();
+            isTouchDown = false;
+            var target = $(event.target);
+            if (target.hasClass("disabled")) {
+              return;
+            }
+            var parent = target.parent();
+            if (parent.data(metadata.date) || parent.data(metadata.focusDate) || parent.data(metadata.mode)) {
+              //clicked on a child element, switch to parent (used when clicking directly on prev/next <i> icon element)
+              target = parent;
+            }
+            var date = target.data(metadata.date);
+            var focusDate = target.data(metadata.focusDate);
+            var mode = target.data(metadata.mode);
+            if (date && settings.onSelect.call(element, date, module.get.mode()) !== false) {
+              var forceSet = target.hasClass(className.today);
+              module.selectDate(date, forceSet);
+            }
+            else if (focusDate) {
+              module.set.focusDate(focusDate);
+            }
+            else if (mode) {
+              module.set.mode(mode);
+            }
+          },
+          keydown: function (event) {
+            var keyCode = event.which;
+            if (keyCode === 27 || keyCode === 9) {
+              //esc || tab
+              module.popup('hide');
+            }
+
+            if (module.popup('is visible')) {
+              if (keyCode === 37 || keyCode === 38 || keyCode === 39 || keyCode === 40) {
+                //arrow keys
+                var mode = module.get.mode();
+                var bigIncrement = mode === 'day' ? 7 : mode === 'hour' ? 4 : mode === 'minute' ? timeGap['column'] : 3;
+                var increment = keyCode === 37 ? -1 : keyCode === 38 ? -bigIncrement : keyCode == 39 ? 1 : bigIncrement;
+                increment *= mode === 'minute' ? settings.minTimeGap : 1;
+                var focusDate = module.get.focusDate() || module.get.date() || new Date();
+                var year = focusDate.getFullYear() + (mode === 'year' ? increment : 0);
+                var month = focusDate.getMonth() + (mode === 'month' ? increment : 0);
+                var day = focusDate.getDate() + (mode === 'day' ? increment : 0);
+                var hour = focusDate.getHours() + (mode === 'hour' ? increment : 0);
+                var minute = focusDate.getMinutes() + (mode === 'minute' ? increment : 0);
+                var newFocusDate = new Date(year, month, day, hour, minute);
+                if (settings.type === 'time') {
+                  newFocusDate = module.helper.mergeDateTime(focusDate, newFocusDate);
+                }
+                if (module.helper.isDateInRange(newFocusDate, mode)) {
+                  module.set.focusDate(newFocusDate);
+                }
+              } else if (keyCode === 13) {
+                //enter
+                var mode = module.get.mode();
+                var date = module.get.focusDate();
+                if (date && !settings.isDisabled(date, mode) && !module.helper.isDisabled(date, mode) && module.helper.isEnabled(date, mode)) {
+                  module.selectDate(date);
+                }
+                //disable form submission:
+                event.preventDefault();
+                event.stopPropagation();
+              }
+            }
+
+            if (keyCode === 38 || keyCode === 40) {
+              //arrow-up || arrow-down
+              event.preventDefault(); //don't scroll
+              module.popup('show');
+            }
+          },
+          inputChange: function () {
+            var val = $input.val();
+            var date = parser.date(val, settings);
+            module.set.date(date, false);
+          },
+          inputFocus: function () {
+            $container.addClass(className.active);
+          },
+          inputBlur: function () {
+            $container.removeClass(className.active);
+            if (settings.formatInput) {
+              var date = module.get.date();
+              var text = formatter.datetime(date, settings);
+              $input.val(text);
+            }
+          },
+          inputClick: function () {
+            module.popup('show');
+          }
+        },
+
+        get: {
+          weekOfYear: function(weekYear,weekMonth,weekDay) {
+              // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
+              var ms1d = 864e5, // milliseconds in a day
+                  ms7d = 7 * ms1d; // milliseconds in a week
+
+              return function() { // return a closure so constants get calculated only once
+                  var DC3 = Date.UTC(weekYear, weekMonth, weekDay + 3) / ms1d, // an Absolute Day Number
+                      AWN = Math.floor(DC3 / 7), // an Absolute Week Number
+                      Wyr = new Date(AWN * ms7d).getUTCFullYear();
+
+                  return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
+              }();
+          },
+          date: function () {
+            return module.helper.sanitiseDate($module.data(metadata.date)) || null;
+          },
+          inputDate: function() {
+            return $input.val();
+          },
+          focusDate: function () {
+            return $module.data(metadata.focusDate) || null;
+          },
+          startDate: function () {
+            var startModule = module.get.calendarModule(settings.startCalendar);
+            return (startModule ? startModule.get.date() : $module.data(metadata.startDate)) || null;
+          },
+          endDate: function () {
+            var endModule = module.get.calendarModule(settings.endCalendar);
+            return (endModule ? endModule.get.date() : $module.data(metadata.endDate)) || null;
+          },
+          minDate: function() {
+            return $module.data(metadata.minDate) || null;
+          },
+          maxDate: function() {
+            return $module.data(metadata.maxDate) || null;
+          },
+          monthOffset: function () {
+            return $module.data(metadata.monthOffset) || 0;
+          },
+          mode: function () {
+            //only returns valid modes for the current settings
+            var mode = $module.data(metadata.mode) || settings.startMode;
+            var validModes = module.get.validModes();
+            if ($.inArray(mode, validModes) >= 0) {
+              return mode;
+            }
+            return settings.type === 'time' ? 'hour' :
+              settings.type === 'month' ? 'month' :
+                settings.type === 'year' ? 'year' : 'day';
+          },
+          validModes: function () {
+            var validModes = [];
+            if (settings.type !== 'time') {
+              if (!settings.disableYear || settings.type === 'year') {
+                validModes.push('year');
+              }
+              if (!(settings.disableMonth || settings.type === 'year') || settings.type === 'month') {
+                validModes.push('month');
+              }
+              if (settings.type.indexOf('date') >= 0) {
+                validModes.push('day');
+              }
+            }
+            if (settings.type.indexOf('time') >= 0) {
+              validModes.push('hour');
+              if (!settings.disableMinute) {
+                validModes.push('minute');
+              }
+            }
+            return validModes;
+          },
+          isTouch: function () {
+            try {
+              document.createEvent('TouchEvent');
+              return true;
+            }
+            catch (e) {
+              return false;
+            }
+          },
+          calendarModule: function (selector) {
+            if (!selector) {
+              return null;
+            }
+            if (!(selector instanceof $)) {
+              selector = $(selector).first();
+            }
+            //assume range related calendars are using the same namespace
+            return selector.data(moduleNamespace);
+          }
+        },
+
+        set: {
+          date: function (date, updateInput, fireChange) {
+            updateInput = updateInput !== false;
+            fireChange = fireChange !== false;
+            date = module.helper.sanitiseDate(date);
+            date = module.helper.dateInRange(date);
+
+            var mode = module.get.mode();
+            var text = formatter.datetime(date, settings);
+            if (fireChange && settings.onChange.call(element, date, text, mode) === false) {
+              return false;
+            }
+
+            module.set.focusDate(date);
+
+            if (settings.isDisabled(date, mode)) {
+              return false;
+            }
+
+            var endDate = module.get.endDate();
+            if (!!endDate && !!date && date > endDate) {
+              //selected date is greater than end date in range, so clear end date
+              module.set.endDate(undefined);
+            }
+            module.set.dataKeyValue(metadata.date, date);
+
+            if (updateInput && $input.length) {
+              $input.val(text);
+            }
+          },
+          startDate: function (date, refreshCalendar) {
+            date = module.helper.sanitiseDate(date);
+            var startModule = module.get.calendarModule(settings.startCalendar);
+            if (startModule) {
+              startModule.set.date(date);
+            }
+            module.set.dataKeyValue(metadata.startDate, date, refreshCalendar);
+          },
+          endDate: function (date, refreshCalendar) {
+            date = module.helper.sanitiseDate(date);
+            var endModule = module.get.calendarModule(settings.endCalendar);
+            if (endModule) {
+              endModule.set.date(date);
+            }
+            module.set.dataKeyValue(metadata.endDate, date, refreshCalendar);
+          },
+          focusDate: function (date, refreshCalendar, updateFocus, updateRange) {
+            date = module.helper.sanitiseDate(date);
+            date = module.helper.dateInRange(date);
+            var isDay = module.get.mode() === 'day';
+            var oldFocusDate = module.get.focusDate();
+            if (isDay && date && oldFocusDate) {
+              var yearDelta = date.getFullYear() - oldFocusDate.getFullYear();
+              var monthDelta = yearDelta * 12 + date.getMonth() - oldFocusDate.getMonth();
+              if (monthDelta) {
+                var monthOffset = module.get.monthOffset() - monthDelta;
+                module.set.monthOffset(monthOffset, false);
+              }
+            }
+            var changed = module.set.dataKeyValue(metadata.focusDate, date, refreshCalendar);
+            updateFocus = (updateFocus !== false && changed && refreshCalendar === false) || focusDateUsedForRange != updateRange;
+            focusDateUsedForRange = updateRange;
+            if (updateFocus) {
+              module.update.focus(updateRange);
+            }
+          },
+          minDate: function (date) {
+            date = module.helper.sanitiseDate(date);
+            if (settings.maxDate !== null && settings.maxDate <= date) {
+              module.verbose('Unable to set minDate variable bigger that maxDate variable', date, settings.maxDate);
+            } else {
+              module.setting('minDate', date);
+              module.set.dataKeyValue(metadata.minDate, date);
+            }
+          },
+          maxDate: function (date) {
+            date = module.helper.sanitiseDate(date);
+            if (settings.minDate !== null && settings.minDate >= date) {
+              module.verbose('Unable to set maxDate variable lower that minDate variable', date, settings.minDate);
+            } else {
+              module.setting('maxDate', date);
+              module.set.dataKeyValue(metadata.maxDate, date);
+            }
+          },
+          monthOffset: function (monthOffset, refreshCalendar) {
+            var multiMonth = Math.max(settings.multiMonth, 1);
+            monthOffset = Math.max(1 - multiMonth, Math.min(0, monthOffset));
+            module.set.dataKeyValue(metadata.monthOffset, monthOffset, refreshCalendar);
+          },
+          mode: function (mode, refreshCalendar) {
+            module.set.dataKeyValue(metadata.mode, mode, refreshCalendar);
+          },
+          dataKeyValue: function (key, value, refreshCalendar) {
+            var oldValue = $module.data(key);
+            var equal = oldValue === value || (oldValue <= value && oldValue >= value); //equality test for dates and string objects
+            if (value) {
+              $module.data(key, value);
+            } else {
+              $module.removeData(key);
+            }
+            refreshCalendar = refreshCalendar !== false && !equal;
+            if (refreshCalendar) {
+              module.refresh();
+            }
+            return !equal;
+          }
+        },
+
+        selectDate: function (date, forceSet) {
+          module.verbose('New date selection', date);
+          var mode = module.get.mode();
+          var complete = forceSet || mode === 'minute' ||
+            (settings.disableMinute && mode === 'hour') ||
+            (settings.type === 'date' && mode === 'day') ||
+            (settings.type === 'month' && mode === 'month') ||
+            (settings.type === 'year' && mode === 'year');
+          if (complete) {
+            var canceled = module.set.date(date) === false;
+            if (!canceled && settings.closable) {
+              module.popup('hide');
+              //if this is a range calendar, show the end date calendar popup and focus the input
+              var endModule = module.get.calendarModule(settings.endCalendar);
+              if (endModule) {
+                endModule.popup('show');
+                endModule.focus();
+              }
+            }
+          } else {
+            var newMode = mode === 'year' ? (!settings.disableMonth ? 'month' : 'day') :
+              mode === 'month' ? 'day' : mode === 'day' ? 'hour' : 'minute';
+            module.set.mode(newMode);
+            if (mode === 'hour' || (mode === 'day' && module.get.date())) {
+              //the user has chosen enough to consider a valid date/time has been chosen
+              module.set.date(date);
+            } else {
+              module.set.focusDate(date);
+            }
+          }
+        },
+
+        changeDate: function (date) {
+          module.set.date(date);
+        },
+
+        clear: function () {
+          module.set.date(undefined);
+        },
+
+        popup: function () {
+          return $activator.popup.apply($activator, arguments);
+        },
+
+        focus: function () {
+          if ($input.length) {
+            $input.focus();
+          } else {
+            $container.focus();
+          }
+        },
+        blur: function () {
+          if ($input.length) {
+            $input.blur();
+          } else {
+            $container.blur();
+          }
+        },
+
+        helper: {
+          isDisabled: function(date, mode) {
+            return mode === 'day' && ((settings.disabledDaysOfWeek.indexOf(date.getDay()) !== -1) || settings.disabledDates.some(function(d){
+              if(typeof d === 'string') {
+                d = module.helper.sanitiseDate(d);
+              }
+              if (d instanceof Date) {
+                return module.helper.dateEqual(date, d, mode);
+              }
+              if (d !== null && typeof d === 'object' && d[metadata.date]) {
+                return module.helper.dateEqual(date, module.helper.sanitiseDate(d[metadata.date]), mode);
+              }
+            }));
+          },
+          isEnabled: function(date, mode) {
+            if (mode === 'day') {
+              return settings.enabledDates.length === 0 || settings.enabledDates.some(function(d){
+                if(typeof d === 'string') {
+                  d = module.helper.sanitiseDate(d);
+                }
+                if (d instanceof Date) {
+                  return module.helper.dateEqual(date, d, mode);
+                }
+                if (d !== null && typeof d === 'object' && d[metadata.date]) {
+                  return module.helper.dateEqual(date, module.helper.sanitiseDate(d[metadata.date]), mode);
+                }
+              });
+            } else {
+              return true;
+            }
+          },
+          findDayAsObject: function(date, mode, dates) {
+            if (mode === 'day') {
+              var i = 0, il = dates.length;
+              var d;
+              for (; i < il; i++) {
+                d = dates[i];
+                if(typeof d === 'string') {
+                  d = module.helper.sanitiseDate(d);
+                }
+                if (d instanceof Date && module.helper.dateEqual(date, d, mode)) {
+                  var dateObject = {};
+                  dateObject[metadata.date] = d;
+                  return dateObject;
+                }
+                else if (d !== null && typeof d === 'object' && d[metadata.date] && module.helper.dateEqual(date,module.helper.sanitiseDate(d[metadata.date]), mode)  ) {
+                  return d;
+                }
+              }
+            }
+            return null;
+          },
+          sanitiseDate: function (date) {
+            if (!date) {
+              return undefined;
+            }
+            if (!(date instanceof Date)) {
+              date = parser.date('' + date, settings);
+            }
+            if (!date || date === null || isNaN(date.getTime())) {
+              return undefined;
+            }
+            return date;
+          },
+          dateDiff: function (date1, date2, mode) {
+            mode = mode || 'day';
+            var isTimeOnly = settings.type === 'time';
+            var isYear = mode === 'year';
+            var isYearOrMonth = isYear || mode === 'month';
+            var isMinute = mode === 'minute';
+            var isHourOrMinute = isMinute || mode === 'hour';
+            //only care about a minute accuracy of settings.minTimeGap
+            date1 = new Date(
+              isTimeOnly ? 2000 : date1.getFullYear(),
+              isTimeOnly ? 0 : isYear ? 0 : date1.getMonth(),
+              isTimeOnly ? 1 : isYearOrMonth ? 1 : date1.getDate(),
+              !isHourOrMinute ? 0 : date1.getHours(),
+              !isMinute ? 0 : settings.minTimeGap * Math.floor(date1.getMinutes() / settings.minTimeGap));
+            date2 = new Date(
+              isTimeOnly ? 2000 : date2.getFullYear(),
+              isTimeOnly ? 0 : isYear ? 0 : date2.getMonth(),
+              isTimeOnly ? 1 : isYearOrMonth ? 1 : date2.getDate(),
+              !isHourOrMinute ? 0 : date2.getHours(),
+              !isMinute ? 0 : settings.minTimeGap * Math.floor(date2.getMinutes() / settings.minTimeGap));
+            return date2.getTime() - date1.getTime();
+          },
+          dateEqual: function (date1, date2, mode) {
+            return !!date1 && !!date2 && module.helper.dateDiff(date1, date2, mode) === 0;
+          },
+          isDateInRange: function (date, mode, minDate, maxDate) {
+            if (!minDate && !maxDate) {
+              var startDate = module.get.startDate();
+              minDate = startDate && settings.minDate ? new Date(Math.max(startDate, settings.minDate)) : startDate || settings.minDate;
+              maxDate = settings.maxDate;
+            }
+            minDate = minDate && new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate(), minDate.getHours(), settings.minTimeGap * Math.ceil(minDate.getMinutes() / settings.minTimeGap));
+            return !(!date ||
+            (minDate && module.helper.dateDiff(date, minDate, mode) > 0) ||
+            (maxDate && module.helper.dateDiff(maxDate, date, mode) > 0));
+          },
+          dateInRange: function (date, minDate, maxDate) {
+            if (!minDate && !maxDate) {
+              var startDate = module.get.startDate();
+              minDate = startDate && settings.minDate ? new Date(Math.max(startDate, settings.minDate)) : startDate || settings.minDate;
+              maxDate = settings.maxDate;
+            }
+            minDate = minDate && new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate(), minDate.getHours(), settings.minTimeGap * Math.ceil(minDate.getMinutes() / settings.minTimeGap));
+            var isTimeOnly = settings.type === 'time';
+            return !date ? date :
+              (minDate && module.helper.dateDiff(date, minDate, 'minute') > 0) ?
+                (isTimeOnly ? module.helper.mergeDateTime(date, minDate) : minDate) :
+                (maxDate && module.helper.dateDiff(maxDate, date, 'minute') > 0) ?
+                  (isTimeOnly ? module.helper.mergeDateTime(date, maxDate) : maxDate) :
+                  date;
+          },
+          mergeDateTime: function (date, time) {
+            return (!date || !time) ? time :
+              new Date(date.getFullYear(), date.getMonth(), date.getDate(), time.getHours(), time.getMinutes());
+          }
+        },
+
+        setting: function (name, value) {
+          module.debug('Changing setting', name, value);
+          if ($.isPlainObject(name)) {
+            $.extend(true, settings, name);
+          }
+          else if (value !== undefined) {
+            if ($.isPlainObject(settings[name])) {
+              $.extend(true, settings[name], value);
+            }
+            else {
+              settings[name] = value;
+            }
+          }
+          else {
+            return settings[name];
+          }
+        },
+        internal: function (name, value) {
+          if( $.isPlainObject(name) ) {
+            $.extend(true, module, name);
+          }
+          else if(value !== undefined) {
+            module[name] = value;
+          }
+          else {
+            return module[name];
+          }
+        },
+        debug: function () {
+          if (!settings.silent && settings.debug) {
+            if (settings.performance) {
+              module.performance.log(arguments);
+            }
+            else {
+              module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
+              module.debug.apply(console, arguments);
+            }
+          }
+        },
+        verbose: function () {
+          if (!settings.silent && settings.verbose && settings.debug) {
+            if (settings.performance) {
+              module.performance.log(arguments);
+            }
+            else {
+              module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
+              module.verbose.apply(console, arguments);
+            }
+          }
+        },
+        error: function () {
+          if (!settings.silent) {
+            module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
+            module.error.apply(console, arguments);
+          }
+        },
+        performance: {
+          log: function (message) {
+            var
+              currentTime,
+              executionTime,
+              previousTime
+              ;
+            if (settings.performance) {
+              currentTime = new Date().getTime();
+              previousTime = time || currentTime;
+              executionTime = currentTime - previousTime;
+              time = currentTime;
+              performance.push({
+                'Name': message[0],
+                'Arguments': [].slice.call(message, 1) || '',
+                'Element': element,
+                'Execution Time': executionTime
+              });
+            }
+            clearTimeout(module.performance.timer);
+            module.performance.timer = setTimeout(module.performance.display, 500);
+          },
+          display: function () {
+            var
+              title = settings.name + ':',
+              totalTime = 0
+              ;
+            time = false;
+            clearTimeout(module.performance.timer);
+            $.each(performance, function (index, data) {
+              totalTime += data['Execution Time'];
+            });
+            title += ' ' + totalTime + 'ms';
+            if (moduleSelector) {
+              title += ' \'' + moduleSelector + '\'';
+            }
+            if ((console.group !== undefined || console.table !== undefined) && performance.length > 0) {
+              console.groupCollapsed(title);
+              if (console.table) {
+                console.table(performance);
+              }
+              else {
+                $.each(performance, function (index, data) {
+                  console.log(data['Name'] + ': ' + data['Execution Time'] + 'ms');
+                });
+              }
+              console.groupEnd();
+            }
+            performance = [];
+          }
+        },
+        invoke: function (query, passedArguments, context) {
+          var
+            object = instance,
+            maxDepth,
+            found,
+            response
+            ;
+          passedArguments = passedArguments || queryArguments;
+          context = element || context;
+          if (typeof query == 'string' && object !== undefined) {
+            query = query.split(/[\. ]/);
+            maxDepth = query.length - 1;
+            $.each(query, function (depth, value) {
+              var camelCaseValue = (depth != maxDepth)
+                  ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
+                  : query
+                ;
+              if ($.isPlainObject(object[camelCaseValue]) && (depth != maxDepth)) {
+                object = object[camelCaseValue];
+              }
+              else if (object[camelCaseValue] !== undefined) {
+                found = object[camelCaseValue];
+                return false;
+              }
+              else if ($.isPlainObject(object[value]) && (depth != maxDepth)) {
+                object = object[value];
+              }
+              else if (object[value] !== undefined) {
+                found = object[value];
+                return false;
+              }
+              else {
+                module.error(error.method, query);
+                return false;
+              }
+            });
+          }
+          if ($.isFunction(found)) {
+            response = found.apply(context, passedArguments);
+          }
+          else if (found !== undefined) {
+            response = found;
+          }
+          if (Array.isArray(returnedValue)) {
+            returnedValue.push(response);
+          }
+          else if (returnedValue !== undefined) {
+            returnedValue = [returnedValue, response];
+          }
+          else if (response !== undefined) {
+            returnedValue = response;
+          }
+          return found;
+        }
+      };
+
+      if (methodInvoked) {
+        if (instance === undefined) {
+          module.initialize();
+        }
+        module.invoke(query);
+      }
+      else {
+        if (instance !== undefined) {
+          instance.invoke('destroy');
+        }
+        module.initialize();
+      }
+    })
+  ;
+  return (returnedValue !== undefined)
+    ? returnedValue
+    : this
+    ;
+};
+
+$.fn.calendar.settings = {
+
+  name            : 'Calendar',
+  namespace       : 'calendar',
+
+  silent: false,
+  debug: false,
+  verbose: false,
+  performance: false,
+
+  type               : 'datetime', // picker type, can be 'datetime', 'date', 'time', 'month', or 'year'
+  firstDayOfWeek     : 0,          // day for first day column (0 = Sunday)
+  constantHeight     : true,       // add rows to shorter months to keep day calendar height consistent (6 rows)
+  today              : false,      // show a 'today/now' button at the bottom of the calendar
+  closable           : true,       // close the popup after selecting a date/time
+  monthFirst         : true,       // month before day when parsing/converting date from/to text
+  touchReadonly      : true,       // set input to readonly on touch devices
+  inline             : false,      // create the calendar inline instead of inside a popup
+  on                 : null,       // when to show the popup (defaults to 'focus' for input, 'click' for others)
+  initialDate        : null,       // date to display initially when no date is selected (null = now)
+  startMode          : false,      // display mode to start in, can be 'year', 'month', 'day', 'hour', 'minute' (false = 'day')
+  minDate            : null,       // minimum date/time that can be selected, dates/times before are disabled
+  maxDate            : null,       // maximum date/time that can be selected, dates/times after are disabled
+  ampm               : true,       // show am/pm in time mode
+  disableYear        : false,      // disable year selection mode
+  disableMonth       : false,      // disable month selection mode
+  disableMinute      : false,      // disable minute selection mode
+  formatInput        : true,       // format the input text upon input blur and module creation
+  startCalendar      : null,       // jquery object or selector for another calendar that represents the start date of a date range
+  endCalendar        : null,       // jquery object or selector for another calendar that represents the end date of a date range
+  multiMonth         : 1,          // show multiple months when in 'day' mode
+  minTimeGap         : 5,
+  showWeekNumbers    : null,       // show Number of Week at the very first column of a dayView
+  disabledDates      : [],         // specific day(s) which won't be selectable and contain additional information.
+  disabledDaysOfWeek : [],         // day(s) which won't be selectable(s) (0 = Sunday)
+  enabledDates       : [],         // specific day(s) which will be selectable, all other days will be disabled
+  eventDates         : [],         // specific day(s) which will be shown in a different color and using tooltips
+  centuryBreak       : 60,         // starting short year until 99 where it will be assumed to belong to the last century
+  currentCentury     : 2000,       // century to be added to 2-digit years (00 to {centuryBreak}-1)
+  selectAdjacentDays : false,     // The calendar can show dates from adjacent month. These adjacent month dates can also be made selectable.
+  // popup options ('popup', 'on', 'hoverable', and show/hide callbacks are overridden)
+  popupOptions: {
+    position: 'bottom left',
+    lastResort: 'bottom left',
+    prefer: 'opposite',
+    hideOnScroll: false
+  },
+
+  text: {
+    days: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
+    months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
+    monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
+    today: 'Today',
+    now: 'Now',
+    am: 'AM',
+    pm: 'PM',
+    weekNo: 'Week'
+  },
+
+  formatter: {
+    header: function (date, mode, settings) {
+      return mode === 'year' ? settings.formatter.yearHeader(date, settings) :
+        mode === 'month' ? settings.formatter.monthHeader(date, settings) :
+          mode === 'day' ? settings.formatter.dayHeader(date, settings) :
+            mode === 'hour' ? settings.formatter.hourHeader(date, settings) :
+              settings.formatter.minuteHeader(date, settings);
+    },
+    yearHeader: function (date, settings) {
+      var decadeYear = Math.ceil(date.getFullYear() / 10) * 10;
+      return (decadeYear - 9) + ' - ' + (decadeYear + 2);
+    },
+    monthHeader: function (date, settings) {
+      return date.getFullYear();
+    },
+    dayHeader: function (date, settings) {
+      var month = settings.text.months[date.getMonth()];
+      var year = date.getFullYear();
+      return month + ' ' + year;
+    },
+    hourHeader: function (date, settings) {
+      return settings.formatter.date(date, settings);
+    },
+    minuteHeader: function (date, settings) {
+      return settings.formatter.date(date, settings);
+    },
+    dayColumnHeader: function (day, settings) {
+      return settings.text.days[day];
+    },
+    datetime: function (date, settings) {
+      if (!date) {
+        return '';
+      }
+      var day = settings.type === 'time' ? '' : settings.formatter.date(date, settings);
+      var time = settings.type.indexOf('time') < 0 ? '' : settings.formatter.time(date, settings, false);
+      var separator = settings.type === 'datetime' ? ' ' : '';
+      return day + separator + time;
+    },
+    date: function (date, settings) {
+      if (!date) {
+        return '';
+      }
+      var day = date.getDate();
+      var month = settings.text.months[date.getMonth()];
+      var year = date.getFullYear();
+      return settings.type === 'year' ? year :
+        settings.type === 'month' ? month + ' ' + year :
+        (settings.monthFirst ? month + ' ' + day : day + ' ' + month) + ', ' + year;
+    },
+    time: function (date, settings, forCalendar) {
+      if (!date) {
+        return '';
+      }
+      var hour = date.getHours();
+      var minute = date.getMinutes();
+      var ampm = '';
+      if (settings.ampm) {
+        ampm = ' ' + (hour < 12 ? settings.text.am : settings.text.pm);
+        hour = hour === 0 ? 12 : hour > 12 ? hour - 12 : hour;
+      }
+      return hour + ':' + (minute < 10 ? '0' : '') + minute + ampm;
+    },
+    today: function (settings) {
+      return settings.type === 'date' ? settings.text.today : settings.text.now;
+    },
+    cell: function (cell, date, cellOptions) {
+    }
+  },
+
+  parser: {
+    date: function (text, settings) {
+      if (text instanceof Date) {
+        return text;
+      }
+      if (!text) {
+        return null;
+      }
+      text = ('' + text).trim().toLowerCase();
+      if (text.length === 0) {
+        return null;
+      }
+      var textDate = new Date(text);
+      if(!isNaN(textDate.getDate())) {
+        return textDate;
+      }
+
+      var i, j, k;
+      var minute = -1, hour = -1, day = -1, month = -1, year = -1;
+      var isAm = undefined;
+
+      var isTimeOnly = settings.type === 'time';
+      var isDateOnly = settings.type.indexOf('time') < 0;
+
+      var words = text.split(settings.regExp.dateWords);
+      var numbers = text.split(settings.regExp.dateNumbers);
+
+      if (!isDateOnly) {
+        //am/pm
+        isAm = $.inArray(settings.text.am.toLowerCase(), words) >= 0 ? true :
+          $.inArray(settings.text.pm.toLowerCase(), words) >= 0 ? false : undefined;
+
+        //time with ':'
+        for (i = 0; i < numbers.length; i++) {
+          var number = numbers[i];
+          if (number.indexOf(':') >= 0) {
+            if (hour < 0 || minute < 0) {
+              var parts = number.split(':');
+              for (k = 0; k < Math.min(2, parts.length); k++) {
+                j = parseInt(parts[k]);
+                if (isNaN(j)) {
+                  j = 0;
+                }
+                if (k === 0) {
+                  hour = j % 24;
+                } else {
+                  minute = j % 60;
+                }
+              }
+            }
+            numbers.splice(i, 1);
+          }
+        }
+      }
+
+      if (!isTimeOnly) {
+        //textual month
+        for (i = 0; i < words.length; i++) {
+          var word = words[i];
+          if (word.length <= 0) {
+            continue;
+          }
+          word = word.substring(0, Math.min(word.length, 3));
+          for (j = 0; j < settings.text.months.length; j++) {
+            var monthString = settings.text.months[j];
+            monthString = monthString.substring(0, Math.min(word.length, Math.min(monthString.length, 3))).toLowerCase();
+            if (monthString === word) {
+              month = j + 1;
+              break;
+            }
+          }
+          if (month >= 0) {
+            break;
+          }
+        }
+
+        //year > settings.centuryBreak
+        for (i = 0; i < numbers.length; i++) {
+          j = parseInt(numbers[i]);
+          if (isNaN(j)) {
+            continue;
+          }
+          if (j >= settings.centuryBreak && i === numbers.length-1) {
+            if (j <= 99) {
+              j += settings.currentCentury - 100;
+            }
+            year = j;
+            numbers.splice(i, 1);
+            break;
+          }
+        }
+
+        //numeric month
+        if (month < 0) {
+          for (i = 0; i < numbers.length; i++) {
+            k = i > 1 || settings.monthFirst ? i : i === 1 ? 0 : 1;
+            j = parseInt(numbers[k]);
+            if (isNaN(j)) {
+              continue;
+            }
+            if (1 <= j && j <= 12) {
+              month = j;
+              numbers.splice(k, 1);
+              break;
+            }
+          }
+        }
+
+        //day
+        for (i = 0; i < numbers.length; i++) {
+          j = parseInt(numbers[i]);
+          if (isNaN(j)) {
+            continue;
+          }
+          if (1 <= j && j <= 31) {
+            day = j;
+            numbers.splice(i, 1);
+            break;
+          }
+        }
+
+        //year <= settings.centuryBreak
+        if (year < 0) {
+          for (i = numbers.length - 1; i >= 0; i--) {
+            j = parseInt(numbers[i]);
+            if (isNaN(j)) {
+              continue;
+            }
+            if (j <= 99) {
+              j += settings.currentCentury;
+            }
+            year = j;
+            numbers.splice(i, 1);
+            break;
+          }
+        }
+      }
+
+      if (!isDateOnly) {
+        //hour
+        if (hour < 0) {
+          for (i = 0; i < numbers.length; i++) {
+            j = parseInt(numbers[i]);
+            if (isNaN(j)) {
+              continue;
+            }
+            if (0 <= j && j <= 23) {
+              hour = j;
+              numbers.splice(i, 1);
+              break;
+            }
+          }
+        }
+
+        //minute
+        if (minute < 0) {
+          for (i = 0; i < numbers.length; i++) {
+            j = parseInt(numbers[i]);
+            if (isNaN(j)) {
+              continue;
+            }
+            if (0 <= j && j <= 59) {
+              minute = j;
+              numbers.splice(i, 1);
+              break;
+            }
+          }
+        }
+      }
+
+      if (minute < 0 && hour < 0 && day < 0 && month < 0 && year < 0) {
+        return null;
+      }
+
+      if (minute < 0) {
+        minute = 0;
+      }
+      if (hour < 0) {
+        hour = 0;
+      }
+      if (day < 0) {
+        day = 1;
+      }
+      if (month < 0) {
+        month = 1;
+      }
+      if (year < 0) {
+        year = new Date().getFullYear();
+      }
+
+      if (isAm !== undefined) {
+        if (isAm) {
+          if (hour === 12) {
+            hour = 0;
+          }
+        } else if (hour < 12) {
+          hour += 12;
+        }
+      }
+
+      var date = new Date(year, month - 1, day, hour, minute);
+      if (date.getMonth() !== month - 1 || date.getFullYear() !== year) {
+        //month or year don't match up, switch to last day of the month
+        date = new Date(year, month, 0, hour, minute);
+      }
+      return isNaN(date.getTime()) ? null : date;
+    }
+  },
+
+  // callback when date changes, return false to cancel the change
+  onChange: function (date, text, mode) {
+    return true;
+  },
+
+  // callback before show animation, return false to prevent show
+  onShow: function () {
+  },
+
+  // callback after show animation
+  onVisible: function () {
+  },
+
+  // callback before hide animation, return false to prevent hide
+  onHide: function () {
+  },
+
+  // callback after hide animation
+  onHidden: function () {
+  },
+
+  // callback before item is selected, return false to prevent selection
+  onSelect: function (date, mode) {
+  },
+
+  // is the given date disabled?
+  isDisabled: function (date, mode) {
+    return false;
+  },
+
+  selector: {
+    popup: '.ui.popup',
+    input: 'input',
+    activator: 'input',
+    append: '.inline.field,.inline.fields'
+  },
+
+  regExp: {
+    dateWords: /[^A-Za-z\u00C0-\u024F]+/g,
+    dateNumbers: /[^\d:]+/g
+  },
+
+  error: {
+    popup: 'UI Popup, a required component is not included in this page',
+    method: 'The method you called is not defined.'
+  },
+
+  className: {
+    calendar: 'calendar',
+    active: 'active',
+    popup: 'ui popup',
+    grid: 'ui equal width grid',
+    column: 'column',
+    table: 'ui celled center aligned unstackable table',
+    prev: 'prev link',
+    next: 'next link',
+    prevIcon: 'chevron left icon',
+    nextIcon: 'chevron right icon',
+    link: 'link',
+    cell: 'link',
+    disabledCell: 'disabled',
+    weekCell: 'disabled',
+    adjacentCell: 'adjacent',
+    activeCell: 'active',
+    rangeCell: 'range',
+    focusCell: 'focus',
+    todayCell: 'today',
+    today: 'today link'
+  },
+
+  metadata: {
+    date: 'date',
+    focusDate: 'focusDate',
+    startDate: 'startDate',
+    endDate: 'endDate',
+    minDate: 'minDate',
+    maxDate: 'maxDate',
+    mode: 'mode',
+    monthOffset: 'monthOffset',
+    message: 'message',
+    class: 'class'
+  },
+
+  eventClass: 'blue'
+};
+
+})(jQuery, window, document);

Разлика између датотеке није приказан због своје велике величине
+ 8 - 0
website/semantic/dist/components/calendar.min.css


Разлика између датотеке није приказан због своје велике величине
+ 10 - 0
website/semantic/dist/components/calendar.min.js


+ 1293 - 0
website/semantic/dist/components/card.css

@@ -0,0 +1,1293 @@
+/*!
+ * # Fomantic-UI - Card
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */
+
+
+/*******************************
+            Standard
+*******************************/
+
+
+/*--------------
+      Card
+---------------*/
+
+.ui.cards > .card,
+.ui.card {
+  max-width: 100%;
+  position: relative;
+  display: -webkit-box;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-box-orient: vertical;
+  -webkit-box-direction: normal;
+      -ms-flex-direction: column;
+          flex-direction: column;
+  width: 290px;
+  min-height: 0;
+  background: #FFFFFF;
+  padding: 0;
+  border: none;
+  border-radius: 0.28571429rem;
+  -webkit-box-shadow: 0 1px 3px 0 #D4D4D5, 0 0 0 1px #D4D4D5;
+          box-shadow: 0 1px 3px 0 #D4D4D5, 0 0 0 1px #D4D4D5;
+  -webkit-transition: -webkit-box-shadow 0.1s ease, -webkit-transform 0.1s ease;
+  transition: -webkit-box-shadow 0.1s ease, -webkit-transform 0.1s ease;
+  transition: box-shadow 0.1s ease, transform 0.1s ease;
+  transition: box-shadow 0.1s ease, transform 0.1s ease, -webkit-box-shadow 0.1s ease, -webkit-transform 0.1s ease;
+  z-index: '';
+  word-wrap: break-word;
+}
+.ui.card {
+  margin: 1em 0;
+}
+.ui.cards > .card a,
+.ui.card a {
+  cursor: pointer;
+}
+.ui.card:first-child {
+  margin-top: 0;
+}
+.ui.card:last-child {
+  margin-bottom: 0;
+}
+
+/*--------------
+      Cards
+---------------*/
+
+.ui.cards {
+  display: -webkit-box;
+  display: -ms-flexbox;
+  display: flex;
+  margin: -0.875em -0.5em;
+  -ms-flex-wrap: wrap;
+      flex-wrap: wrap;
+}
+.ui.cards > .card {
+  display: -webkit-box;
+  display: -ms-flexbox;
+  display: flex;
+  margin: 0.875em 0.5em;
+  float: none;
+}
+
+/* Clearing */
+.ui.cards:after,
+.ui.card:after {
+  display: block;
+  content: ' ';
+  height: 0;
+  clear: both;
+  overflow: hidden;
+  visibility: hidden;
+}
+
+/* Consecutive Card Groups Preserve Row Spacing */
+.ui.cards ~ .ui.cards {
+  margin-top: 0.875em;
+}
+
+/*--------------
+  Rounded Edges
+---------------*/
+
+.ui.cards > .card > :first-child,
+.ui.card > :first-child {
+  border-radius: 0.28571429rem 0.28571429rem 0 0 !important;
+  border-top: none !important;
+}
+.ui.cards > .card > :last-child,
+.ui.card > :last-child {
+  border-radius: 0 0 0.28571429rem 0.28571429rem !important;
+}
+.ui.cards > .card > :only-child,
+.ui.card > :only-child {
+  border-radius: 0.28571429rem !important;
+}
+
+/*--------------
+     Images
+---------------*/
+
+.ui.cards > .card > .image,
+.ui.card > .image {
+  position: relative;
+  display: block;
+  -webkit-box-flex: 0;
+      -ms-flex: 0 0 auto;
+          flex: 0 0 auto;
+  padding: 0;
+  background: rgba(0, 0, 0, 0.05);
+}
+.ui.cards > .card > .image > img,
+.ui.card > .image > img {
+  display: block;
+  width: 100%;
+  height: auto;
+  border-radius: inherit;
+}
+.ui.cards > .card > .image:not(.ui) > img,
+.ui.card > .image:not(.ui) > img {
+  border: none;
+}
+
+/*--------------
+     Content
+---------------*/
+
+.ui.cards > .card > .content,
+.ui.card > .content {
+  -webkit-box-flex: 1;
+      -ms-flex-positive: 1;
+          flex-grow: 1;
+  border: none;
+  border-top: 1px solid rgba(34, 36, 38, 0.1);
+  background: none;
+  margin: 0;
+  padding: 1em 1em;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+  font-size: 1em;
+  border-radius: 0;
+}
+.ui.cards > .card > .content:after,
+.ui.card > .content:after {
+  display: block;
+  content: ' ';
+  height: 0;
+  clear: both;
+  overflow: hidden;
+  visibility: hidden;
+}
+.ui.cards > .card > .content > .header,
+.ui.card > .content > .header {
+  display: block;
+  margin: '';
+  font-family: 'Lato', 'Helvetica Neue', Arial, Helvetica, sans-serif;
+  color: rgba(0, 0, 0, 0.85);
+}
+
+/* Default Header Size */
+.ui.cards > .card > .content > .header:not(.ui),
+.ui.card > .content > .header:not(.ui) {
+  font-weight: bold;
+  font-size: 1.28571429em;
+  margin-top: -0.21425em;
+  line-height: 1.28571429em;
+}
+.ui.cards > .card > .content > .meta + .description,
+.ui.cards > .card > .content > .header + .description,
+.ui.card > .content > .meta + .description,
+.ui.card > .content > .header + .description {
+  margin-top: 0.5em;
+}
+
+/*----------------
+ Floated Content
+-----------------*/
+
+.ui.cards > .card [class*="left floated"],
+.ui.card [class*="left floated"] {
+  float: left;
+}
+.ui.cards > .card [class*="right floated"],
+.ui.card [class*="right floated"] {
+  float: right;
+}
+
+/*--------------
+     Aligned
+---------------*/
+
+.ui.cards > .card [class*="left aligned"],
+.ui.card [class*="left aligned"] {
+  text-align: left;
+}
+.ui.cards > .card [class*="center aligned"],
+.ui.card [class*="center aligned"] {
+  text-align: center;
+}
+.ui.cards > .card [class*="right aligned"],
+.ui.card [class*="right aligned"] {
+  text-align: right;
+}
+
+/*--------------
+  Content Image
+---------------*/
+
+.ui.cards > .card .content img,
+.ui.card .content img {
+  display: inline-block;
+  vertical-align: middle;
+  width: '';
+}
+.ui.cards > .card img.avatar,
+.ui.cards > .card .avatar img,
+.ui.card img.avatar,
+.ui.card .avatar img {
+  width: 2em;
+  height: 2em;
+  border-radius: 500rem;
+}
+
+/*--------------
+   Description
+---------------*/
+
+.ui.cards > .card > .content > .description,
+.ui.card > .content > .description {
+  clear: both;
+  color: rgba(0, 0, 0, 0.68);
+}
+
+/*--------------
+    Paragraph
+---------------*/
+
+.ui.cards > .card > .content p,
+.ui.card > .content p {
+  margin: 0 0 0.5em;
+}
+.ui.cards > .card > .content p:last-child,
+.ui.card > .content p:last-child {
+  margin-bottom: 0;
+}
+
+/*--------------
+      Meta
+---------------*/
+
+.ui.cards > .card .meta,
+.ui.card .meta {
+  font-size: 1em;
+  color: rgba(0, 0, 0, 0.4);
+}
+.ui.cards > .card .meta *,
+.ui.card .meta * {
+  margin-right: 0.3em;
+}
+.ui.cards > .card .meta :last-child,
+.ui.card .meta :last-child {
+  margin-right: 0;
+}
+.ui.cards > .card .meta [class*="right floated"],
+.ui.card .meta [class*="right floated"] {
+  margin-right: 0;
+  margin-left: 0.3em;
+}
+
+/*--------------
+      Links
+---------------*/
+
+
+/* Generic */
+.ui.cards > .card > .content a:not(.ui),
+.ui.card > .content a:not(.ui) {
+  color: '';
+  -webkit-transition: color 0.1s ease;
+  transition: color 0.1s ease;
+}
+.ui.cards > .card > .content a:not(.ui):hover,
+.ui.card > .content a:not(.ui):hover {
+  color: '';
+}
+
+/* Header */
+.ui.cards > .card > .content > a.header,
+.ui.card > .content > a.header {
+  color: rgba(0, 0, 0, 0.85);
+}
+.ui.cards > .card > .content > a.header:hover,
+.ui.card > .content > a.header:hover {
+  color: #1e70bf;
+}
+
+/* Meta */
+.ui.cards > .card .meta > a:not(.ui),
+.ui.card .meta > a:not(.ui) {
+  color: rgba(0, 0, 0, 0.4);
+}
+.ui.cards > .card .meta > a:not(.ui):hover,
+.ui.card .meta > a:not(.ui):hover {
+  color: #AAAAAA;
+}
+
+/*--------------
+     Buttons
+---------------*/
+
+.ui.cards > .card > .buttons,
+.ui.card > .buttons,
+.ui.cards > .card > .button,
+.ui.card > .button {
+  margin: 0 -1px;
+  width: calc(100% + 2px);
+}
+.ui.cards > .card > .buttons:last-child,
+.ui.card > .buttons:last-child,
+.ui.cards > .card > .button:last-child,
+.ui.card > .button:last-child {
+  margin-bottom: -1px;
+}
+
+/*--------------
+      Dimmer
+---------------*/
+
+.ui.cards > .card .dimmer,
+.ui.card .dimmer {
+  background-color: '';
+  z-index: 10;
+}
+
+/*--------------
+     Labels
+---------------*/
+
+
+/*-----Star----- */
+
+
+/* Icon */
+.ui.cards > .card > .content .star.icon,
+.ui.card > .content .star.icon {
+  cursor: pointer;
+  opacity: 0.75;
+  -webkit-transition: color 0.1s ease;
+  transition: color 0.1s ease;
+}
+.ui.cards > .card > .content .star.icon:hover,
+.ui.card > .content .star.icon:hover {
+  opacity: 1;
+  color: #FFB70A;
+}
+.ui.cards > .card > .content .active.star.icon,
+.ui.card > .content .active.star.icon {
+  color: #FFE623;
+}
+
+/*-----Like----- */
+
+
+/* Icon */
+.ui.cards > .card > .content .like.icon,
+.ui.card > .content .like.icon {
+  cursor: pointer;
+  opacity: 0.75;
+  -webkit-transition: color 0.1s ease;
+  transition: color 0.1s ease;
+}
+.ui.cards > .card > .content .like.icon:hover,
+.ui.card > .content .like.icon:hover {
+  opacity: 1;
+  color: #FF2733;
+}
+.ui.cards > .card > .content .active.like.icon,
+.ui.card > .content .active.like.icon {
+  color: #FF2733;
+}
+
+/*----------------
+  Extra Content
+-----------------*/
+
+.ui.cards > .card > .extra,
+.ui.card > .extra {
+  max-width: 100%;
+  min-height: 0 !important;
+  -webkit-box-flex: 0;
+      -ms-flex-positive: 0;
+          flex-grow: 0;
+  border-top: 1px solid rgba(0, 0, 0, 0.05) !important;
+  position: static;
+  background: none;
+  width: auto;
+  margin: 0 0;
+  padding: 0.75em 1em;
+  top: 0;
+  left: 0;
+  color: rgba(0, 0, 0, 0.4);
+  -webkit-box-shadow: none;
+          box-shadow: none;
+  -webkit-transition: color 0.1s ease;
+  transition: color 0.1s ease;
+}
+.ui.cards > .card > .extra a:not(.ui),
+.ui.card > .extra a:not(.ui) {
+  color: rgba(0, 0, 0, 0.4);
+}
+.ui.cards > .card > .extra a:not(.ui):hover,
+.ui.card > .extra a:not(.ui):hover {
+  color: #1e70bf;
+}
+
+
+/*******************************
+           Variations
+*******************************/
+
+
+/*-------------------
+      Horizontal
+--------------------*/
+
+.ui.horizontal.cards > .card,
+.ui.card.horizontal {
+  -webkit-box-orient: horizontal;
+  -webkit-box-direction: normal;
+      -ms-flex-direction: row;
+          flex-direction: row;
+  -ms-flex-wrap: wrap;
+      flex-wrap: wrap;
+  min-width: 400px;
+  width: 400px;
+  max-width: 100%;
+}
+.ui.horizontal.cards > .card > .image,
+.ui.card.horizontal > .image {
+  border-radius: 0.28571429rem 0 0 0.28571429rem;
+  width: 150px;
+}
+.ui.horizontal.cards > .card > .image > img,
+.ui.card.horizontal > .image > img {
+  background-size: cover;
+  background-repeat: no-repeat;
+  background-position: center;
+  -webkit-box-pack: center;
+      -ms-flex-pack: center;
+          justify-content: center;
+  -webkit-box-align: center;
+      -ms-flex-align: center;
+          align-items: center;
+  display: -webkit-box;
+  display: -ms-flexbox;
+  display: flex;
+  width: 100%;
+  height: 100%;
+  border-radius: 0.28571429rem 0 0 0.28571429rem;
+}
+.ui.horizontal.cards > .card > .content,
+.ui.horizontal.card > .content {
+  -ms-flex-preferred-size: 1px;
+      flex-basis: 1px;
+}
+.ui.horizontal.cards > .card > .extra,
+.ui.horizontal.card > .extra {
+  -ms-flex-preferred-size: 100%;
+      flex-basis: 100%;
+}
+
+/*-------------------
+       Raised
+--------------------*/
+
+.ui.raised.cards > .card,
+.ui.raised.card {
+  -webkit-box-shadow: 0 0 0 1px #D4D4D5, 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15);
+          box-shadow: 0 0 0 1px #D4D4D5, 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15);
+}
+.ui.raised.cards a.card:hover,
+.ui.link.cards .raised.card:hover,
+a.ui.raised.card:hover,
+.ui.link.raised.card:hover {
+  -webkit-box-shadow: 0 0 0 1px #D4D4D5, 0 2px 4px 0 rgba(34, 36, 38, 0.15), 0 2px 10px 0 rgba(34, 36, 38, 0.25);
+          box-shadow: 0 0 0 1px #D4D4D5, 0 2px 4px 0 rgba(34, 36, 38, 0.15), 0 2px 10px 0 rgba(34, 36, 38, 0.25);
+}
+
+/*-------------------
+       Centered
+--------------------*/
+
+.ui.centered.cards {
+  -webkit-box-pack: center;
+      -ms-flex-pack: center;
+          justify-content: center;
+}
+.ui.centered.card {
+  margin-left: auto;
+  margin-right: auto;
+}
+
+/*-------------------
+        Fluid
+--------------------*/
+
+.ui.fluid.card {
+  width: 100%;
+  max-width: 9999px;
+}
+
+/*-------------------
+        Link
+--------------------*/
+
+.ui.cards a.card,
+.ui.link.cards .card,
+a.ui.card,
+.ui.link.card {
+  -webkit-transform: none;
+          transform: none;
+}
+.ui.cards a.card:hover,
+.ui.link.cards .card:not(.icon):hover,
+a.ui.card:hover,
+.ui.link.card:hover {
+  cursor: pointer;
+  z-index: 5;
+  background: #FFFFFF;
+  border: none;
+  -webkit-box-shadow: 0 1px 3px 0 #BCBDBD, 0 0 0 1px #D4D4D5;
+          box-shadow: 0 1px 3px 0 #BCBDBD, 0 0 0 1px #D4D4D5;
+  -webkit-transform: translateY(-3px);
+          transform: translateY(-3px);
+}
+
+/*-------------------
+       Colors
+--------------------*/
+
+.ui.primary.cards > .card,
+.ui.cards > .primary.card,
+.ui.primary.card {
+  -webkit-box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #2185D0, 0 1px 3px 0 #D4D4D5;
+          box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #2185D0, 0 1px 3px 0 #D4D4D5;
+}
+.ui.primary.cards > .card:hover,
+.ui.cards > .primary.card:hover,
+.ui.primary.card:hover {
+  -webkit-box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #1678c2, 0 1px 3px 0 #BCBDBD;
+          box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #1678c2, 0 1px 3px 0 #BCBDBD;
+}
+.ui.inverted.primary.cards > .card,
+.ui.inverted.cards > .primary.card,
+.ui.inverted.primary.card {
+  -webkit-box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #54C8FF, 0 0 0 1px #555555;
+          box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #54C8FF, 0 0 0 1px #555555;
+}
+.ui.inverted.primary.cards > .card:hover,
+.ui.inverted.cards > .primary.card:hover,
+.ui.inverted.primary.card:hover {
+  -webkit-box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #21b8ff, 0 0 0 1px #555555;
+          box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #21b8ff, 0 0 0 1px #555555;
+}
+.ui.secondary.cards > .card,
+.ui.cards > .secondary.card,
+.ui.secondary.card {
+  -webkit-box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #1B1C1D, 0 1px 3px 0 #D4D4D5;
+          box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #1B1C1D, 0 1px 3px 0 #D4D4D5;
+}
+.ui.secondary.cards > .card:hover,
+.ui.cards > .secondary.card:hover,
+.ui.secondary.card:hover {
+  -webkit-box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #27292a, 0 1px 3px 0 #BCBDBD;
+          box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #27292a, 0 1px 3px 0 #BCBDBD;
+}
+.ui.inverted.secondary.cards > .card,
+.ui.inverted.cards > .secondary.card,
+.ui.inverted.secondary.card {
+  -webkit-box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #545454, 0 0 0 1px #555555;
+          box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #545454, 0 0 0 1px #555555;
+}
+.ui.inverted.secondary.cards > .card:hover,
+.ui.inverted.cards > .secondary.card:hover,
+.ui.inverted.secondary.card:hover {
+  -webkit-box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #6e6e6e, 0 0 0 1px #555555;
+          box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #6e6e6e, 0 0 0 1px #555555;
+}
+.ui.red.cards > .card,
+.ui.cards > .red.card,
+.ui.red.card {
+  -webkit-box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #DB2828, 0 1px 3px 0 #D4D4D5;
+          box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #DB2828, 0 1px 3px 0 #D4D4D5;
+}
+.ui.red.cards > .card:hover,
+.ui.cards > .red.card:hover,
+.ui.red.card:hover {
+  -webkit-box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #d01919, 0 1px 3px 0 #BCBDBD;
+          box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #d01919, 0 1px 3px 0 #BCBDBD;
+}
+.ui.inverted.red.cards > .card,
+.ui.inverted.cards > .red.card,
+.ui.inverted.red.card {
+  -webkit-box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #FF695E, 0 0 0 1px #555555;
+          box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #FF695E, 0 0 0 1px #555555;
+}
+.ui.inverted.red.cards > .card:hover,
+.ui.inverted.cards > .red.card:hover,
+.ui.inverted.red.card:hover {
+  -webkit-box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #ff392b, 0 0 0 1px #555555;
+          box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #ff392b, 0 0 0 1px #555555;
+}
+.ui.orange.cards > .card,
+.ui.cards > .orange.card,
+.ui.orange.card {
+  -webkit-box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #F2711C, 0 1px 3px 0 #D4D4D5;
+          box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #F2711C, 0 1px 3px 0 #D4D4D5;
+}
+.ui.orange.cards > .card:hover,
+.ui.cards > .orange.card:hover,
+.ui.orange.card:hover {
+  -webkit-box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #f26202, 0 1px 3px 0 #BCBDBD;
+          box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #f26202, 0 1px 3px 0 #BCBDBD;
+}
+.ui.inverted.orange.cards > .card,
+.ui.inverted.cards > .orange.card,
+.ui.inverted.orange.card {
+  -webkit-box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #FF851B, 0 0 0 1px #555555;
+          box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #FF851B, 0 0 0 1px #555555;
+}
+.ui.inverted.orange.cards > .card:hover,
+.ui.inverted.cards > .orange.card:hover,
+.ui.inverted.orange.card:hover {
+  -webkit-box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #e76b00, 0 0 0 1px #555555;
+          box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #e76b00, 0 0 0 1px #555555;
+}
+.ui.yellow.cards > .card,
+.ui.cards > .yellow.card,
+.ui.yellow.card {
+  -webkit-box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #FBBD08, 0 1px 3px 0 #D4D4D5;
+          box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #FBBD08, 0 1px 3px 0 #D4D4D5;
+}
+.ui.yellow.cards > .card:hover,
+.ui.cards > .yellow.card:hover,
+.ui.yellow.card:hover {
+  -webkit-box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #eaae00, 0 1px 3px 0 #BCBDBD;
+          box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #eaae00, 0 1px 3px 0 #BCBDBD;
+}
+.ui.inverted.yellow.cards > .card,
+.ui.inverted.cards > .yellow.card,
+.ui.inverted.yellow.card {
+  -webkit-box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #FFE21F, 0 0 0 1px #555555;
+          box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #FFE21F, 0 0 0 1px #555555;
+}
+.ui.inverted.yellow.cards > .card:hover,
+.ui.inverted.cards > .yellow.card:hover,
+.ui.inverted.yellow.card:hover {
+  -webkit-box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #ebcd00, 0 0 0 1px #555555;
+          box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #ebcd00, 0 0 0 1px #555555;
+}
+.ui.olive.cards > .card,
+.ui.cards > .olive.card,
+.ui.olive.card {
+  -webkit-box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #B5CC18, 0 1px 3px 0 #D4D4D5;
+          box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #B5CC18, 0 1px 3px 0 #D4D4D5;
+}
+.ui.olive.cards > .card:hover,
+.ui.cards > .olive.card:hover,
+.ui.olive.card:hover {
+  -webkit-box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #a7bd0d, 0 1px 3px 0 #BCBDBD;
+          box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #a7bd0d, 0 1px 3px 0 #BCBDBD;
+}
+.ui.inverted.olive.cards > .card,
+.ui.inverted.cards > .olive.card,
+.ui.inverted.olive.card {
+  -webkit-box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #D9E778, 0 0 0 1px #555555;
+          box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #D9E778, 0 0 0 1px #555555;
+}
+.ui.inverted.olive.cards > .card:hover,
+.ui.inverted.cards > .olive.card:hover,
+.ui.inverted.olive.card:hover {
+  -webkit-box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #d2e745, 0 0 0 1px #555555;
+          box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #d2e745, 0 0 0 1px #555555;
+}
+.ui.green.cards > .card,
+.ui.cards > .green.card,
+.ui.green.card {
+  -webkit-box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #21BA45, 0 1px 3px 0 #D4D4D5;
+          box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #21BA45, 0 1px 3px 0 #D4D4D5;
+}
+.ui.green.cards > .card:hover,
+.ui.cards > .green.card:hover,
+.ui.green.card:hover {
+  -webkit-box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #16ab39, 0 1px 3px 0 #BCBDBD;
+          box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #16ab39, 0 1px 3px 0 #BCBDBD;
+}
+.ui.inverted.green.cards > .card,
+.ui.inverted.cards > .green.card,
+.ui.inverted.green.card {
+  -webkit-box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #2ECC40, 0 0 0 1px #555555;
+          box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #2ECC40, 0 0 0 1px #555555;
+}
+.ui.inverted.green.cards > .card:hover,
+.ui.inverted.cards > .green.card:hover,
+.ui.inverted.green.card:hover {
+  -webkit-box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #1ea92e, 0 0 0 1px #555555;
+          box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #1ea92e, 0 0 0 1px #555555;
+}
+.ui.teal.cards > .card,
+.ui.cards > .teal.card,
+.ui.teal.card {
+  -webkit-box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #00B5AD, 0 1px 3px 0 #D4D4D5;
+          box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #00B5AD, 0 1px 3px 0 #D4D4D5;
+}
+.ui.teal.cards > .card:hover,
+.ui.cards > .teal.card:hover,
+.ui.teal.card:hover {
+  -webkit-box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #009c95, 0 1px 3px 0 #BCBDBD;
+          box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #009c95, 0 1px 3px 0 #BCBDBD;
+}
+.ui.inverted.teal.cards > .card,
+.ui.inverted.cards > .teal.card,
+.ui.inverted.teal.card {
+  -webkit-box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #6DFFFF, 0 0 0 1px #555555;
+          box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #6DFFFF, 0 0 0 1px #555555;
+}
+.ui.inverted.teal.cards > .card:hover,
+.ui.inverted.cards > .teal.card:hover,
+.ui.inverted.teal.card:hover {
+  -webkit-box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #3affff, 0 0 0 1px #555555;
+          box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #3affff, 0 0 0 1px #555555;
+}
+.ui.blue.cards > .card,
+.ui.cards > .blue.card,
+.ui.blue.card {
+  -webkit-box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #2185D0, 0 1px 3px 0 #D4D4D5;
+          box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #2185D0, 0 1px 3px 0 #D4D4D5;
+}
+.ui.blue.cards > .card:hover,
+.ui.cards > .blue.card:hover,
+.ui.blue.card:hover {
+  -webkit-box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #1678c2, 0 1px 3px 0 #BCBDBD;
+          box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #1678c2, 0 1px 3px 0 #BCBDBD;
+}
+.ui.inverted.blue.cards > .card,
+.ui.inverted.cards > .blue.card,
+.ui.inverted.blue.card {
+  -webkit-box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #54C8FF, 0 0 0 1px #555555;
+          box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #54C8FF, 0 0 0 1px #555555;
+}
+.ui.inverted.blue.cards > .card:hover,
+.ui.inverted.cards > .blue.card:hover,
+.ui.inverted.blue.card:hover {
+  -webkit-box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #21b8ff, 0 0 0 1px #555555;
+          box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #21b8ff, 0 0 0 1px #555555;
+}
+.ui.violet.cards > .card,
+.ui.cards > .violet.card,
+.ui.violet.card {
+  -webkit-box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #6435C9, 0 1px 3px 0 #D4D4D5;
+          box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #6435C9, 0 1px 3px 0 #D4D4D5;
+}
+.ui.violet.cards > .card:hover,
+.ui.cards > .violet.card:hover,
+.ui.violet.card:hover {
+  -webkit-box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #5829bb, 0 1px 3px 0 #BCBDBD;
+          box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #5829bb, 0 1px 3px 0 #BCBDBD;
+}
+.ui.inverted.violet.cards > .card,
+.ui.inverted.cards > .violet.card,
+.ui.inverted.violet.card {
+  -webkit-box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #A291FB, 0 0 0 1px #555555;
+          box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #A291FB, 0 0 0 1px #555555;
+}
+.ui.inverted.violet.cards > .card:hover,
+.ui.inverted.cards > .violet.card:hover,
+.ui.inverted.violet.card:hover {
+  -webkit-box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #745aff, 0 0 0 1px #555555;
+          box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #745aff, 0 0 0 1px #555555;
+}
+.ui.purple.cards > .card,
+.ui.cards > .purple.card,
+.ui.purple.card {
+  -webkit-box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #A333C8, 0 1px 3px 0 #D4D4D5;
+          box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #A333C8, 0 1px 3px 0 #D4D4D5;
+}
+.ui.purple.cards > .card:hover,
+.ui.cards > .purple.card:hover,
+.ui.purple.card:hover {
+  -webkit-box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #9627ba, 0 1px 3px 0 #BCBDBD;
+          box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #9627ba, 0 1px 3px 0 #BCBDBD;
+}
+.ui.inverted.purple.cards > .card,
+.ui.inverted.cards > .purple.card,
+.ui.inverted.purple.card {
+  -webkit-box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #DC73FF, 0 0 0 1px #555555;
+          box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #DC73FF, 0 0 0 1px #555555;
+}
+.ui.inverted.purple.cards > .card:hover,
+.ui.inverted.cards > .purple.card:hover,
+.ui.inverted.purple.card:hover {
+  -webkit-box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #cf40ff, 0 0 0 1px #555555;
+          box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #cf40ff, 0 0 0 1px #555555;
+}
+.ui.pink.cards > .card,
+.ui.cards > .pink.card,
+.ui.pink.card {
+  -webkit-box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #E03997, 0 1px 3px 0 #D4D4D5;
+          box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #E03997, 0 1px 3px 0 #D4D4D5;
+}
+.ui.pink.cards > .card:hover,
+.ui.cards > .pink.card:hover,
+.ui.pink.card:hover {
+  -webkit-box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #e61a8d, 0 1px 3px 0 #BCBDBD;
+          box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #e61a8d, 0 1px 3px 0 #BCBDBD;
+}
+.ui.inverted.pink.cards > .card,
+.ui.inverted.cards > .pink.card,
+.ui.inverted.pink.card {
+  -webkit-box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #FF8EDF, 0 0 0 1px #555555;
+          box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #FF8EDF, 0 0 0 1px #555555;
+}
+.ui.inverted.pink.cards > .card:hover,
+.ui.inverted.cards > .pink.card:hover,
+.ui.inverted.pink.card:hover {
+  -webkit-box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #ff5bd1, 0 0 0 1px #555555;
+          box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #ff5bd1, 0 0 0 1px #555555;
+}
+.ui.brown.cards > .card,
+.ui.cards > .brown.card,
+.ui.brown.card {
+  -webkit-box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #A5673F, 0 1px 3px 0 #D4D4D5;
+          box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #A5673F, 0 1px 3px 0 #D4D4D5;
+}
+.ui.brown.cards > .card:hover,
+.ui.cards > .brown.card:hover,
+.ui.brown.card:hover {
+  -webkit-box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #975b33, 0 1px 3px 0 #BCBDBD;
+          box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #975b33, 0 1px 3px 0 #BCBDBD;
+}
+.ui.inverted.brown.cards > .card,
+.ui.inverted.cards > .brown.card,
+.ui.inverted.brown.card {
+  -webkit-box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #D67C1C, 0 0 0 1px #555555;
+          box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #D67C1C, 0 0 0 1px #555555;
+}
+.ui.inverted.brown.cards > .card:hover,
+.ui.inverted.cards > .brown.card:hover,
+.ui.inverted.brown.card:hover {
+  -webkit-box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #b0620f, 0 0 0 1px #555555;
+          box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #b0620f, 0 0 0 1px #555555;
+}
+.ui.grey.cards > .card,
+.ui.cards > .grey.card,
+.ui.grey.card {
+  -webkit-box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #767676, 0 1px 3px 0 #D4D4D5;
+          box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #767676, 0 1px 3px 0 #D4D4D5;
+}
+.ui.grey.cards > .card:hover,
+.ui.cards > .grey.card:hover,
+.ui.grey.card:hover {
+  -webkit-box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #838383, 0 1px 3px 0 #BCBDBD;
+          box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #838383, 0 1px 3px 0 #BCBDBD;
+}
+.ui.inverted.grey.cards > .card,
+.ui.inverted.cards > .grey.card,
+.ui.inverted.grey.card {
+  -webkit-box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #DCDDDE, 0 0 0 1px #555555;
+          box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #DCDDDE, 0 0 0 1px #555555;
+}
+.ui.inverted.grey.cards > .card:hover,
+.ui.inverted.cards > .grey.card:hover,
+.ui.inverted.grey.card:hover {
+  -webkit-box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #c2c4c5, 0 0 0 1px #555555;
+          box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #c2c4c5, 0 0 0 1px #555555;
+}
+.ui.black.cards > .card,
+.ui.cards > .black.card,
+.ui.black.card {
+  -webkit-box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #1B1C1D, 0 1px 3px 0 #D4D4D5;
+          box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #1B1C1D, 0 1px 3px 0 #D4D4D5;
+}
+.ui.black.cards > .card:hover,
+.ui.cards > .black.card:hover,
+.ui.black.card:hover {
+  -webkit-box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #27292a, 0 1px 3px 0 #BCBDBD;
+          box-shadow: 0 0 0 1px #D4D4D5, 0 2px 0 0 #27292a, 0 1px 3px 0 #BCBDBD;
+}
+.ui.inverted.black.cards > .card,
+.ui.inverted.cards > .black.card,
+.ui.inverted.black.card {
+  -webkit-box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #545454, 0 0 0 1px #555555;
+          box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #545454, 0 0 0 1px #555555;
+}
+.ui.inverted.black.cards > .card:hover,
+.ui.inverted.cards > .black.card:hover,
+.ui.inverted.black.card:hover {
+  -webkit-box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #000000, 0 0 0 1px #555555;
+          box-shadow: 0 1px 3px 0 #555555, 0 2px 0 0 #000000, 0 0 0 1px #555555;
+}
+
+/*--------------
+   Card Count
+---------------*/
+
+.ui.one.cards {
+  margin-left: 0;
+  margin-right: 0;
+}
+.ui.one.cards > .card {
+  width: 100%;
+}
+.ui.two.cards {
+  margin-left: -1em;
+  margin-right: -1em;
+}
+.ui.two.cards > .card {
+  width: calc(50% - 2em);
+  margin-left: 1em;
+  margin-right: 1em;
+}
+.ui.three.cards {
+  margin-left: -1em;
+  margin-right: -1em;
+}
+.ui.three.cards > .card {
+  width: calc(33.33333333333333% - 2em);
+  margin-left: 1em;
+  margin-right: 1em;
+}
+.ui.four.cards {
+  margin-left: -0.75em;
+  margin-right: -0.75em;
+}
+.ui.four.cards > .card {
+  width: calc(25% - 1.5em);
+  margin-left: 0.75em;
+  margin-right: 0.75em;
+}
+.ui.five.cards {
+  margin-left: -0.75em;
+  margin-right: -0.75em;
+}
+.ui.five.cards > .card {
+  width: calc(20% - 1.5em);
+  margin-left: 0.75em;
+  margin-right: 0.75em;
+}
+.ui.six.cards {
+  margin-left: -0.75em;
+  margin-right: -0.75em;
+}
+.ui.six.cards > .card {
+  width: calc(16.666666666666664% - 1.5em);
+  margin-left: 0.75em;
+  margin-right: 0.75em;
+}
+.ui.seven.cards {
+  margin-left: -0.5em;
+  margin-right: -0.5em;
+}
+.ui.seven.cards > .card {
+  width: calc(14.285714285714285% - 1em);
+  margin-left: 0.5em;
+  margin-right: 0.5em;
+}
+.ui.eight.cards {
+  margin-left: -0.5em;
+  margin-right: -0.5em;
+}
+.ui.eight.cards > .card {
+  width: calc(12.5% - 1em);
+  margin-left: 0.5em;
+  margin-right: 0.5em;
+  font-size: 11px;
+}
+.ui.nine.cards {
+  margin-left: -0.5em;
+  margin-right: -0.5em;
+}
+.ui.nine.cards > .card {
+  width: calc(11.11111111111111% - 1em);
+  margin-left: 0.5em;
+  margin-right: 0.5em;
+  font-size: 10px;
+}
+.ui.ten.cards {
+  margin-left: -0.5em;
+  margin-right: -0.5em;
+}
+.ui.ten.cards > .card {
+  width: calc(10% - 1em);
+  margin-left: 0.5em;
+  margin-right: 0.5em;
+}
+
+/*-------------------
+      Doubling
+--------------------*/
+
+
+/* Mobile Only */
+@media only screen and (max-width: 767.98px) {
+  .ui.two.doubling.cards {
+    margin-left: 0;
+    margin-right: 0;
+  }
+  .ui.two.doubling.cards > .card {
+    width: 100%;
+    margin-left: 0;
+    margin-right: 0;
+  }
+  .ui.three.doubling.cards {
+    margin-left: -1em;
+    margin-right: -1em;
+  }
+  .ui.three.doubling.cards > .card {
+    width: calc(50% - 2em);
+    margin-left: 1em;
+    margin-right: 1em;
+  }
+  .ui.four.doubling.cards {
+    margin-left: -1em;
+    margin-right: -1em;
+  }
+  .ui.four.doubling.cards > .card {
+    width: calc(50% - 2em);
+    margin-left: 1em;
+    margin-right: 1em;
+  }
+  .ui.five.doubling.cards {
+    margin-left: -1em;
+    margin-right: -1em;
+  }
+  .ui.five.doubling.cards > .card {
+    width: calc(50% - 2em);
+    margin-left: 1em;
+    margin-right: 1em;
+  }
+  .ui.six.doubling.cards {
+    margin-left: -1em;
+    margin-right: -1em;
+  }
+  .ui.six.doubling.cards > .card {
+    width: calc(50% - 2em);
+    margin-left: 1em;
+    margin-right: 1em;
+  }
+  .ui.seven.doubling.cards {
+    margin-left: -1em;
+    margin-right: -1em;
+  }
+  .ui.seven.doubling.cards > .card {
+    width: calc(33.33333333333333% - 2em);
+    margin-left: 1em;
+    margin-right: 1em;
+  }
+  .ui.eight.doubling.cards {
+    margin-left: -1em;
+    margin-right: -1em;
+  }
+  .ui.eight.doubling.cards > .card {
+    width: calc(33.33333333333333% - 2em);
+    margin-left: 1em;
+    margin-right: 1em;
+  }
+  .ui.nine.doubling.cards {
+    margin-left: -1em;
+    margin-right: -1em;
+  }
+  .ui.nine.doubling.cards > .card {
+    width: calc(33.33333333333333% - 2em);
+    margin-left: 1em;
+    margin-right: 1em;
+  }
+  .ui.ten.doubling.cards {
+    margin-left: -1em;
+    margin-right: -1em;
+  }
+  .ui.ten.doubling.cards > .card {
+    width: calc(33.33333333333333% - 2em);
+    margin-left: 1em;
+    margin-right: 1em;
+  }
+}
+
+/* Tablet Only */
+@media only screen and (min-width: 768px) and (max-width: 991.98px) {
+  .ui.two.doubling.cards {
+    margin-left: 0;
+    margin-right: 0;
+  }
+  .ui.two.doubling.cards > .card {
+    width: 100%;
+    margin-left: 0;
+    margin-right: 0;
+  }
+  .ui.three.doubling.cards {
+    margin-left: -1em;
+    margin-right: -1em;
+  }
+  .ui.three.doubling.cards > .card {
+    width: calc(50% - 2em);
+    margin-left: 1em;
+    margin-right: 1em;
+  }
+  .ui.four.doubling.cards {
+    margin-left: -1em;
+    margin-right: -1em;
+  }
+  .ui.four.doubling.cards > .card {
+    width: calc(50% - 2em);
+    margin-left: 1em;
+    margin-right: 1em;
+  }
+  .ui.five.doubling.cards {
+    margin-left: -1em;
+    margin-right: -1em;
+  }
+  .ui.five.doubling.cards > .card {
+    width: calc(33.33333333333333% - 2em);
+    margin-left: 1em;
+    margin-right: 1em;
+  }
+  .ui.six.doubling.cards {
+    margin-left: -1em;
+    margin-right: -1em;
+  }
+  .ui.six.doubling.cards > .card {
+    width: calc(33.33333333333333% - 2em);
+    margin-left: 1em;
+    margin-right: 1em;
+  }
+  .ui.eight.doubling.cards {
+    margin-left: -1em;
+    margin-right: -1em;
+  }
+  .ui.eight.doubling.cards > .card {
+    width: calc(33.33333333333333% - 2em);
+    margin-left: 1em;
+    margin-right: 1em;
+  }
+  .ui.eight.doubling.cards {
+    margin-left: -0.75em;
+    margin-right: -0.75em;
+  }
+  .ui.eight.doubling.cards > .card {
+    width: calc(25% - 1.5em);
+    margin-left: 0.75em;
+    margin-right: 0.75em;
+  }
+  .ui.nine.doubling.cards {
+    margin-left: -0.75em;
+    margin-right: -0.75em;
+  }
+  .ui.nine.doubling.cards > .card {
+    width: calc(25% - 1.5em);
+    margin-left: 0.75em;
+    margin-right: 0.75em;
+  }
+  .ui.ten.doubling.cards {
+    margin-left: -0.75em;
+    margin-right: -0.75em;
+  }
+  .ui.ten.doubling.cards > .card {
+    width: calc(20% - 1.5em);
+    margin-left: 0.75em;
+    margin-right: 0.75em;
+  }
+}
+
+/*-------------------
+      Stackable
+--------------------*/
+
+@media only screen and (max-width: 767.98px) {
+  .ui.stackable.cards {
+    display: block !important;
+  }
+  .ui.stackable.cards .card:first-child {
+    margin-top: 0 !important;
+  }
+  .ui.stackable.cards > .card {
+    display: block !important;
+    height: auto !important;
+    margin: 1em 1em;
+    padding: 0 !important;
+    width: calc(100% - 2em) !important;
+  }
+}
+
+/*--------------
+      Size
+---------------*/
+
+.ui.cards > .card {
+  font-size: 1em;
+}
+
+/*-----------------
+      Inverted
+------------------*/
+
+.ui.inverted.cards > .card,
+.ui.inverted.card {
+  background: #1B1C1D;
+  -webkit-box-shadow: 0 1px 3px 0 #555555, 0 0 0 1px #555555;
+          box-shadow: 0 1px 3px 0 #555555, 0 0 0 1px #555555;
+}
+
+/* Content */
+.ui.inverted.cards > .card > .content,
+.ui.inverted.card > .content {
+  border-top: 1px solid rgba(255, 255, 255, 0.15);
+}
+
+/* Header */
+.ui.inverted.cards > .card > .content > .header,
+.ui.inverted.card > .content > .header {
+  color: rgba(255, 255, 255, 0.9);
+}
+
+/* Description */
+.ui.inverted.cards > .card > .content > .description,
+.ui.inverted.card > .content > .description {
+  color: rgba(255, 255, 255, 0.8);
+}
+
+/* Meta */
+.ui.inverted.cards > .card .meta,
+.ui.inverted.card .meta {
+  color: rgba(255, 255, 255, 0.7);
+}
+.ui.inverted.cards > .card .meta > a:not(.ui),
+.ui.inverted.card .meta > a:not(.ui) {
+  color: rgba(255, 255, 255, 0.7);
+}
+.ui.inverted.cards > .card .meta > a:not(.ui):hover,
+.ui.inverted.card .meta > a:not(.ui):hover {
+  color: #ffffff;
+}
+
+/* Extra */
+.ui.inverted.cards > .card > .extra,
+.ui.inverted.card > .extra {
+  border-top: 1px solid rgba(255, 255, 255, 0.15) !important;
+  color: rgba(255, 255, 255, 0.7);
+}
+.ui.inverted.cards > .card > .extra a:not(.ui),
+.ui.inverted.card > .extra a:not(.ui) {
+  color: rgba(255, 255, 255, 0.5);
+}
+.ui.inverted.cards > .card > .extra a:not(.ui):hover,
+.ui.inverted.card > .extra a:not(.ui):hover {
+  color: #1e70bf;
+}
+
+/* Link card(s) */
+.ui.inverted.cards a.card:hover,
+.ui.inverted.link.cards .card:not(.icon):hover,
+a.inverted.ui.card:hover,
+.ui.inverted.link.card:hover {
+  background: #1B1C1D;
+}
+
+
+/*******************************
+         Theme Overrides
+*******************************/
+
+
+
+/*******************************
+    User Variable Overrides
+*******************************/
+

Разлика између датотеке није приказан због своје велике величине
+ 8 - 0
website/semantic/dist/components/card.min.css


+ 878 - 0
website/semantic/dist/components/checkbox.css

@@ -0,0 +1,878 @@
+/*!
+ * # Fomantic-UI - Checkbox
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */
+
+
+/*******************************
+           Checkbox
+*******************************/
+
+
+/*--------------
+    Content
+---------------*/
+
+.ui.checkbox {
+  position: relative;
+  display: inline-block;
+  -webkit-backface-visibility: hidden;
+          backface-visibility: hidden;
+  outline: none;
+  vertical-align: baseline;
+  font-style: normal;
+  min-height: 17px;
+  font-size: 1em;
+  line-height: 17px;
+  min-width: 17px;
+}
+
+/* HTML Checkbox */
+.ui.checkbox input[type="checkbox"],
+.ui.checkbox input[type="radio"] {
+  cursor: pointer;
+  position: absolute;
+  top: 0;
+  left: 0;
+  opacity: 0 !important;
+  outline: none;
+  z-index: 3;
+  width: 17px;
+  height: 17px;
+}
+
+/*--------------
+      Box
+---------------*/
+
+.ui.checkbox .box,
+.ui.checkbox label {
+  cursor: auto;
+  position: relative;
+  display: block;
+  padding-left: 1.85714em;
+  outline: none;
+  font-size: 1em;
+}
+.ui.checkbox .box:before,
+.ui.checkbox label:before {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 17px;
+  height: 17px;
+  content: '';
+  background: #FFFFFF;
+  border-radius: 0.21428571rem;
+  -webkit-transition: border 0.1s ease, opacity 0.1s ease, -webkit-transform 0.1s ease, -webkit-box-shadow 0.1s ease;
+  transition: border 0.1s ease, opacity 0.1s ease, -webkit-transform 0.1s ease, -webkit-box-shadow 0.1s ease;
+  transition: border 0.1s ease, opacity 0.1s ease, transform 0.1s ease, box-shadow 0.1s ease;
+  transition: border 0.1s ease, opacity 0.1s ease, transform 0.1s ease, box-shadow 0.1s ease, -webkit-transform 0.1s ease, -webkit-box-shadow 0.1s ease;
+  border: 1px solid #D4D4D5;
+}
+
+/*--------------
+    Checkmark
+---------------*/
+
+.ui.checkbox .box:after,
+.ui.checkbox label:after {
+  position: absolute;
+  font-size: 14px;
+  top: 0;
+  left: 0;
+  width: 17px;
+  height: 17px;
+  text-align: center;
+  opacity: 0;
+  color: #AAAAAA;
+  -webkit-transition: border 0.1s ease, opacity 0.1s ease, -webkit-transform 0.1s ease, -webkit-box-shadow 0.1s ease;
+  transition: border 0.1s ease, opacity 0.1s ease, -webkit-transform 0.1s ease, -webkit-box-shadow 0.1s ease;
+  transition: border 0.1s ease, opacity 0.1s ease, transform 0.1s ease, box-shadow 0.1s ease;
+  transition: border 0.1s ease, opacity 0.1s ease, transform 0.1s ease, box-shadow 0.1s ease, -webkit-transform 0.1s ease, -webkit-box-shadow 0.1s ease;
+}
+
+/*--------------
+      Label
+---------------*/
+
+
+/* Inside */
+.ui.checkbox label,
+.ui.checkbox + label {
+  color: #AAAAAA;
+  -webkit-transition: color 0.1s ease;
+  transition: color 0.1s ease;
+}
+
+/* Outside */
+.ui.checkbox + label {
+  vertical-align: middle;
+}
+
+
+/*******************************
+           States
+*******************************/
+
+
+/*--------------
+      Hover
+---------------*/
+
+.ui.checkbox .box:hover::before,
+.ui.checkbox label:hover::before {
+  background: #FFFFFF;
+  border-color: rgba(34, 36, 38, 0.35);
+}
+.ui.checkbox label:hover,
+.ui.checkbox + label:hover {
+  color: rgba(0, 0, 0, 0.8);
+}
+
+/*--------------
+      Down
+---------------*/
+
+.ui.checkbox .box:active::before,
+.ui.checkbox label:active::before {
+  background: #F9FAFB;
+  border-color: rgba(34, 36, 38, 0.35);
+}
+.ui.checkbox .box:active::after,
+.ui.checkbox label:active::after {
+  color: rgba(0, 0, 0, 0.95);
+}
+.ui.checkbox input:active ~ label {
+  color: rgba(0, 0, 0, 0.95);
+}
+
+/*--------------
+     Focus
+---------------*/
+
+.ui.checkbox input:focus ~ .box:before,
+.ui.checkbox input:focus ~ label:before {
+  background: #FFFFFF;
+  border-color: #96C8DA;
+}
+.ui.checkbox input:focus ~ .box:after,
+.ui.checkbox input:focus ~ label:after {
+  color: rgba(0, 0, 0, 0.95);
+}
+.ui.checkbox input:focus ~ label {
+  color: rgba(0, 0, 0, 0.95);
+}
+
+/*--------------
+     Active
+---------------*/
+
+.ui.checkbox input:checked ~ .box:before,
+.ui.checkbox input:checked ~ label:before {
+  background: #FFFFFF;
+  border-color: rgba(34, 36, 38, 0.35);
+}
+.ui.checkbox input:checked ~ .box:after,
+.ui.checkbox input:checked ~ label:after {
+  opacity: 1;
+  color: rgba(0, 0, 0, 0.95);
+}
+
+/*--------------
+  Indeterminate
+---------------*/
+
+.ui.checkbox input:not([type=radio]):indeterminate ~ .box:before,
+.ui.checkbox input:not([type=radio]):indeterminate ~ label:before {
+  background: #FFFFFF;
+  border-color: rgba(34, 36, 38, 0.35);
+}
+.ui.checkbox input:not([type=radio]):indeterminate ~ .box:after,
+.ui.checkbox input:not([type=radio]):indeterminate ~ label:after {
+  opacity: 1;
+  color: rgba(0, 0, 0, 0.95);
+}
+.ui.indeterminate.toggle.checkbox input:not([type=radio]):indeterminate ~ label:before {
+  background: rgba(0, 0, 0, 0.15);
+}
+.ui.indeterminate.toggle.checkbox input:not([type=radio]) ~ label:after {
+  left: 1.075rem;
+}
+
+/*--------------
+  Active Focus
+---------------*/
+
+.ui.checkbox input:not([type=radio]):indeterminate:focus ~ .box:before,
+.ui.checkbox input:not([type=radio]):indeterminate:focus ~ label:before,
+.ui.checkbox input:checked:focus ~ .box:before,
+.ui.checkbox input:checked:focus ~ label:before {
+  background: #FFFFFF;
+  border-color: #96C8DA;
+}
+.ui.checkbox input:not([type=radio]):indeterminate:focus ~ .box:after,
+.ui.checkbox input:not([type=radio]):indeterminate:focus ~ label:after,
+.ui.checkbox input:checked:focus ~ .box:after,
+.ui.checkbox input:checked:focus ~ label:after {
+  color: rgba(0, 0, 0, 0.95);
+}
+
+/*--------------
+    Read-Only
+---------------*/
+
+.ui.read-only.checkbox,
+.ui.read-only.checkbox label {
+  cursor: default;
+}
+
+/*--------------
+     Disabled
+---------------*/
+
+.ui.disabled.checkbox .box:after,
+.ui.disabled.checkbox label,
+.ui.checkbox input[disabled] ~ .box:after,
+.ui.checkbox input[disabled] ~ label {
+  cursor: default !important;
+  opacity: 0.5;
+  color: #000000;
+}
+
+/*--------------
+     Hidden
+---------------*/
+
+
+/* Initialized checkbox moves input below element
+ to prevent manually triggering */
+.ui.checkbox input.hidden {
+  z-index: -1;
+}
+
+/* Selectable Label */
+.ui.checkbox input.hidden + label {
+  cursor: pointer;
+  -webkit-user-select: none;
+     -moz-user-select: none;
+      -ms-user-select: none;
+          user-select: none;
+}
+
+
+/*******************************
+             Types
+*******************************/
+
+
+/*--------------
+     Radio
+---------------*/
+
+.ui.radio.checkbox {
+  min-height: 15px;
+}
+.ui.radio.checkbox .box,
+.ui.radio.checkbox label {
+  padding-left: 1.85714em;
+}
+
+/* Box */
+.ui.radio.checkbox .box:before,
+.ui.radio.checkbox label:before {
+  content: '';
+  -webkit-transform: none;
+          transform: none;
+  width: 15px;
+  height: 15px;
+  border-radius: 500rem;
+  top: 1px;
+  left: 0;
+}
+
+/* Bullet */
+.ui.radio.checkbox .box:after,
+.ui.radio.checkbox label:after {
+  border: none;
+  content: '' !important;
+  width: 15px;
+  height: 15px;
+  line-height: 15px;
+}
+
+/* Radio Checkbox */
+.ui.radio.checkbox .box:after,
+.ui.radio.checkbox label:after {
+  top: 1px;
+  left: 0;
+  width: 15px;
+  height: 15px;
+  border-radius: 500rem;
+  -webkit-transform: scale(0.46666667);
+          transform: scale(0.46666667);
+  background-color: #AAAAAA;
+}
+
+/* Focus */
+.ui.radio.checkbox input:focus ~ .box:before,
+.ui.radio.checkbox input:focus ~ label:before {
+  background-color: #FFFFFF;
+}
+.ui.radio.checkbox input:focus ~ .box:after,
+.ui.radio.checkbox input:focus ~ label:after {
+  background-color: rgba(0, 0, 0, 0.95);
+}
+
+/* Indeterminate */
+.ui.radio.checkbox input:indeterminate ~ .box:after,
+.ui.radio.checkbox input:indeterminate ~ label:after {
+  opacity: 0;
+}
+
+/* Active */
+.ui.radio.checkbox input:checked ~ .box:before,
+.ui.radio.checkbox input:checked ~ label:before {
+  background-color: #FFFFFF;
+}
+.ui.radio.checkbox input:checked ~ .box:after,
+.ui.radio.checkbox input:checked ~ label:after {
+  background-color: rgba(0, 0, 0, 0.95);
+}
+
+/* Active Focus */
+.ui.radio.checkbox input:focus:checked ~ .box:before,
+.ui.radio.checkbox input:focus:checked ~ label:before {
+  background-color: #FFFFFF;
+}
+.ui.radio.checkbox input:focus:checked ~ .box:after,
+.ui.radio.checkbox input:focus:checked ~ label:after {
+  background-color: rgba(0, 0, 0, 0.95);
+}
+
+/*--------------
+     Slider
+---------------*/
+
+.ui.slider.checkbox {
+  min-height: 1.25rem;
+}
+
+/* Input */
+.ui.slider.checkbox input {
+  width: 3.5rem;
+  height: 1.25rem;
+}
+
+/* Label */
+.ui.slider.checkbox .box,
+.ui.slider.checkbox label {
+  padding-left: 4.5rem;
+  line-height: 1rem;
+  color: rgba(0, 0, 0, 0.4);
+}
+
+/* Line */
+.ui.slider.checkbox .box:before,
+.ui.slider.checkbox label:before {
+  display: block;
+  position: absolute;
+  content: '';
+  -webkit-transform: none;
+          transform: none;
+  border: none !important;
+  left: 0;
+  z-index: 1;
+  top: 0.4rem;
+  background-color: rgba(0, 0, 0, 0.05);
+  width: 3.5rem;
+  height: 0.21428571rem;
+  border-radius: 500rem;
+  -webkit-transition: background 0.3s ease;
+  transition: background 0.3s ease;
+}
+
+/* Handle */
+.ui.slider.checkbox .box:after,
+.ui.slider.checkbox label:after {
+  background: #FFFFFF -webkit-gradient(linear, left top, left bottom, from(transparent), to(rgba(0, 0, 0, 0.05)));
+  background: #FFFFFF -webkit-linear-gradient(transparent, rgba(0, 0, 0, 0.05));
+  background: #FFFFFF linear-gradient(transparent, rgba(0, 0, 0, 0.05));
+  position: absolute;
+  content: '' !important;
+  opacity: 1;
+  z-index: 2;
+  border: none;
+  -webkit-box-shadow: 0 1px 2px 0 rgba(34, 36, 38, 0.15), 0 0 0 1px rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 1px 2px 0 rgba(34, 36, 38, 0.15), 0 0 0 1px rgba(34, 36, 38, 0.15) inset;
+  width: 1.5rem;
+  height: 1.5rem;
+  top: -0.25rem;
+  left: 0;
+  -webkit-transform: none;
+          transform: none;
+  border-radius: 500rem;
+  -webkit-transition: left 0.3s ease;
+  transition: left 0.3s ease;
+}
+
+/* Focus */
+.ui.slider.checkbox input:focus ~ .box:before,
+.ui.slider.checkbox input:focus ~ label:before {
+  background-color: rgba(0, 0, 0, 0.15);
+  border: none;
+}
+
+/* Hover */
+.ui.slider.checkbox .box:hover,
+.ui.slider.checkbox label:hover {
+  color: rgba(0, 0, 0, 0.8);
+}
+.ui.slider.checkbox .box:hover::before,
+.ui.slider.checkbox label:hover::before {
+  background: rgba(0, 0, 0, 0.15);
+}
+
+/* Active */
+.ui.slider.checkbox input:checked ~ .box,
+.ui.slider.checkbox input:checked ~ label {
+  color: rgba(0, 0, 0, 0.95) !important;
+}
+.ui.slider.checkbox input:checked ~ .box:before,
+.ui.slider.checkbox input:checked ~ label:before {
+  background-color: #545454 !important;
+}
+.ui.slider.checkbox input:checked ~ .box:after,
+.ui.slider.checkbox input:checked ~ label:after {
+  left: 2rem;
+}
+
+/* Active Focus */
+.ui.slider.checkbox input:focus:checked ~ .box,
+.ui.slider.checkbox input:focus:checked ~ label {
+  color: rgba(0, 0, 0, 0.95) !important;
+}
+.ui.slider.checkbox input:focus:checked ~ .box:before,
+.ui.slider.checkbox input:focus:checked ~ label:before {
+  background-color: #000000 !important;
+}
+
+/*--------------
+     Toggle
+---------------*/
+
+.ui.toggle.checkbox {
+  min-height: 1.5rem;
+}
+
+/* Input */
+.ui.toggle.checkbox input {
+  width: 3.5rem;
+  height: 1.5rem;
+}
+
+/* Label */
+.ui.toggle.checkbox .box,
+.ui.toggle.checkbox label {
+  min-height: 1.5rem;
+  padding-left: 4.5rem;
+  color: #AAAAAA;
+}
+.ui.toggle.checkbox label {
+  padding-top: 0.15em;
+}
+
+/* Switch */
+.ui.toggle.checkbox .box:before,
+.ui.toggle.checkbox label:before {
+  display: block;
+  position: absolute;
+  content: '';
+  z-index: 1;
+  -webkit-transform: none;
+          transform: none;
+  border: none;
+  top: 0;
+  background: rgba(0, 0, 0, 0.05);
+  -webkit-box-shadow: none;
+          box-shadow: none;
+  width: 3.5rem;
+  height: 1.5rem;
+  border-radius: 500rem;
+}
+
+/* Handle */
+.ui.toggle.checkbox .box:after,
+.ui.toggle.checkbox label:after {
+  background: #FFFFFF -webkit-gradient(linear, left top, left bottom, from(transparent), to(rgba(0, 0, 0, 0.05)));
+  background: #FFFFFF -webkit-linear-gradient(transparent, rgba(0, 0, 0, 0.05));
+  background: #FFFFFF linear-gradient(transparent, rgba(0, 0, 0, 0.05));
+  position: absolute;
+  content: '' !important;
+  opacity: 1;
+  z-index: 2;
+  border: none;
+  -webkit-box-shadow: 0 1px 2px 0 rgba(34, 36, 38, 0.15), 0 0 0 1px rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 1px 2px 0 rgba(34, 36, 38, 0.15), 0 0 0 1px rgba(34, 36, 38, 0.15) inset;
+  width: 1.5rem;
+  height: 1.5rem;
+  top: 0;
+  left: 0;
+  border-radius: 500rem;
+  -webkit-transition: background 0.3s ease, left 0.3s ease;
+  transition: background 0.3s ease, left 0.3s ease;
+}
+.ui.toggle.checkbox input ~ .box:after,
+.ui.toggle.checkbox input ~ label:after {
+  left: -0.05rem;
+  -webkit-box-shadow: 0 1px 2px 0 rgba(34, 36, 38, 0.15), 0 0 0 1px rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 1px 2px 0 rgba(34, 36, 38, 0.15), 0 0 0 1px rgba(34, 36, 38, 0.15) inset;
+}
+
+/* Focus */
+.ui.toggle.checkbox input:focus ~ .box:before,
+.ui.toggle.checkbox input:focus ~ label:before {
+  background-color: rgba(0, 0, 0, 0.15);
+  border: none;
+}
+
+/* Hover */
+.ui.toggle.checkbox .box:hover::before,
+.ui.toggle.checkbox label:hover::before {
+  background-color: rgba(0, 0, 0, 0.15);
+  border: none;
+}
+
+/* Active */
+.ui.toggle.checkbox input:checked ~ .box,
+.ui.toggle.checkbox input:checked ~ label {
+  color: rgba(0, 0, 0, 0.95) !important;
+}
+.ui.toggle.checkbox input:checked ~ .box:before,
+.ui.toggle.checkbox input:checked ~ label:before {
+  background-color: #2185D0 !important;
+}
+.ui.toggle.checkbox input:checked ~ .box:after,
+.ui.toggle.checkbox input:checked ~ label:after {
+  left: 2.15rem;
+  -webkit-box-shadow: 0 1px 2px 0 rgba(34, 36, 38, 0.15), 0 0 0 1px rgba(34, 36, 38, 0.15) inset;
+          box-shadow: 0 1px 2px 0 rgba(34, 36, 38, 0.15), 0 0 0 1px rgba(34, 36, 38, 0.15) inset;
+}
+
+/* Active Focus */
+.ui.toggle.checkbox input:focus:checked ~ .box,
+.ui.toggle.checkbox input:focus:checked ~ label {
+  color: rgba(0, 0, 0, 0.95) !important;
+}
+.ui.toggle.checkbox input:focus:checked ~ .box:before,
+.ui.toggle.checkbox input:focus:checked ~ label:before {
+  background-color: #0d71bb !important;
+}
+
+
+/*******************************
+            Variations
+*******************************/
+
+
+/*--------------
+     Fitted
+---------------*/
+
+.ui.fitted.checkbox .box,
+.ui.fitted.checkbox label {
+  padding-left: 0 !important;
+}
+.ui.fitted.toggle.checkbox {
+  width: 3.5rem;
+}
+.ui.fitted.slider.checkbox {
+  width: 3.5rem;
+}
+
+/*--------------
+     Inverted
+---------------*/
+
+.ui.inverted.checkbox label,
+.ui.inverted.checkbox + label {
+  color: rgba(255, 255, 255, 0.9) !important;
+}
+
+/* Hover */
+.ui.inverted.checkbox .box:hover,
+.ui.inverted.checkbox label:hover {
+  color: #ffffff !important;
+}
+.ui.inverted.checkbox .box:hover::before,
+.ui.inverted.checkbox label:hover::before {
+  border-color: rgba(34, 36, 38, 0.5);
+}
+/*Slider Label */
+.ui.inverted.slider.checkbox .box,
+.ui.inverted.slider.checkbox label {
+  color: rgba(255, 255, 255, 0.5);
+}
+
+/* Slider Line */
+.ui.inverted.slider.checkbox .box:before,
+.ui.inverted.slider.checkbox label:before {
+  background-color: rgba(255, 255, 255, 0.5) !important;
+}
+
+/* Slider Hover */
+.ui.inverted.slider.checkbox .box:hover::before,
+.ui.inverted.slider.checkbox label:hover::before {
+  background: rgba(255, 255, 255, 0.7) !important;
+}
+
+/* Slider Active */
+.ui.inverted.slider.checkbox input:checked ~ .box,
+.ui.inverted.slider.checkbox input:checked ~ label {
+  color: #ffffff !important;
+}
+.ui.inverted.slider.checkbox input:checked ~ .box:before,
+.ui.inverted.slider.checkbox input:checked ~ label:before {
+  background-color: rgba(255, 255, 255, 0.8) !important;
+}
+
+/* Slider Active Focus */
+.ui.inverted.slider.checkbox input:focus:checked ~ .box,
+.ui.inverted.slider.checkbox input:focus:checked ~ label {
+  color: #ffffff !important;
+}
+.ui.inverted.slider.checkbox input:focus:checked ~ .box:before,
+.ui.inverted.slider.checkbox input:focus:checked ~ label:before {
+  background-color: rgba(255, 255, 255, 0.8) !important;
+}
+
+/* Toggle Switch */
+.ui.inverted.toggle.checkbox .box:before,
+.ui.inverted.toggle.checkbox label:before {
+  background-color: rgba(255, 255, 255, 0.9) !important;
+}
+
+/* Toggle Hover */
+.ui.inverted.toggle.checkbox .box:hover::before,
+.ui.inverted.toggle.checkbox label:hover::before {
+  background: #ffffff !important;
+}
+
+/* Toggle Active */
+.ui.inverted.toggle.checkbox input:checked ~ .box,
+.ui.inverted.toggle.checkbox input:checked ~ label {
+  color: #ffffff !important;
+}
+.ui.inverted.toggle.checkbox input:checked ~ .box:before,
+.ui.inverted.toggle.checkbox input:checked ~ label:before {
+  background-color: #2185D0 !important;
+}
+
+/* Toggle Active Focus */
+.ui.inverted.toggle.checkbox input:focus:checked ~ .box,
+.ui.inverted.toggle.checkbox input:focus:checked ~ label {
+  color: #ffffff !important;
+}
+.ui.inverted.toggle.checkbox input:focus:checked ~ .box:before,
+.ui.inverted.toggle.checkbox input:focus:checked ~ label:before {
+  background-color: #0d71bb !important;
+}
+
+/*--------------------
+        Size
+---------------------*/
+
+.ui.mini.checkbox {
+  font-size: 0.78571429em;
+}
+.ui.tiny.checkbox {
+  font-size: 0.85714286em;
+}
+.ui.small.checkbox {
+  font-size: 0.92857143em;
+}
+.ui.large.checkbox {
+  font-size: 1.14285714em;
+}
+.ui.large.form .checkbox:not(.slider):not(.toggle):not(.radio) .box:after,
+.ui.large.checkbox:not(.slider):not(.toggle):not(.radio) .box:after,
+.ui.large.form .checkbox:not(.slider):not(.toggle):not(.radio) .box:before,
+.ui.large.checkbox:not(.slider):not(.toggle):not(.radio) .box:before,
+.ui.large.form .checkbox:not(.slider):not(.toggle):not(.radio) label:after,
+.ui.large.checkbox:not(.slider):not(.toggle):not(.radio) label:after,
+.ui.large.form .checkbox:not(.slider):not(.toggle):not(.radio) label:before,
+.ui.large.checkbox:not(.slider):not(.toggle):not(.radio) label:before {
+  -webkit-transform: scale(1.14285714);
+          transform: scale(1.14285714);
+  -webkit-transform-origin: left;
+          transform-origin: left;
+}
+.ui.large.form .checkbox.radio .box:before,
+.ui.large.checkbox.radio .box:before,
+.ui.large.form .checkbox.radio label:before,
+.ui.large.checkbox.radio label:before {
+  -webkit-transform: scale(1.14285714);
+          transform: scale(1.14285714);
+  -webkit-transform-origin: left;
+          transform-origin: left;
+}
+.ui.large.form .checkbox.radio .box:after,
+.ui.large.checkbox.radio .box:after,
+.ui.large.form .checkbox.radio label:after,
+.ui.large.checkbox.radio label:after {
+  -webkit-transform: scale(0.57142857);
+          transform: scale(0.57142857);
+  -webkit-transform-origin: left;
+          transform-origin: left;
+  left: 0.33571429em;
+}
+.ui.big.checkbox {
+  font-size: 1.28571429em;
+}
+.ui.big.form .checkbox:not(.slider):not(.toggle):not(.radio) .box:after,
+.ui.big.checkbox:not(.slider):not(.toggle):not(.radio) .box:after,
+.ui.big.form .checkbox:not(.slider):not(.toggle):not(.radio) .box:before,
+.ui.big.checkbox:not(.slider):not(.toggle):not(.radio) .box:before,
+.ui.big.form .checkbox:not(.slider):not(.toggle):not(.radio) label:after,
+.ui.big.checkbox:not(.slider):not(.toggle):not(.radio) label:after,
+.ui.big.form .checkbox:not(.slider):not(.toggle):not(.radio) label:before,
+.ui.big.checkbox:not(.slider):not(.toggle):not(.radio) label:before {
+  -webkit-transform: scale(1.28571429);
+          transform: scale(1.28571429);
+  -webkit-transform-origin: left;
+          transform-origin: left;
+}
+.ui.big.form .checkbox.radio .box:before,
+.ui.big.checkbox.radio .box:before,
+.ui.big.form .checkbox.radio label:before,
+.ui.big.checkbox.radio label:before {
+  -webkit-transform: scale(1.28571429);
+          transform: scale(1.28571429);
+  -webkit-transform-origin: left;
+          transform-origin: left;
+}
+.ui.big.form .checkbox.radio .box:after,
+.ui.big.checkbox.radio .box:after,
+.ui.big.form .checkbox.radio label:after,
+.ui.big.checkbox.radio label:after {
+  -webkit-transform: scale(0.64285714);
+          transform: scale(0.64285714);
+  -webkit-transform-origin: left;
+          transform-origin: left;
+  left: 0.37142857em;
+}
+.ui.huge.checkbox {
+  font-size: 1.42857143em;
+}
+.ui.huge.form .checkbox:not(.slider):not(.toggle):not(.radio) .box:after,
+.ui.huge.checkbox:not(.slider):not(.toggle):not(.radio) .box:after,
+.ui.huge.form .checkbox:not(.slider):not(.toggle):not(.radio) .box:before,
+.ui.huge.checkbox:not(.slider):not(.toggle):not(.radio) .box:before,
+.ui.huge.form .checkbox:not(.slider):not(.toggle):not(.radio) label:after,
+.ui.huge.checkbox:not(.slider):not(.toggle):not(.radio) label:after,
+.ui.huge.form .checkbox:not(.slider):not(.toggle):not(.radio) label:before,
+.ui.huge.checkbox:not(.slider):not(.toggle):not(.radio) label:before {
+  -webkit-transform: scale(1.42857143);
+          transform: scale(1.42857143);
+  -webkit-transform-origin: left;
+          transform-origin: left;
+}
+.ui.huge.form .checkbox.radio .box:before,
+.ui.huge.checkbox.radio .box:before,
+.ui.huge.form .checkbox.radio label:before,
+.ui.huge.checkbox.radio label:before {
+  -webkit-transform: scale(1.42857143);
+          transform: scale(1.42857143);
+  -webkit-transform-origin: left;
+          transform-origin: left;
+}
+.ui.huge.form .checkbox.radio .box:after,
+.ui.huge.checkbox.radio .box:after,
+.ui.huge.form .checkbox.radio label:after,
+.ui.huge.checkbox.radio label:after {
+  -webkit-transform: scale(0.71428571);
+          transform: scale(0.71428571);
+  -webkit-transform-origin: left;
+          transform-origin: left;
+  left: 0.40714286em;
+}
+.ui.massive.checkbox {
+  font-size: 1.71428571em;
+}
+.ui.massive.form .checkbox:not(.slider):not(.toggle):not(.radio) .box:after,
+.ui.massive.checkbox:not(.slider):not(.toggle):not(.radio) .box:after,
+.ui.massive.form .checkbox:not(.slider):not(.toggle):not(.radio) .box:before,
+.ui.massive.checkbox:not(.slider):not(.toggle):not(.radio) .box:before,
+.ui.massive.form .checkbox:not(.slider):not(.toggle):not(.radio) label:after,
+.ui.massive.checkbox:not(.slider):not(.toggle):not(.radio) label:after,
+.ui.massive.form .checkbox:not(.slider):not(.toggle):not(.radio) label:before,
+.ui.massive.checkbox:not(.slider):not(.toggle):not(.radio) label:before {
+  -webkit-transform: scale(1.71428571);
+          transform: scale(1.71428571);
+  -webkit-transform-origin: left;
+          transform-origin: left;
+}
+.ui.massive.form .checkbox.radio .box:before,
+.ui.massive.checkbox.radio .box:before,
+.ui.massive.form .checkbox.radio label:before,
+.ui.massive.checkbox.radio label:before {
+  -webkit-transform: scale(1.71428571);
+          transform: scale(1.71428571);
+  -webkit-transform-origin: left;
+          transform-origin: left;
+}
+.ui.massive.form .checkbox.radio .box:after,
+.ui.massive.checkbox.radio .box:after,
+.ui.massive.form .checkbox.radio label:after,
+.ui.massive.checkbox.radio label:after {
+  -webkit-transform: scale(0.85714286);
+          transform: scale(0.85714286);
+  -webkit-transform-origin: left;
+          transform-origin: left;
+  left: 0.47857143em;
+}
+
+
+/*******************************
+         Theme Overrides
+*******************************/
+
+@font-face {
+  font-family: 'Checkbox';
+  src: url(data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMg8SBD8AAAC8AAAAYGNtYXAYVtCJAAABHAAAAFRnYXNwAAAAEAAAAXAAAAAIZ2x5Zn4huwUAAAF4AAABYGhlYWQGPe1ZAAAC2AAAADZoaGVhB30DyAAAAxAAAAAkaG10eBBKAEUAAAM0AAAAHGxvY2EAmgESAAADUAAAABBtYXhwAAkALwAAA2AAAAAgbmFtZSC8IugAAAOAAAABknBvc3QAAwAAAAAFFAAAACAAAwMTAZAABQAAApkCzAAAAI8CmQLMAAAB6wAzAQkAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADoAgPA/8AAQAPAAEAAAAABAAAAAAAAAAAAAAAgAAAAAAADAAAAAwAAABwAAQADAAAAHAADAAEAAAAcAAQAOAAAAAoACAACAAIAAQAg6AL//f//AAAAAAAg6AD//f//AAH/4xgEAAMAAQAAAAAAAAAAAAAAAQAB//8ADwABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQAAAAAAAAAAAAIAADc5AQAAAAABAEUAUQO7AvgAGgAAARQHAQYjIicBJjU0PwE2MzIfAQE2MzIfARYVA7sQ/hQQFhcQ/uMQEE4QFxcQqAF2EBcXEE4QAnMWEP4UEBABHRAXFhBOEBCoAXcQEE4QFwAAAAABAAABbgMlAkkAFAAAARUUBwYjISInJj0BNDc2MyEyFxYVAyUQEBf9SRcQEBAQFwK3FxAQAhJtFxAQEBAXbRcQEBAQFwAAAAABAAAASQMlA24ALAAAARUUBwYrARUUBwYrASInJj0BIyInJj0BNDc2OwE1NDc2OwEyFxYdATMyFxYVAyUQEBfuEBAXbhYQEO4XEBAQEBfuEBAWbhcQEO4XEBACEm0XEBDuFxAQEBAX7hAQF20XEBDuFxAQEBAX7hAQFwAAAQAAAAIAAHRSzT9fDzz1AAsEAAAAAADRsdR3AAAAANGx1HcAAAAAA7sDbgAAAAgAAgAAAAAAAAABAAADwP/AAAAEAAAAAAADuwABAAAAAAAAAAAAAAAAAAAABwQAAAAAAAAAAAAAAAIAAAAEAABFAyUAAAMlAAAAAAAAAAoAFAAeAE4AcgCwAAEAAAAHAC0AAQAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAOAK4AAQAAAAAAAQAIAAAAAQAAAAAAAgAHAGkAAQAAAAAAAwAIADkAAQAAAAAABAAIAH4AAQAAAAAABQALABgAAQAAAAAABgAIAFEAAQAAAAAACgAaAJYAAwABBAkAAQAQAAgAAwABBAkAAgAOAHAAAwABBAkAAwAQAEEAAwABBAkABAAQAIYAAwABBAkABQAWACMAAwABBAkABgAQAFkAAwABBAkACgA0ALBDaGVja2JveABDAGgAZQBjAGsAYgBvAHhWZXJzaW9uIDIuMABWAGUAcgBzAGkAbwBuACAAMgAuADBDaGVja2JveABDAGgAZQBjAGsAYgBvAHhDaGVja2JveABDAGgAZQBjAGsAYgBvAHhSZWd1bGFyAFIAZQBnAHUAbABhAHJDaGVja2JveABDAGgAZQBjAGsAYgBvAHhGb250IGdlbmVyYXRlZCBieSBJY29Nb29uLgBGAG8AbgB0ACAAZwBlAG4AZQByAGEAdABlAGQAIABiAHkAIABJAGMAbwBNAG8AbwBuAC4AAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) format('truetype');
+}
+
+/* Checkmark */
+.ui.checkbox label:after,
+.ui.checkbox .box:after {
+  font-family: 'Checkbox';
+}
+
+/* Checked */
+.ui.checkbox input:checked ~ .box:after,
+.ui.checkbox input:checked ~ label:after {
+  content: '\e800';
+}
+
+/* Indeterminate */
+.ui.checkbox input:indeterminate ~ .box:after,
+.ui.checkbox input:indeterminate ~ label:after {
+  font-size: 12px;
+  content: '\e801';
+}
+/*  UTF Reference
+.check:before { content: '\e800'; }
+.dash:before  { content: '\e801'; }
+.plus:before { content: '\e802'; }
+*/
+
+
+/*******************************
+         Site Overrides
+*******************************/
+

+ 876 - 0
website/semantic/dist/components/checkbox.js

@@ -0,0 +1,876 @@
+/*!
+ * # Fomantic-UI - Checkbox
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */
+
+;(function ($, window, document, undefined) {
+
+'use strict';
+
+$.isFunction = $.isFunction || function(obj) {
+  return typeof obj === "function" && typeof obj.nodeType !== "number";
+};
+
+window = (typeof window != 'undefined' && window.Math == Math)
+  ? window
+  : (typeof self != 'undefined' && self.Math == Math)
+    ? self
+    : Function('return this')()
+;
+
+$.fn.checkbox = function(parameters) {
+  var
+    $allModules    = $(this),
+    moduleSelector = $allModules.selector || '',
+
+    time           = new Date().getTime(),
+    performance    = [],
+
+    query          = arguments[0],
+    methodInvoked  = (typeof query == 'string'),
+    queryArguments = [].slice.call(arguments, 1),
+    returnedValue
+  ;
+
+  $allModules
+    .each(function() {
+      var
+        settings        = $.extend(true, {}, $.fn.checkbox.settings, parameters),
+
+        className       = settings.className,
+        namespace       = settings.namespace,
+        selector        = settings.selector,
+        error           = settings.error,
+
+        eventNamespace  = '.' + namespace,
+        moduleNamespace = 'module-' + namespace,
+
+        $module         = $(this),
+        $label          = $(this).children(selector.label),
+        $input          = $(this).children(selector.input),
+        input           = $input[0],
+
+        initialLoad     = false,
+        shortcutPressed = false,
+        instance        = $module.data(moduleNamespace),
+
+        observer,
+        element         = this,
+        module
+      ;
+
+      module      = {
+
+        initialize: function() {
+          module.verbose('Initializing checkbox', settings);
+
+          module.create.label();
+          module.bind.events();
+
+          module.set.tabbable();
+          module.hide.input();
+
+          module.observeChanges();
+          module.instantiate();
+          module.setup();
+        },
+
+        instantiate: function() {
+          module.verbose('Storing instance of module', module);
+          instance = module;
+          $module
+            .data(moduleNamespace, module)
+          ;
+        },
+
+        destroy: function() {
+          module.verbose('Destroying module');
+          module.unbind.events();
+          module.show.input();
+          $module.removeData(moduleNamespace);
+        },
+
+        fix: {
+          reference: function() {
+            if( $module.is(selector.input) ) {
+              module.debug('Behavior called on <input> adjusting invoked element');
+              $module = $module.closest(selector.checkbox);
+              module.refresh();
+            }
+          }
+        },
+
+        setup: function() {
+          module.set.initialLoad();
+          if( module.is.indeterminate() ) {
+            module.debug('Initial value is indeterminate');
+            module.indeterminate();
+          }
+          else if( module.is.checked() ) {
+            module.debug('Initial value is checked');
+            module.check();
+          }
+          else {
+            module.debug('Initial value is unchecked');
+            module.uncheck();
+          }
+          module.remove.initialLoad();
+        },
+
+        refresh: function() {
+          $label = $module.children(selector.label);
+          $input = $module.children(selector.input);
+          input  = $input[0];
+        },
+
+        hide: {
+          input: function() {
+            module.verbose('Modifying <input> z-index to be unselectable');
+            $input.addClass(className.hidden);
+          }
+        },
+        show: {
+          input: function() {
+            module.verbose('Modifying <input> z-index to be selectable');
+            $input.removeClass(className.hidden);
+          }
+        },
+
+        observeChanges: function() {
+          if('MutationObserver' in window) {
+            observer = new MutationObserver(function(mutations) {
+              module.debug('DOM tree modified, updating selector cache');
+              module.refresh();
+            });
+            observer.observe(element, {
+              childList : true,
+              subtree   : true
+            });
+            module.debug('Setting up mutation observer', observer);
+          }
+        },
+
+        attachEvents: function(selector, event) {
+          var
+            $element = $(selector)
+          ;
+          event = $.isFunction(module[event])
+            ? module[event]
+            : module.toggle
+          ;
+          if($element.length > 0) {
+            module.debug('Attaching checkbox events to element', selector, event);
+            $element
+              .on('click' + eventNamespace, event)
+            ;
+          }
+          else {
+            module.error(error.notFound);
+          }
+        },
+
+        preventDefaultOnInputTarget: function() {
+          if(typeof event !== 'undefined' && $(event.target).is(selector.input)) {
+            module.verbose('Preventing default check action after manual check action');
+            event.preventDefault();
+          }
+        },
+
+        event: {
+          change: function(event) {
+            if( !module.should.ignoreCallbacks() ) {
+              settings.onChange.call(input);
+            }
+          },
+          click: function(event) {
+            var
+              $target = $(event.target)
+            ;
+            if( $target.is(selector.input) ) {
+              module.verbose('Using default check action on initialized checkbox');
+              return;
+            }
+            if( $target.is(selector.link) ) {
+              module.debug('Clicking link inside checkbox, skipping toggle');
+              return;
+            }
+            module.toggle();
+            $input.focus();
+            event.preventDefault();
+          },
+          keydown: function(event) {
+            var
+              key     = event.which,
+              keyCode = {
+                enter  : 13,
+                space  : 32,
+                escape : 27,
+                left   : 37,
+                up     : 38,
+                right  : 39,
+                down   : 40
+              }
+            ;
+
+            var r = module.get.radios(),
+                rIndex = r.index($module),
+                rLen = r.length,
+                checkIndex = false;
+
+            if(key == keyCode.left || key == keyCode.up) {
+              checkIndex = (rIndex === 0 ? rLen : rIndex) - 1;
+            } else if(key == keyCode.right || key == keyCode.down) {
+              checkIndex = rIndex === rLen-1 ? 0 : rIndex+1;
+            }
+
+            if (!module.should.ignoreCallbacks() && checkIndex !== false) {
+              if(settings.beforeUnchecked.apply(input)===false) {
+                module.verbose('Option not allowed to be unchecked, cancelling key navigation');
+                return false;
+              }
+              if (settings.beforeChecked.apply($(r[checkIndex]).children(selector.input)[0])===false) {
+                module.verbose('Next option should not allow check, cancelling key navigation');
+                return false;
+              }
+            }
+
+            if(key == keyCode.escape) {
+              module.verbose('Escape key pressed blurring field');
+              $input.blur();
+              shortcutPressed = true;
+            }
+            else if(!event.ctrlKey && ( key == keyCode.space || (key == keyCode.enter && settings.enableEnterKey)) ) {
+              module.verbose('Enter/space key pressed, toggling checkbox');
+              module.toggle();
+              shortcutPressed = true;
+            }
+            else {
+              shortcutPressed = false;
+            }
+          },
+          keyup: function(event) {
+            if(shortcutPressed) {
+              event.preventDefault();
+            }
+          }
+        },
+
+        check: function() {
+          if( !module.should.allowCheck() ) {
+            return;
+          }
+          module.debug('Checking checkbox', $input);
+          module.set.checked();
+          if( !module.should.ignoreCallbacks() ) {
+            settings.onChecked.call(input);
+            module.trigger.change();
+          }
+          module.preventDefaultOnInputTarget();
+        },
+
+        uncheck: function() {
+          if( !module.should.allowUncheck() ) {
+            return;
+          }
+          module.debug('Unchecking checkbox');
+          module.set.unchecked();
+          if( !module.should.ignoreCallbacks() ) {
+            settings.onUnchecked.call(input);
+            module.trigger.change();
+          }
+          module.preventDefaultOnInputTarget();
+        },
+
+        indeterminate: function() {
+          if( module.should.allowIndeterminate() ) {
+            module.debug('Checkbox is already indeterminate');
+            return;
+          }
+          module.debug('Making checkbox indeterminate');
+          module.set.indeterminate();
+          if( !module.should.ignoreCallbacks() ) {
+            settings.onIndeterminate.call(input);
+            module.trigger.change();
+          }
+        },
+
+        determinate: function() {
+          if( module.should.allowDeterminate() ) {
+            module.debug('Checkbox is already determinate');
+            return;
+          }
+          module.debug('Making checkbox determinate');
+          module.set.determinate();
+          if( !module.should.ignoreCallbacks() ) {
+            settings.onDeterminate.call(input);
+            module.trigger.change();
+          }
+        },
+
+        enable: function() {
+          if( module.is.enabled() ) {
+            module.debug('Checkbox is already enabled');
+            return;
+          }
+          module.debug('Enabling checkbox');
+          module.set.enabled();
+          if( !module.should.ignoreCallbacks() ) {
+            settings.onEnable.call(input);
+            // preserve legacy callbacks
+            settings.onEnabled.call(input);
+            module.trigger.change();
+          }
+        },
+
+        disable: function() {
+          if( module.is.disabled() ) {
+            module.debug('Checkbox is already disabled');
+            return;
+          }
+          module.debug('Disabling checkbox');
+          module.set.disabled();
+          if( !module.should.ignoreCallbacks() ) {
+            settings.onDisable.call(input);
+            // preserve legacy callbacks
+            settings.onDisabled.call(input);
+            module.trigger.change();
+          }
+        },
+
+        get: {
+          radios: function() {
+            var
+              name = module.get.name()
+            ;
+            return $('input[name="' + name + '"]').closest(selector.checkbox);
+          },
+          otherRadios: function() {
+            return module.get.radios().not($module);
+          },
+          name: function() {
+            return $input.attr('name');
+          }
+        },
+
+        is: {
+          initialLoad: function() {
+            return initialLoad;
+          },
+          radio: function() {
+            return ($input.hasClass(className.radio) || $input.attr('type') == 'radio');
+          },
+          indeterminate: function() {
+            return $input.prop('indeterminate') !== undefined && $input.prop('indeterminate');
+          },
+          checked: function() {
+            return $input.prop('checked') !== undefined && $input.prop('checked');
+          },
+          disabled: function() {
+            return $input.prop('disabled') !== undefined && $input.prop('disabled');
+          },
+          enabled: function() {
+            return !module.is.disabled();
+          },
+          determinate: function() {
+            return !module.is.indeterminate();
+          },
+          unchecked: function() {
+            return !module.is.checked();
+          }
+        },
+
+        should: {
+          allowCheck: function() {
+            if(module.is.determinate() && module.is.checked() && !module.is.initialLoad() ) {
+              module.debug('Should not allow check, checkbox is already checked');
+              return false;
+            }
+            if(!module.should.ignoreCallbacks() && settings.beforeChecked.apply(input) === false) {
+              module.debug('Should not allow check, beforeChecked cancelled');
+              return false;
+            }
+            return true;
+          },
+          allowUncheck: function() {
+            if(module.is.determinate() && module.is.unchecked() && !module.is.initialLoad() ) {
+              module.debug('Should not allow uncheck, checkbox is already unchecked');
+              return false;
+            }
+            if(!module.should.ignoreCallbacks() && settings.beforeUnchecked.apply(input) === false) {
+              module.debug('Should not allow uncheck, beforeUnchecked cancelled');
+              return false;
+            }
+            return true;
+          },
+          allowIndeterminate: function() {
+            if(module.is.indeterminate() && !module.is.initialLoad() ) {
+              module.debug('Should not allow indeterminate, checkbox is already indeterminate');
+              return false;
+            }
+            if(!module.should.ignoreCallbacks() && settings.beforeIndeterminate.apply(input) === false) {
+              module.debug('Should not allow indeterminate, beforeIndeterminate cancelled');
+              return false;
+            }
+            return true;
+          },
+          allowDeterminate: function() {
+            if(module.is.determinate() && !module.is.initialLoad() ) {
+              module.debug('Should not allow determinate, checkbox is already determinate');
+              return false;
+            }
+            if(!module.should.ignoreCallbacks() && settings.beforeDeterminate.apply(input) === false) {
+              module.debug('Should not allow determinate, beforeDeterminate cancelled');
+              return false;
+            }
+            return true;
+          },
+          ignoreCallbacks: function() {
+            return (initialLoad && !settings.fireOnInit);
+          }
+        },
+
+        can: {
+          change: function() {
+            return !( $module.hasClass(className.disabled) || $module.hasClass(className.readOnly) || $input.prop('disabled') || $input.prop('readonly') );
+          },
+          uncheck: function() {
+            return (typeof settings.uncheckable === 'boolean')
+              ? settings.uncheckable
+              : !module.is.radio()
+            ;
+          }
+        },
+
+        set: {
+          initialLoad: function() {
+            initialLoad = true;
+          },
+          checked: function() {
+            module.verbose('Setting class to checked');
+            $module
+              .removeClass(className.indeterminate)
+              .addClass(className.checked)
+            ;
+            if( module.is.radio() ) {
+              module.uncheckOthers();
+            }
+            if(!module.is.indeterminate() && module.is.checked()) {
+              module.debug('Input is already checked, skipping input property change');
+              return;
+            }
+            module.verbose('Setting state to checked', input);
+            $input
+              .prop('indeterminate', false)
+              .prop('checked', true)
+            ;
+          },
+          unchecked: function() {
+            module.verbose('Removing checked class');
+            $module
+              .removeClass(className.indeterminate)
+              .removeClass(className.checked)
+            ;
+            if(!module.is.indeterminate() &&  module.is.unchecked() ) {
+              module.debug('Input is already unchecked');
+              return;
+            }
+            module.debug('Setting state to unchecked');
+            $input
+              .prop('indeterminate', false)
+              .prop('checked', false)
+            ;
+          },
+          indeterminate: function() {
+            module.verbose('Setting class to indeterminate');
+            $module
+              .addClass(className.indeterminate)
+            ;
+            if( module.is.indeterminate() ) {
+              module.debug('Input is already indeterminate, skipping input property change');
+              return;
+            }
+            module.debug('Setting state to indeterminate');
+            $input
+              .prop('indeterminate', true)
+            ;
+          },
+          determinate: function() {
+            module.verbose('Removing indeterminate class');
+            $module
+              .removeClass(className.indeterminate)
+            ;
+            if( module.is.determinate() ) {
+              module.debug('Input is already determinate, skipping input property change');
+              return;
+            }
+            module.debug('Setting state to determinate');
+            $input
+              .prop('indeterminate', false)
+            ;
+          },
+          disabled: function() {
+            module.verbose('Setting class to disabled');
+            $module
+              .addClass(className.disabled)
+            ;
+            if( module.is.disabled() ) {
+              module.debug('Input is already disabled, skipping input property change');
+              return;
+            }
+            module.debug('Setting state to disabled');
+            $input
+              .prop('disabled', 'disabled')
+            ;
+          },
+          enabled: function() {
+            module.verbose('Removing disabled class');
+            $module.removeClass(className.disabled);
+            if( module.is.enabled() ) {
+              module.debug('Input is already enabled, skipping input property change');
+              return;
+            }
+            module.debug('Setting state to enabled');
+            $input
+              .prop('disabled', false)
+            ;
+          },
+          tabbable: function() {
+            module.verbose('Adding tabindex to checkbox');
+            if( $input.attr('tabindex') === undefined) {
+              $input.attr('tabindex', 0);
+            }
+          }
+        },
+
+        remove: {
+          initialLoad: function() {
+            initialLoad = false;
+          }
+        },
+
+        trigger: {
+          change: function() {
+            var
+              events       = document.createEvent('HTMLEvents'),
+              inputElement = $input[0]
+            ;
+            if(inputElement) {
+              module.verbose('Triggering native change event');
+              events.initEvent('change', true, false);
+              inputElement.dispatchEvent(events);
+            }
+          }
+        },
+
+
+        create: {
+          label: function() {
+            if($input.prevAll(selector.label).length > 0) {
+              $input.prev(selector.label).detach().insertAfter($input);
+              module.debug('Moving existing label', $label);
+            }
+            else if( !module.has.label() ) {
+              $label = $('<label>').insertAfter($input);
+              module.debug('Creating label', $label);
+            }
+          }
+        },
+
+        has: {
+          label: function() {
+            return ($label.length > 0);
+          }
+        },
+
+        bind: {
+          events: function() {
+            module.verbose('Attaching checkbox events');
+            $module
+              .on('click'   + eventNamespace, module.event.click)
+              .on('change'  + eventNamespace, module.event.change)
+              .on('keydown' + eventNamespace, selector.input, module.event.keydown)
+              .on('keyup'   + eventNamespace, selector.input, module.event.keyup)
+            ;
+          }
+        },
+
+        unbind: {
+          events: function() {
+            module.debug('Removing events');
+            $module
+              .off(eventNamespace)
+            ;
+          }
+        },
+
+        uncheckOthers: function() {
+          var
+            $radios = module.get.otherRadios()
+          ;
+          module.debug('Unchecking other radios', $radios);
+          $radios.removeClass(className.checked);
+        },
+
+        toggle: function() {
+          if( !module.can.change() ) {
+            if(!module.is.radio()) {
+              module.debug('Checkbox is read-only or disabled, ignoring toggle');
+            }
+            return;
+          }
+          if( module.is.indeterminate() || module.is.unchecked() ) {
+            module.debug('Currently unchecked');
+            module.check();
+          }
+          else if( module.is.checked() && module.can.uncheck() ) {
+            module.debug('Currently checked');
+            module.uncheck();
+          }
+        },
+        setting: function(name, value) {
+          module.debug('Changing setting', name, value);
+          if( $.isPlainObject(name) ) {
+            $.extend(true, settings, name);
+          }
+          else if(value !== undefined) {
+            if($.isPlainObject(settings[name])) {
+              $.extend(true, settings[name], value);
+            }
+            else {
+              settings[name] = value;
+            }
+          }
+          else {
+            return settings[name];
+          }
+        },
+        internal: function(name, value) {
+          if( $.isPlainObject(name) ) {
+            $.extend(true, module, name);
+          }
+          else if(value !== undefined) {
+            module[name] = value;
+          }
+          else {
+            return module[name];
+          }
+        },
+        debug: function() {
+          if(!settings.silent && settings.debug) {
+            if(settings.performance) {
+              module.performance.log(arguments);
+            }
+            else {
+              module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
+              module.debug.apply(console, arguments);
+            }
+          }
+        },
+        verbose: function() {
+          if(!settings.silent && settings.verbose && settings.debug) {
+            if(settings.performance) {
+              module.performance.log(arguments);
+            }
+            else {
+              module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
+              module.verbose.apply(console, arguments);
+            }
+          }
+        },
+        error: function() {
+          if(!settings.silent) {
+            module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
+            module.error.apply(console, arguments);
+          }
+        },
+        performance: {
+          log: function(message) {
+            var
+              currentTime,
+              executionTime,
+              previousTime
+            ;
+            if(settings.performance) {
+              currentTime   = new Date().getTime();
+              previousTime  = time || currentTime;
+              executionTime = currentTime - previousTime;
+              time          = currentTime;
+              performance.push({
+                'Name'           : message[0],
+                'Arguments'      : [].slice.call(message, 1) || '',
+                'Element'        : element,
+                'Execution Time' : executionTime
+              });
+            }
+            clearTimeout(module.performance.timer);
+            module.performance.timer = setTimeout(module.performance.display, 500);
+          },
+          display: function() {
+            var
+              title = settings.name + ':',
+              totalTime = 0
+            ;
+            time = false;
+            clearTimeout(module.performance.timer);
+            $.each(performance, function(index, data) {
+              totalTime += data['Execution Time'];
+            });
+            title += ' ' + totalTime + 'ms';
+            if(moduleSelector) {
+              title += ' \'' + moduleSelector + '\'';
+            }
+            if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
+              console.groupCollapsed(title);
+              if(console.table) {
+                console.table(performance);
+              }
+              else {
+                $.each(performance, function(index, data) {
+                  console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
+                });
+              }
+              console.groupEnd();
+            }
+            performance = [];
+          }
+        },
+        invoke: function(query, passedArguments, context) {
+          var
+            object = instance,
+            maxDepth,
+            found,
+            response
+          ;
+          passedArguments = passedArguments || queryArguments;
+          context         = element         || context;
+          if(typeof query == 'string' && object !== undefined) {
+            query    = query.split(/[\. ]/);
+            maxDepth = query.length - 1;
+            $.each(query, function(depth, value) {
+              var camelCaseValue = (depth != maxDepth)
+                ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
+                : query
+              ;
+              if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
+                object = object[camelCaseValue];
+              }
+              else if( object[camelCaseValue] !== undefined ) {
+                found = object[camelCaseValue];
+                return false;
+              }
+              else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
+                object = object[value];
+              }
+              else if( object[value] !== undefined ) {
+                found = object[value];
+                return false;
+              }
+              else {
+                module.error(error.method, query);
+                return false;
+              }
+            });
+          }
+          if ( $.isFunction( found ) ) {
+            response = found.apply(context, passedArguments);
+          }
+          else if(found !== undefined) {
+            response = found;
+          }
+          if(Array.isArray(returnedValue)) {
+            returnedValue.push(response);
+          }
+          else if(returnedValue !== undefined) {
+            returnedValue = [returnedValue, response];
+          }
+          else if(response !== undefined) {
+            returnedValue = response;
+          }
+          return found;
+        }
+      };
+
+      if(methodInvoked) {
+        if(instance === undefined) {
+          module.initialize();
+        }
+        module.invoke(query);
+      }
+      else {
+        if(instance !== undefined) {
+          instance.invoke('destroy');
+        }
+        module.initialize();
+      }
+    })
+  ;
+
+  return (returnedValue !== undefined)
+    ? returnedValue
+    : this
+  ;
+};
+
+$.fn.checkbox.settings = {
+
+  name                : 'Checkbox',
+  namespace           : 'checkbox',
+
+  silent              : false,
+  debug               : false,
+  verbose             : true,
+  performance         : true,
+
+  // delegated event context
+  uncheckable         : 'auto',
+  fireOnInit          : false,
+  enableEnterKey      : true,
+
+  onChange            : function(){},
+
+  beforeChecked       : function(){},
+  beforeUnchecked     : function(){},
+  beforeDeterminate   : function(){},
+  beforeIndeterminate : function(){},
+
+  onChecked           : function(){},
+  onUnchecked         : function(){},
+
+  onDeterminate       : function() {},
+  onIndeterminate     : function() {},
+
+  onEnable            : function(){},
+  onDisable           : function(){},
+
+  // preserve misspelled callbacks (will be removed in 3.0)
+  onEnabled           : function(){},
+  onDisabled          : function(){},
+
+  className       : {
+    checked       : 'checked',
+    indeterminate : 'indeterminate',
+    disabled      : 'disabled',
+    hidden        : 'hidden',
+    radio         : 'radio',
+    readOnly      : 'read-only'
+  },
+
+  error     : {
+    method       : 'The method you called is not defined'
+  },
+
+  selector : {
+    checkbox : '.ui.checkbox',
+    label    : 'label, .box',
+    input    : 'input[type="checkbox"], input[type="radio"]',
+    link     : 'a[href]'
+  }
+
+};
+
+})( jQuery, window, document );

Разлика између датотеке није приказан због своје велике величине
+ 8 - 0
website/semantic/dist/components/checkbox.min.css


Разлика између датотеке није приказан због своје велике величине
+ 10 - 0
website/semantic/dist/components/checkbox.min.js


+ 296 - 0
website/semantic/dist/components/comment.css

@@ -0,0 +1,296 @@
+/*!
+ * # Fomantic-UI - Comment
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */
+
+
+/*******************************
+            Standard
+*******************************/
+
+
+/*--------------
+    Comments
+---------------*/
+
+.ui.comments {
+  margin: 1.5em 0;
+  max-width: 650px;
+}
+.ui.comments:first-child {
+  margin-top: 0;
+}
+.ui.comments:last-child {
+  margin-bottom: 0;
+}
+
+/*--------------
+     Comment
+---------------*/
+
+.ui.comments .comment {
+  position: relative;
+  background: none;
+  margin: 0.5em 0 0;
+  padding: 0.5em 0 0;
+  border: none;
+  border-top: none;
+  line-height: 1.2;
+}
+.ui.comments .comment:first-child {
+  margin-top: 0;
+  padding-top: 0;
+}
+
+/*--------------------
+    Nested Comments
+---------------------*/
+
+.ui.comments .comment > .comments {
+  margin: 0 0 0.5em 0.5em;
+  padding: 1em 0 1em 1em;
+}
+.ui.comments .comment > .comments:before {
+  position: absolute;
+  top: 0;
+  left: 0;
+}
+.ui.comments .comment > .comments .comment {
+  border: none;
+  border-top: none;
+  background: none;
+}
+
+/*--------------
+     Avatar
+---------------*/
+
+.ui.comments .comment .avatar {
+  display: block;
+  width: 2.5em;
+  height: auto;
+  float: left;
+  margin: 0.2em 0 0;
+}
+.ui.comments .comment img.avatar,
+.ui.comments .comment .avatar img {
+  display: block;
+  margin: 0 auto;
+  width: 100%;
+  height: 100%;
+  border-radius: 0.25rem;
+}
+
+/*--------------
+     Content
+---------------*/
+
+.ui.comments .comment > .content {
+  display: block;
+}
+
+/* If there is an avatar move content over */
+.ui.comments .comment > .avatar ~ .content {
+  margin-left: 3.5em;
+}
+
+/*--------------
+     Author
+---------------*/
+
+.ui.comments .comment .author {
+  font-size: 1em;
+  color: #AAAAAA;
+  font-weight: bold;
+}
+.ui.comments .comment a.author {
+  cursor: pointer;
+}
+.ui.comments .comment a.author:hover {
+  color: #1e70bf;
+}
+
+/*--------------
+     Metadata
+---------------*/
+
+.ui.comments .comment .metadata {
+  display: inline-block;
+  margin-left: 0.5em;
+  color: rgba(0, 0, 0, 0.4);
+  font-size: 0.875em;
+}
+.ui.comments .comment .metadata > * {
+  display: inline-block;
+  margin: 0 0.5em 0 0;
+}
+.ui.comments .comment .metadata > :last-child {
+  margin-right: 0;
+}
+
+/*--------------------
+     Comment Text
+---------------------*/
+
+.ui.comments .comment .text {
+  margin: 0.25em 0 0.5em;
+  font-size: 1em;
+  word-wrap: break-word;
+  color: #AAAAAA;
+  line-height: 1.3;
+}
+
+/*--------------------
+     User Actions
+---------------------*/
+
+.ui.comments .comment .actions {
+  font-size: 0.875em;
+}
+.ui.comments .comment .actions a {
+  cursor: pointer;
+  display: inline-block;
+  margin: 0 0.75em 0 0;
+  color: rgba(0, 0, 0, 0.4);
+}
+.ui.comments .comment .actions a:last-child {
+  margin-right: 0;
+}
+.ui.comments .comment .actions a.active,
+.ui.comments .comment .actions a:hover {
+  color: rgba(0, 0, 0, 0.8);
+}
+
+/*--------------------
+      Reply Form
+---------------------*/
+
+.ui.comments > .reply.form {
+  margin-top: 1em;
+}
+.ui.comments .comment .reply.form {
+  width: 100%;
+  margin-top: 1em;
+}
+.ui.comments .reply.form textarea {
+  font-size: 1em;
+  height: 12em;
+}
+
+
+/*******************************
+            State
+*******************************/
+
+.ui.collapsed.comments,
+.ui.comments .collapsed.comments,
+.ui.comments .collapsed.comment {
+  display: none;
+}
+
+
+/*******************************
+           Variations
+*******************************/
+
+
+/*--------------------
+        Threaded
+---------------------*/
+
+.ui.threaded.comments .comment > .comments {
+  margin: -1.5em 0 -1em 1.25em;
+  padding: 3em 0 2em 2.25em;
+  -webkit-box-shadow: -1px 0 0 rgba(34, 36, 38, 0.15);
+          box-shadow: -1px 0 0 rgba(34, 36, 38, 0.15);
+}
+
+/*--------------------
+        Minimal
+---------------------*/
+
+.ui.minimal.comments .comment .actions {
+  opacity: 0;
+  position: absolute;
+  top: 0;
+  right: 0;
+  left: auto;
+  -webkit-transition: opacity 0.2s ease;
+  transition: opacity 0.2s ease;
+  -webkit-transition-delay: 0.1s;
+          transition-delay: 0.1s;
+}
+.ui.minimal.comments .comment > .content:hover > .actions {
+  opacity: 1;
+}
+
+/*-------------------
+        Sizes
+--------------------*/
+
+.ui.mini.comments {
+  font-size: 0.78571429rem;
+}
+.ui.tiny.comments {
+  font-size: 0.85714286rem;
+}
+.ui.small.comments {
+  font-size: 0.92857143rem;
+}
+.ui.comments {
+  font-size: 1rem;
+}
+.ui.large.comments {
+  font-size: 1.14285714rem;
+}
+.ui.big.comments {
+  font-size: 1.28571429rem;
+}
+.ui.huge.comments {
+  font-size: 1.42857143rem;
+}
+.ui.massive.comments {
+  font-size: 1.71428571rem;
+}
+
+/*-------------------
+        Inverted
+--------------------*/
+
+.ui.inverted.comments .comment {
+  background-color: #1B1C1D;
+}
+.ui.inverted.comments .comment .author,
+.ui.inverted.comments .comment .text {
+  color: rgba(255, 255, 255, 0.9);
+}
+.ui.inverted.comments .comment .metadata,
+.ui.inverted.comments .comment .actions a {
+  color: rgba(255, 255, 255, 0.7);
+}
+.ui.inverted.comments .comment a.author:hover,
+.ui.inverted.comments .comment .actions a.active,
+.ui.inverted.comments .comment .actions a:hover {
+  color: #ffffff;
+}
+.ui.inverted.threaded.comments .comment > .comments {
+  -webkit-box-shadow: -1px 0 0 #555555;
+          box-shadow: -1px 0 0 #555555;
+}
+
+
+/*******************************
+         Theme Overrides
+*******************************/
+
+
+
+/*******************************
+    User Variable Overrides
+*******************************/
+

Разлика између датотеке није приказан због своје велике величине
+ 8 - 0
website/semantic/dist/components/comment.min.css


+ 145 - 0
website/semantic/dist/components/container.css

@@ -0,0 +1,145 @@
+/*!
+ * # Fomantic-UI - Container
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */
+
+
+/*******************************
+            Container
+*******************************/
+
+
+/* All Sizes */
+.ui.container {
+  display: block;
+  max-width: 100%;
+}
+
+/* Mobile */
+@media only screen and (max-width: 767.98px) {
+  .ui.ui.ui.container:not(.fluid) {
+    width: auto;
+    margin-left: 1em;
+    margin-right: 1em;
+  }
+  .ui.ui.grid.container {
+    width: auto;
+  }
+  .ui.ui.relaxed.grid.container {
+    width: auto;
+  }
+  .ui.ui.very.relaxed.grid.container {
+    width: auto;
+  }
+}
+
+/* Tablet */
+@media only screen and (min-width: 768px) and (max-width: 991.98px) {
+  .ui.ui.ui.container:not(.fluid) {
+    width: 723px;
+    margin-left: auto;
+    margin-right: auto;
+  }
+  .ui.ui.grid.container {
+    width: calc(723px + 2rem);
+  }
+  .ui.ui.relaxed.grid.container {
+    width: calc(723px + 3rem);
+  }
+  .ui.ui.very.relaxed.grid.container {
+    width: calc(723px + 5rem);
+  }
+}
+
+/* Small Monitor */
+@media only screen and (min-width: 992px) and (max-width: 1199.98px) {
+  .ui.ui.ui.container:not(.fluid) {
+    width: 933px;
+    margin-left: auto;
+    margin-right: auto;
+  }
+  .ui.ui.grid.container {
+    width: calc(933px + 2rem);
+  }
+  .ui.ui.relaxed.grid.container {
+    width: calc(933px + 3rem);
+  }
+  .ui.ui.very.relaxed.grid.container {
+    width: calc(933px + 5rem);
+  }
+}
+
+/* Large Monitor */
+@media only screen and (min-width: 1200px) {
+  .ui.ui.ui.container:not(.fluid) {
+    width: 1127px;
+    margin-left: auto;
+    margin-right: auto;
+  }
+  .ui.ui.grid.container {
+    width: calc(1127px + 2rem);
+  }
+  .ui.ui.relaxed.grid.container {
+    width: calc(1127px + 3rem);
+  }
+  .ui.ui.very.relaxed.grid.container {
+    width: calc(1127px + 5rem);
+  }
+}
+
+
+/*******************************
+             Types
+*******************************/
+
+
+/* Text Container */
+.ui.text.container {
+  font-family: 'Lato', 'Helvetica Neue', Arial, Helvetica, sans-serif;
+  max-width: 700px;
+  line-height: 1.5;
+  font-size: 1.14285714rem;
+}
+
+/* Fluid */
+.ui.fluid.container {
+  width: 100%;
+}
+
+
+/*******************************
+           Variations
+*******************************/
+
+.ui[class*="left aligned"].container {
+  text-align: left;
+}
+.ui[class*="center aligned"].container {
+  text-align: center;
+}
+.ui[class*="right aligned"].container {
+  text-align: right;
+}
+.ui.justified.container {
+  text-align: justify;
+  -webkit-hyphens: auto;
+      -ms-hyphens: auto;
+          hyphens: auto;
+}
+
+
+/*******************************
+         Theme Overrides
+*******************************/
+
+
+
+/*******************************
+         Site Overrides
+*******************************/
+

+ 9 - 0
website/semantic/dist/components/container.min.css

@@ -0,0 +1,9 @@
+/*!
+ * # Fomantic-UI - Container
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */.ui.container{display:block;max-width:100%}@media only screen and (max-width:767.98px){.ui.ui.ui.container:not(.fluid){width:auto;margin-left:1em;margin-right:1em}.ui.ui.grid.container{width:auto}.ui.ui.relaxed.grid.container{width:auto}.ui.ui.very.relaxed.grid.container{width:auto}}@media only screen and (min-width:768px) and (max-width:991.98px){.ui.ui.ui.container:not(.fluid){width:723px;margin-left:auto;margin-right:auto}.ui.ui.grid.container{width:calc(723px + 2rem)}.ui.ui.relaxed.grid.container{width:calc(723px + 3rem)}.ui.ui.very.relaxed.grid.container{width:calc(723px + 5rem)}}@media only screen and (min-width:992px) and (max-width:1199.98px){.ui.ui.ui.container:not(.fluid){width:933px;margin-left:auto;margin-right:auto}.ui.ui.grid.container{width:calc(933px + 2rem)}.ui.ui.relaxed.grid.container{width:calc(933px + 3rem)}.ui.ui.very.relaxed.grid.container{width:calc(933px + 5rem)}}@media only screen and (min-width:1200px){.ui.ui.ui.container:not(.fluid){width:1127px;margin-left:auto;margin-right:auto}.ui.ui.grid.container{width:calc(1127px + 2rem)}.ui.ui.relaxed.grid.container{width:calc(1127px + 3rem)}.ui.ui.very.relaxed.grid.container{width:calc(1127px + 5rem)}}.ui.text.container{font-family:Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;max-width:700px;line-height:1.5;font-size:1.14285714rem}.ui.fluid.container{width:100%}.ui[class*="left aligned"].container{text-align:left}.ui[class*="center aligned"].container{text-align:center}.ui[class*="right aligned"].container{text-align:right}.ui.justified.container{text-align:justify;-webkit-hyphens:auto;-ms-hyphens:auto;hyphens:auto}

+ 464 - 0
website/semantic/dist/components/dimmer.css

@@ -0,0 +1,464 @@
+/*!
+ * # Fomantic-UI - Dimmer
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */
+
+
+/*******************************
+            Dimmer
+*******************************/
+
+.dimmable:not(body) {
+  position: relative;
+}
+.ui.dimmer {
+  display: none;
+  position: absolute;
+  top: 0 !important;
+  left: 0 !important;
+  width: 100%;
+  height: 100%;
+  text-align: center;
+  vertical-align: middle;
+  padding: 1em;
+  background-color: rgba(0, 0, 0, 0.85);
+  opacity: 0;
+  line-height: 1;
+  -webkit-animation-fill-mode: both;
+          animation-fill-mode: both;
+  -webkit-animation-duration: 0.5s;
+          animation-duration: 0.5s;
+  -webkit-transition: background-color 0.5s linear;
+  transition: background-color 0.5s linear;
+  -webkit-box-orient: vertical;
+  -webkit-box-direction: normal;
+      -ms-flex-direction: column;
+          flex-direction: column;
+  -webkit-box-align: center;
+      -ms-flex-align: center;
+          align-items: center;
+  -webkit-box-pack: center;
+      -ms-flex-pack: center;
+          justify-content: center;
+  -webkit-user-select: none;
+     -moz-user-select: none;
+      -ms-user-select: none;
+          user-select: none;
+  will-change: opacity;
+  z-index: 1000;
+}
+
+/* Dimmer Content */
+.ui.dimmer > .content {
+  -webkit-user-select: text;
+     -moz-user-select: text;
+      -ms-user-select: text;
+          user-select: text;
+  color: #FFFFFF;
+}
+
+/* Loose Coupling */
+.ui.segment > .ui.dimmer {
+  border-radius: inherit !important;
+}
+
+/* Scrollbars */
+.ui.dimmer:not(.inverted)::-webkit-scrollbar-track {
+  background: rgba(255, 255, 255, 0.1);
+}
+.ui.dimmer:not(.inverted)::-webkit-scrollbar-thumb {
+  background: rgba(255, 255, 255, 0.25);
+}
+.ui.dimmer:not(.inverted)::-webkit-scrollbar-thumb:window-inactive {
+  background: rgba(255, 255, 255, 0.15);
+}
+.ui.dimmer:not(.inverted)::-webkit-scrollbar-thumb:hover {
+  background: rgba(255, 255, 255, 0.35);
+}
+
+
+/*******************************
+            States
+*******************************/
+
+
+/* Animating */
+.animating.dimmable:not(body),
+.dimmed.dimmable:not(body) {
+  overflow: hidden;
+}
+
+/* Animating / Active / Visible */
+.dimmed.dimmable > .ui.animating.dimmer,
+.dimmed.dimmable > .ui.visible.dimmer,
+.ui.active.dimmer {
+  display: -webkit-box;
+  display: -ms-flexbox;
+  display: flex;
+  opacity: 1;
+}
+
+/* Disabled */
+.ui.disabled.dimmer {
+  width: 0 !important;
+  height: 0 !important;
+}
+
+
+/*******************************
+           Variations
+*******************************/
+
+
+/*--------------
+    Legacy
+---------------*/
+
+
+/* Animating / Active / Visible */
+.dimmed.dimmable > .ui.animating.legacy.dimmer,
+.dimmed.dimmable > .ui.visible.legacy.dimmer,
+.ui.active.legacy.dimmer {
+  display: block;
+}
+
+/*--------------
+    Alignment
+---------------*/
+
+.ui[class*="top aligned"].dimmer {
+  -webkit-box-pack: start;
+      -ms-flex-pack: start;
+          justify-content: flex-start;
+}
+.ui[class*="bottom aligned"].dimmer {
+  -webkit-box-pack: end;
+      -ms-flex-pack: end;
+          justify-content: flex-end;
+}
+
+/*--------------
+      Page
+---------------*/
+
+.ui.page.dimmer {
+  position: fixed;
+  -webkit-transform-style: '';
+          transform-style: '';
+  -webkit-perspective: 2000px;
+          perspective: 2000px;
+  -webkit-transform-origin: center center;
+          transform-origin: center center;
+}
+body.animating.in.dimmable,
+body.dimmed.dimmable {
+  overflow: hidden;
+}
+body.dimmable > .dimmer {
+  position: fixed;
+}
+
+/*--------------
+    Blurring
+---------------*/
+
+.blurring.dimmable > :not(.dimmer) {
+  -webkit-filter: initial;
+          filter: initial;
+  -webkit-transition: 800ms -webkit-filter ease;
+  transition: 800ms -webkit-filter ease;
+  transition: 800ms filter ease;
+  transition: 800ms filter ease, 800ms -webkit-filter ease;
+}
+.blurring.dimmed.dimmable > :not(.dimmer):not(.popup) {
+  -webkit-filter: blur(5px) grayscale(0.7);
+          filter: blur(5px) grayscale(0.7);
+}
+
+/* Dimmer Color */
+.blurring.dimmable > .dimmer {
+  background-color: rgba(0, 0, 0, 0.6);
+}
+.blurring.dimmable > .inverted.dimmer {
+  background-color: rgba(255, 255, 255, 0.6);
+}
+
+/*--------------
+    Aligned
+---------------*/
+
+.ui.dimmer > .top.aligned.content > * {
+  vertical-align: top;
+}
+.ui.dimmer > .bottom.aligned.content > * {
+  vertical-align: bottom;
+}
+
+/*--------------
+    Shades
+---------------*/
+
+.medium.medium.medium.medium.medium.dimmer {
+  background-color: rgba(0, 0, 0, 0.65);
+}
+.light.light.light.light.light.dimmer {
+  background-color: rgba(0, 0, 0, 0.45);
+}
+.very.light.light.light.light.dimmer {
+  background-color: rgba(0, 0, 0, 0.25);
+}
+
+/*--------------
+    Inverted
+---------------*/
+
+.ui.inverted.dimmer {
+  background-color: rgba(255, 255, 255, 0.85);
+}
+.ui.inverted.dimmer > .content,
+.ui.inverted.dimmer > .content > * {
+  color: #000000;
+}
+
+/*--------------
+ Inverted Shades
+---------------*/
+
+.medium.medium.medium.medium.medium.inverted.dimmer {
+  background-color: rgba(255, 255, 255, 0.65);
+}
+.light.light.light.light.light.inverted.dimmer {
+  background-color: rgba(255, 255, 255, 0.45);
+}
+.very.light.light.light.light.inverted.dimmer {
+  background-color: rgba(255, 255, 255, 0.25);
+}
+
+/*--------------
+     Simple
+---------------*/
+
+
+/* Displays without javascript */
+.ui.simple.dimmer {
+  display: block;
+  overflow: hidden;
+  opacity: 0;
+  width: 0;
+  height: 0;
+  z-index: -100;
+  background-color: rgba(0, 0, 0, 0);
+}
+.dimmed.dimmable > .ui.simple.dimmer {
+  overflow: visible;
+  opacity: 1;
+  width: 100%;
+  height: 100%;
+  background-color: rgba(0, 0, 0, 0.85);
+  z-index: 1;
+}
+.ui.simple.inverted.dimmer {
+  background-color: rgba(255, 255, 255, 0);
+}
+.dimmed.dimmable > .ui.simple.inverted.dimmer {
+  background-color: rgba(255, 255, 255, 0.85);
+}
+
+/*--------------
+     Partially
+----------------*/
+
+.ui[class*="top dimmer"],
+.ui[class*="center dimmer"],
+.ui[class*="bottom dimmer"] {
+  height: auto;
+}
+.ui[class*="bottom dimmer"] {
+  top: auto !important;
+  bottom: 0;
+}
+.ui[class*="center dimmer"] {
+  top: 50% !important;
+  transform: translateY(-50%);
+  -webkit-transform: translateY(calc(-50% - 0.5px));
+}
+.ui.segment > .ui[class*="top dimmer"] {
+  border-bottom-left-radius: 0 !important;
+  border-bottom-right-radius: 0 !important;
+}
+.ui.segment > .ui[class*="center dimmer"] {
+  border-radius: 0 !important;
+}
+.ui.segment > .ui[class*="bottom dimmer"] {
+  border-top-left-radius: 0 !important;
+  border-top-right-radius: 0 !important;
+}
+.ui[class*="center dimmer"].transition[class*="fade up"].in {
+  -webkit-animation-name: fadeInUpCenter;
+          animation-name: fadeInUpCenter;
+}
+.ui[class*="center dimmer"].transition[class*="fade down"].in {
+  -webkit-animation-name: fadeInDownCenter;
+          animation-name: fadeInDownCenter;
+}
+.ui[class*="center dimmer"].transition[class*="fade up"].out {
+  -webkit-animation-name: fadeOutUpCenter;
+          animation-name: fadeOutUpCenter;
+}
+.ui[class*="center dimmer"].transition[class*="fade down"].out {
+  -webkit-animation-name: fadeOutDownCenter;
+          animation-name: fadeOutDownCenter;
+}
+.ui[class*="center dimmer"].bounce.transition {
+  -webkit-animation-name: bounceCenter;
+          animation-name: bounceCenter;
+}
+@-webkit-keyframes fadeInUpCenter {
+  0% {
+    opacity: 0;
+    transform: translateY(-40%);
+    -webkit-transform: translateY(calc(-40% - 0.5px));
+  }
+  100% {
+    opacity: 1;
+    transform: translateY(-50%);
+    -webkit-transform: translateY(calc(-50% - 0.5px));
+  }
+}
+@keyframes fadeInUpCenter {
+  0% {
+    opacity: 0;
+    transform: translateY(-40%);
+    -webkit-transform: translateY(calc(-40% - 0.5px));
+  }
+  100% {
+    opacity: 1;
+    transform: translateY(-50%);
+    -webkit-transform: translateY(calc(-50% - 0.5px));
+  }
+}
+@-webkit-keyframes fadeInDownCenter {
+  0% {
+    opacity: 0;
+    transform: translateY(-60%);
+    -webkit-transform: translateY(calc(-60% - 0.5px));
+  }
+  100% {
+    opacity: 1;
+    transform: translateY(-50%);
+    -webkit-transform: translateY(calc(-50% - 0.5px));
+  }
+}
+@keyframes fadeInDownCenter {
+  0% {
+    opacity: 0;
+    transform: translateY(-60%);
+    -webkit-transform: translateY(calc(-60% - 0.5px));
+  }
+  100% {
+    opacity: 1;
+    transform: translateY(-50%);
+    -webkit-transform: translateY(calc(-50% - 0.5px));
+  }
+}
+@-webkit-keyframes fadeOutUpCenter {
+  0% {
+    opacity: 1;
+    transform: translateY(-50%);
+    -webkit-transform: translateY(calc(-50% - 0.5px));
+  }
+  100% {
+    opacity: 0;
+    transform: translateY(-45%);
+    -webkit-transform: translateY(calc(-45% - 0.5px));
+  }
+}
+@keyframes fadeOutUpCenter {
+  0% {
+    opacity: 1;
+    transform: translateY(-50%);
+    -webkit-transform: translateY(calc(-50% - 0.5px));
+  }
+  100% {
+    opacity: 0;
+    transform: translateY(-45%);
+    -webkit-transform: translateY(calc(-45% - 0.5px));
+  }
+}
+@-webkit-keyframes fadeOutDownCenter {
+  0% {
+    opacity: 1;
+    transform: translateY(-50%);
+    -webkit-transform: translateY(calc(-50% - 0.5px));
+  }
+  100% {
+    opacity: 0;
+    transform: translateY(-55%);
+    -webkit-transform: translateY(calc(-55% - 0.5px));
+  }
+}
+@keyframes fadeOutDownCenter {
+  0% {
+    opacity: 1;
+    transform: translateY(-50%);
+    -webkit-transform: translateY(calc(-50% - 0.5px));
+  }
+  100% {
+    opacity: 0;
+    transform: translateY(-55%);
+    -webkit-transform: translateY(calc(-55% - 0.5px));
+  }
+}
+@-webkit-keyframes bounceCenter {
+  0%,
+  20%,
+  50%,
+  80%,
+  100% {
+    transform: translateY(-50%);
+    -webkit-transform: translateY(calc(-50% - 0.5px));
+  }
+  40% {
+    -webkit-transform: translateY(calc(-50% - 30px));
+            transform: translateY(calc(-50% - 30px));
+  }
+  60% {
+    -webkit-transform: translateY(calc(-50% - 15px));
+            transform: translateY(calc(-50% - 15px));
+  }
+}
+@keyframes bounceCenter {
+  0%,
+  20%,
+  50%,
+  80%,
+  100% {
+    transform: translateY(-50%);
+    -webkit-transform: translateY(calc(-50% - 0.5px));
+  }
+  40% {
+    -webkit-transform: translateY(calc(-50% - 30px));
+            transform: translateY(calc(-50% - 30px));
+  }
+  60% {
+    -webkit-transform: translateY(calc(-50% - 15px));
+            transform: translateY(calc(-50% - 15px));
+  }
+}
+
+
+/*******************************
+         Theme Overrides
+*******************************/
+
+
+
+/*******************************
+        User Overrides
+*******************************/
+

+ 753 - 0
website/semantic/dist/components/dimmer.js

@@ -0,0 +1,753 @@
+/*!
+ * # Fomantic-UI - Dimmer
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */
+
+;(function ($, window, document, undefined) {
+
+'use strict';
+
+$.isFunction = $.isFunction || function(obj) {
+  return typeof obj === "function" && typeof obj.nodeType !== "number";
+};
+
+window = (typeof window != 'undefined' && window.Math == Math)
+  ? window
+  : (typeof self != 'undefined' && self.Math == Math)
+    ? self
+    : Function('return this')()
+;
+
+$.fn.dimmer = function(parameters) {
+  var
+    $allModules     = $(this),
+
+    time            = new Date().getTime(),
+    performance     = [],
+
+    query           = arguments[0],
+    methodInvoked   = (typeof query == 'string'),
+    queryArguments  = [].slice.call(arguments, 1),
+
+    returnedValue
+  ;
+
+  $allModules
+    .each(function() {
+      var
+        settings        = ( $.isPlainObject(parameters) )
+          ? $.extend(true, {}, $.fn.dimmer.settings, parameters)
+          : $.extend({}, $.fn.dimmer.settings),
+
+        selector        = settings.selector,
+        namespace       = settings.namespace,
+        className       = settings.className,
+        error           = settings.error,
+
+        eventNamespace  = '.' + namespace,
+        moduleNamespace = 'module-' + namespace,
+        moduleSelector  = $allModules.selector || '',
+
+        clickEvent      = ('ontouchstart' in document.documentElement)
+          ? 'touchstart'
+          : 'click',
+
+        $module = $(this),
+        $dimmer,
+        $dimmable,
+
+        element   = this,
+        instance  = $module.data(moduleNamespace),
+        module
+      ;
+
+      module = {
+
+        preinitialize: function() {
+          if( module.is.dimmer() ) {
+
+            $dimmable = $module.parent();
+            $dimmer   = $module;
+          }
+          else {
+            $dimmable = $module;
+            if( module.has.dimmer() ) {
+              if(settings.dimmerName) {
+                $dimmer = $dimmable.find(selector.dimmer).filter('.' + settings.dimmerName);
+              }
+              else {
+                $dimmer = $dimmable.find(selector.dimmer);
+              }
+            }
+            else {
+              $dimmer = module.create();
+            }
+          }
+        },
+
+        initialize: function() {
+          module.debug('Initializing dimmer', settings);
+
+          module.bind.events();
+          module.set.dimmable();
+          module.instantiate();
+        },
+
+        instantiate: function() {
+          module.verbose('Storing instance of module', module);
+          instance = module;
+          $module
+            .data(moduleNamespace, instance)
+          ;
+        },
+
+        destroy: function() {
+          module.verbose('Destroying previous module', $dimmer);
+          module.unbind.events();
+          module.remove.variation();
+          $dimmable
+            .off(eventNamespace)
+          ;
+        },
+
+        bind: {
+          events: function() {
+            if(settings.on == 'hover') {
+              $dimmable
+                .on('mouseenter' + eventNamespace, module.show)
+                .on('mouseleave' + eventNamespace, module.hide)
+              ;
+            }
+            else if(settings.on == 'click') {
+              $dimmable
+                .on(clickEvent + eventNamespace, module.toggle)
+              ;
+            }
+            if( module.is.page() ) {
+              module.debug('Setting as a page dimmer', $dimmable);
+              module.set.pageDimmer();
+            }
+
+            if( module.is.closable() ) {
+              module.verbose('Adding dimmer close event', $dimmer);
+              $dimmable
+                .on(clickEvent + eventNamespace, selector.dimmer, module.event.click)
+              ;
+            }
+          }
+        },
+
+        unbind: {
+          events: function() {
+            $module
+              .removeData(moduleNamespace)
+            ;
+            $dimmable
+              .off(eventNamespace)
+            ;
+          }
+        },
+
+        event: {
+          click: function(event) {
+            module.verbose('Determining if event occured on dimmer', event);
+            if( $dimmer.find(event.target).length === 0 || $(event.target).is(selector.content) ) {
+              module.hide();
+              event.stopImmediatePropagation();
+            }
+          }
+        },
+
+        addContent: function(element) {
+          var
+            $content = $(element)
+          ;
+          module.debug('Add content to dimmer', $content);
+          if($content.parent()[0] !== $dimmer[0]) {
+            $content.detach().appendTo($dimmer);
+          }
+        },
+
+        create: function() {
+          var
+            $element = $( settings.template.dimmer(settings) )
+          ;
+          if(settings.dimmerName) {
+            module.debug('Creating named dimmer', settings.dimmerName);
+            $element.addClass(settings.dimmerName);
+          }
+          $element
+            .appendTo($dimmable)
+          ;
+          return $element;
+        },
+
+        show: function(callback) {
+          callback = $.isFunction(callback)
+            ? callback
+            : function(){}
+          ;
+          module.debug('Showing dimmer', $dimmer, settings);
+          module.set.variation();
+          if( (!module.is.dimmed() || module.is.animating()) && module.is.enabled() ) {
+            module.animate.show(callback);
+            settings.onShow.call(element);
+            settings.onChange.call(element);
+          }
+          else {
+            module.debug('Dimmer is already shown or disabled');
+          }
+        },
+
+        hide: function(callback) {
+          callback = $.isFunction(callback)
+            ? callback
+            : function(){}
+          ;
+          if( module.is.dimmed() || module.is.animating() ) {
+            module.debug('Hiding dimmer', $dimmer);
+            module.animate.hide(callback);
+            settings.onHide.call(element);
+            settings.onChange.call(element);
+          }
+          else {
+            module.debug('Dimmer is not visible');
+          }
+        },
+
+        toggle: function() {
+          module.verbose('Toggling dimmer visibility', $dimmer);
+          if( !module.is.dimmed() ) {
+            module.show();
+          }
+          else {
+            if ( module.is.closable() ) {
+              module.hide();
+            }
+          }
+        },
+
+        animate: {
+          show: function(callback) {
+            callback = $.isFunction(callback)
+              ? callback
+              : function(){}
+            ;
+            if(settings.useCSS && $.fn.transition !== undefined && $dimmer.transition('is supported')) {
+              if(settings.useFlex) {
+                module.debug('Using flex dimmer');
+                module.remove.legacy();
+              }
+              else {
+                module.debug('Using legacy non-flex dimmer');
+                module.set.legacy();
+              }
+              if(settings.opacity !== 'auto') {
+                module.set.opacity();
+              }
+              $dimmer
+                .transition({
+                  displayType : settings.useFlex
+                    ? 'flex'
+                    : 'block',
+                  animation   : settings.transition + ' in',
+                  queue       : false,
+                  duration    : module.get.duration(),
+                  useFailSafe : true,
+                  onStart     : function() {
+                    module.set.dimmed();
+                  },
+                  onComplete  : function() {
+                    module.set.active();
+                    callback();
+                  }
+                })
+              ;
+            }
+            else {
+              module.verbose('Showing dimmer animation with javascript');
+              module.set.dimmed();
+              if(settings.opacity == 'auto') {
+                settings.opacity = 0.8;
+              }
+              $dimmer
+                .stop()
+                .css({
+                  opacity : 0,
+                  width   : '100%',
+                  height  : '100%'
+                })
+                .fadeTo(module.get.duration(), settings.opacity, function() {
+                  $dimmer.removeAttr('style');
+                  module.set.active();
+                  callback();
+                })
+              ;
+            }
+          },
+          hide: function(callback) {
+            callback = $.isFunction(callback)
+              ? callback
+              : function(){}
+            ;
+            if(settings.useCSS && $.fn.transition !== undefined && $dimmer.transition('is supported')) {
+              module.verbose('Hiding dimmer with css');
+              $dimmer
+                .transition({
+                  displayType : settings.useFlex
+                    ? 'flex'
+                    : 'block',
+                  animation   : settings.transition + ' out',
+                  queue       : false,
+                  duration    : module.get.duration(),
+                  useFailSafe : true,
+                  onComplete  : function() {
+                    module.remove.dimmed();
+                    module.remove.variation();
+                    module.remove.active();
+                    callback();
+                  }
+                })
+              ;
+            }
+            else {
+              module.verbose('Hiding dimmer with javascript');
+              $dimmer
+                .stop()
+                .fadeOut(module.get.duration(), function() {
+                  module.remove.dimmed();
+                  module.remove.active();
+                  $dimmer.removeAttr('style');
+                  callback();
+                })
+              ;
+            }
+          }
+        },
+
+        get: {
+          dimmer: function() {
+            return $dimmer;
+          },
+          duration: function() {
+            if(typeof settings.duration == 'object') {
+              if( module.is.active() ) {
+                return settings.duration.hide;
+              }
+              else {
+                return settings.duration.show;
+              }
+            }
+            return settings.duration;
+          }
+        },
+
+        has: {
+          dimmer: function() {
+            if(settings.dimmerName) {
+              return ($module.find(selector.dimmer).filter('.' + settings.dimmerName).length > 0);
+            }
+            else {
+              return ( $module.find(selector.dimmer).length > 0 );
+            }
+          }
+        },
+
+        is: {
+          active: function() {
+            return $dimmer.hasClass(className.active);
+          },
+          animating: function() {
+            return ( $dimmer.is(':animated') || $dimmer.hasClass(className.animating) );
+          },
+          closable: function() {
+            if(settings.closable == 'auto') {
+              if(settings.on == 'hover') {
+                return false;
+              }
+              return true;
+            }
+            return settings.closable;
+          },
+          dimmer: function() {
+            return $module.hasClass(className.dimmer);
+          },
+          dimmable: function() {
+            return $module.hasClass(className.dimmable);
+          },
+          dimmed: function() {
+            return $dimmable.hasClass(className.dimmed);
+          },
+          disabled: function() {
+            return $dimmable.hasClass(className.disabled);
+          },
+          enabled: function() {
+            return !module.is.disabled();
+          },
+          page: function () {
+            return $dimmable.is('body');
+          },
+          pageDimmer: function() {
+            return $dimmer.hasClass(className.pageDimmer);
+          }
+        },
+
+        can: {
+          show: function() {
+            return !$dimmer.hasClass(className.disabled);
+          }
+        },
+
+        set: {
+          opacity: function(opacity) {
+            var
+              color      = $dimmer.css('background-color'),
+              colorArray = color.split(','),
+              isRGB      = (colorArray && colorArray.length == 3),
+              isRGBA     = (colorArray && colorArray.length == 4)
+            ;
+            opacity    = settings.opacity === 0 ? 0 : settings.opacity || opacity;
+            if(isRGB || isRGBA) {
+              colorArray[3] = opacity + ')';
+              color         = colorArray.join(',');
+            }
+            else {
+              color = 'rgba(0, 0, 0, ' + opacity + ')';
+            }
+            module.debug('Setting opacity to', opacity);
+            $dimmer.css('background-color', color);
+          },
+          legacy: function() {
+            $dimmer.addClass(className.legacy);
+          },
+          active: function() {
+            $dimmer.addClass(className.active);
+          },
+          dimmable: function() {
+            $dimmable.addClass(className.dimmable);
+          },
+          dimmed: function() {
+            $dimmable.addClass(className.dimmed);
+          },
+          pageDimmer: function() {
+            $dimmer.addClass(className.pageDimmer);
+          },
+          disabled: function() {
+            $dimmer.addClass(className.disabled);
+          },
+          variation: function(variation) {
+            variation = variation || settings.variation;
+            if(variation) {
+              $dimmer.addClass(variation);
+            }
+          }
+        },
+
+        remove: {
+          active: function() {
+            $dimmer
+              .removeClass(className.active)
+            ;
+          },
+          legacy: function() {
+            $dimmer.removeClass(className.legacy);
+          },
+          dimmed: function() {
+            $dimmable.removeClass(className.dimmed);
+          },
+          disabled: function() {
+            $dimmer.removeClass(className.disabled);
+          },
+          variation: function(variation) {
+            variation = variation || settings.variation;
+            if(variation) {
+              $dimmer.removeClass(variation);
+            }
+          }
+        },
+
+        setting: function(name, value) {
+          module.debug('Changing setting', name, value);
+          if( $.isPlainObject(name) ) {
+            $.extend(true, settings, name);
+          }
+          else if(value !== undefined) {
+            if($.isPlainObject(settings[name])) {
+              $.extend(true, settings[name], value);
+            }
+            else {
+              settings[name] = value;
+            }
+          }
+          else {
+            return settings[name];
+          }
+        },
+        internal: function(name, value) {
+          if( $.isPlainObject(name) ) {
+            $.extend(true, module, name);
+          }
+          else if(value !== undefined) {
+            module[name] = value;
+          }
+          else {
+            return module[name];
+          }
+        },
+        debug: function() {
+          if(!settings.silent && settings.debug) {
+            if(settings.performance) {
+              module.performance.log(arguments);
+            }
+            else {
+              module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
+              module.debug.apply(console, arguments);
+            }
+          }
+        },
+        verbose: function() {
+          if(!settings.silent && settings.verbose && settings.debug) {
+            if(settings.performance) {
+              module.performance.log(arguments);
+            }
+            else {
+              module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
+              module.verbose.apply(console, arguments);
+            }
+          }
+        },
+        error: function() {
+          if(!settings.silent) {
+            module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
+            module.error.apply(console, arguments);
+          }
+        },
+        performance: {
+          log: function(message) {
+            var
+              currentTime,
+              executionTime,
+              previousTime
+            ;
+            if(settings.performance) {
+              currentTime   = new Date().getTime();
+              previousTime  = time || currentTime;
+              executionTime = currentTime - previousTime;
+              time          = currentTime;
+              performance.push({
+                'Name'           : message[0],
+                'Arguments'      : [].slice.call(message, 1) || '',
+                'Element'        : element,
+                'Execution Time' : executionTime
+              });
+            }
+            clearTimeout(module.performance.timer);
+            module.performance.timer = setTimeout(module.performance.display, 500);
+          },
+          display: function() {
+            var
+              title = settings.name + ':',
+              totalTime = 0
+            ;
+            time = false;
+            clearTimeout(module.performance.timer);
+            $.each(performance, function(index, data) {
+              totalTime += data['Execution Time'];
+            });
+            title += ' ' + totalTime + 'ms';
+            if(moduleSelector) {
+              title += ' \'' + moduleSelector + '\'';
+            }
+            if($allModules.length > 1) {
+              title += ' ' + '(' + $allModules.length + ')';
+            }
+            if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
+              console.groupCollapsed(title);
+              if(console.table) {
+                console.table(performance);
+              }
+              else {
+                $.each(performance, function(index, data) {
+                  console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
+                });
+              }
+              console.groupEnd();
+            }
+            performance = [];
+          }
+        },
+        invoke: function(query, passedArguments, context) {
+          var
+            object = instance,
+            maxDepth,
+            found,
+            response
+          ;
+          passedArguments = passedArguments || queryArguments;
+          context         = element         || context;
+          if(typeof query == 'string' && object !== undefined) {
+            query    = query.split(/[\. ]/);
+            maxDepth = query.length - 1;
+            $.each(query, function(depth, value) {
+              var camelCaseValue = (depth != maxDepth)
+                ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
+                : query
+              ;
+              if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
+                object = object[camelCaseValue];
+              }
+              else if( object[camelCaseValue] !== undefined ) {
+                found = object[camelCaseValue];
+                return false;
+              }
+              else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
+                object = object[value];
+              }
+              else if( object[value] !== undefined ) {
+                found = object[value];
+                return false;
+              }
+              else {
+                module.error(error.method, query);
+                return false;
+              }
+            });
+          }
+          if ( $.isFunction( found ) ) {
+            response = found.apply(context, passedArguments);
+          }
+          else if(found !== undefined) {
+            response = found;
+          }
+          if(Array.isArray(returnedValue)) {
+            returnedValue.push(response);
+          }
+          else if(returnedValue !== undefined) {
+            returnedValue = [returnedValue, response];
+          }
+          else if(response !== undefined) {
+            returnedValue = response;
+          }
+          return found;
+        }
+      };
+
+      module.preinitialize();
+
+      if(methodInvoked) {
+        if(instance === undefined) {
+          module.initialize();
+        }
+        module.invoke(query);
+      }
+      else {
+        if(instance !== undefined) {
+          instance.invoke('destroy');
+        }
+        module.initialize();
+      }
+    })
+  ;
+
+  return (returnedValue !== undefined)
+    ? returnedValue
+    : this
+  ;
+};
+
+$.fn.dimmer.settings = {
+
+  name        : 'Dimmer',
+  namespace   : 'dimmer',
+
+  silent      : false,
+  debug       : false,
+  verbose     : false,
+  performance : true,
+
+  // whether should use flex layout
+  useFlex     : true,
+
+  // name to distinguish between multiple dimmers in context
+  dimmerName  : false,
+
+  // whether to add a variation type
+  variation   : false,
+
+  // whether to bind close events
+  closable    : 'auto',
+
+  // whether to use css animations
+  useCSS      : true,
+
+  // css animation to use
+  transition  : 'fade',
+
+  // event to bind to
+  on          : false,
+
+  // overriding opacity value
+  opacity     : 'auto',
+
+  // transition durations
+  duration    : {
+    show : 500,
+    hide : 500
+  },
+// whether the dynamically created dimmer should have a loader
+  displayLoader: false,
+  loaderText  : false,
+  loaderVariation : '',
+
+  onChange    : function(){},
+  onShow      : function(){},
+  onHide      : function(){},
+
+  error   : {
+    method   : 'The method you called is not defined.'
+  },
+
+  className : {
+    active     : 'active',
+    animating  : 'animating',
+    dimmable   : 'dimmable',
+    dimmed     : 'dimmed',
+    dimmer     : 'dimmer',
+    disabled   : 'disabled',
+    hide       : 'hide',
+    legacy     : 'legacy',
+    pageDimmer : 'page',
+    show       : 'show',
+    loader     : 'ui loader'
+  },
+
+  selector: {
+    dimmer   : '> .ui.dimmer',
+    content  : '.ui.dimmer > .content, .ui.dimmer > .content > .center'
+  },
+
+  template: {
+    dimmer: function(settings) {
+        var d = $('<div/>').addClass('ui dimmer'),l;
+        if(settings.displayLoader) {
+          l = $('<div/>')
+              .addClass(settings.className.loader)
+              .addClass(settings.loaderVariation);
+          if(!!settings.loaderText){
+            l.text(settings.loaderText);
+            l.addClass('text');
+          }
+          d.append(l);
+        }
+        return d;
+    }
+  }
+
+};
+
+})( jQuery, window, document );

Разлика између датотеке није приказан због своје велике величине
+ 8 - 0
website/semantic/dist/components/dimmer.min.css


Разлика између датотеке није приказан због своје велике величине
+ 10 - 0
website/semantic/dist/components/dimmer.min.js


+ 276 - 0
website/semantic/dist/components/divider.css

@@ -0,0 +1,276 @@
+/*!
+ * # Fomantic-UI - Divider
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */
+
+
+/*******************************
+            Divider
+*******************************/
+
+.ui.divider {
+  margin: 1rem 0;
+  line-height: 1;
+  height: 0;
+  font-weight: bold;
+  text-transform: uppercase;
+  letter-spacing: 0.05em;
+  color: rgba(0, 0, 0, 0.85);
+  -webkit-user-select: none;
+     -moz-user-select: none;
+      -ms-user-select: none;
+          user-select: none;
+  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+}
+
+/*--------------
+      Basic
+---------------*/
+
+.ui.divider:not(.vertical):not(.horizontal) {
+  border-top: 1px solid rgba(34, 36, 38, 0.15);
+  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+}
+
+/*--------------
+    Coupling
+---------------*/
+
+
+/* Allow divider between each column row */
+.ui.grid > .column + .divider,
+.ui.grid > .row > .column + .divider {
+  left: auto;
+}
+
+/*--------------
+   Horizontal
+---------------*/
+
+.ui.horizontal.divider {
+  display: table;
+  white-space: nowrap;
+  height: auto;
+  margin: '';
+  line-height: 1;
+  text-align: center;
+}
+.ui.horizontal.divider:before,
+.ui.horizontal.divider:after {
+  content: '';
+  display: table-cell;
+  position: relative;
+  top: 50%;
+  width: 50%;
+  background-repeat: no-repeat;
+}
+.ui.horizontal.divider:before {
+  background-position: right 1em top 50%;
+}
+.ui.horizontal.divider:after {
+  background-position: left 1em top 50%;
+}
+
+/*--------------
+    Vertical
+---------------*/
+
+.ui.vertical.divider {
+  position: absolute;
+  z-index: 2;
+  top: 50%;
+  left: 50%;
+  margin: 0;
+  padding: 0;
+  width: auto;
+  height: 50%;
+  line-height: 0;
+  text-align: center;
+  -webkit-transform: translateX(-50%);
+          transform: translateX(-50%);
+}
+.ui.vertical.divider:before,
+.ui.vertical.divider:after {
+  position: absolute;
+  left: 50%;
+  content: '';
+  z-index: 3;
+  border-left: 1px solid rgba(34, 36, 38, 0.15);
+  border-right: 1px solid rgba(255, 255, 255, 0.1);
+  width: 0;
+  height: calc(100% - 1rem);
+}
+.ui.vertical.divider:before {
+  top: -100%;
+}
+.ui.vertical.divider:after {
+  top: auto;
+  bottom: 0;
+}
+
+/* Inside grid */
+@media only screen and (max-width: 767.98px) {
+  .ui.stackable.grid .ui.vertical.divider,
+  .ui.grid .stackable.row .ui.vertical.divider {
+    display: table;
+    white-space: nowrap;
+    height: auto;
+    margin: '';
+    overflow: hidden;
+    line-height: 1;
+    text-align: center;
+    position: static;
+    top: 0;
+    left: 0;
+    -webkit-transform: none;
+            transform: none;
+  }
+  .ui.stackable.grid .ui.vertical.divider:before,
+  .ui.grid .stackable.row .ui.vertical.divider:before,
+  .ui.stackable.grid .ui.vertical.divider:after,
+  .ui.grid .stackable.row .ui.vertical.divider:after {
+    left: 0;
+    border-left: none;
+    border-right: none;
+    content: '';
+    display: table-cell;
+    position: relative;
+    top: 50%;
+    width: 50%;
+    background-repeat: no-repeat;
+  }
+  .ui.stackable.grid .ui.vertical.divider:before,
+  .ui.grid .stackable.row .ui.vertical.divider:before {
+    background-position: right 1em top 50%;
+  }
+  .ui.stackable.grid .ui.vertical.divider:after,
+  .ui.grid .stackable.row .ui.vertical.divider:after {
+    background-position: left 1em top 50%;
+  }
+}
+
+/*--------------
+      Icon
+---------------*/
+
+.ui.divider > .icon {
+  margin: 0;
+  font-size: 1rem;
+  height: 1em;
+  vertical-align: middle;
+}
+
+/*--------------
+     Header
+---------------*/
+
+.ui.horizontal.divider[class*="left aligned"]:before {
+  display: none;
+}
+.ui.horizontal.divider[class*="left aligned"]:after {
+  width: 100%;
+}
+.ui.horizontal.divider[class*="right aligned"]:before {
+  width: 100%;
+}
+.ui.horizontal.divider[class*="right aligned"]:after {
+  display: none;
+}
+
+
+/*******************************
+          Variations
+*******************************/
+
+
+/*--------------
+    Hidden
+---------------*/
+
+.ui.hidden.divider {
+  border-color: transparent !important;
+}
+.ui.hidden.divider:before,
+.ui.hidden.divider:after {
+  display: none;
+}
+
+/*--------------
+    Inverted
+---------------*/
+
+.ui.divider.inverted,
+.ui.vertical.inverted.divider,
+.ui.horizontal.inverted.divider {
+  color: #FFFFFF;
+}
+.ui.divider.inverted,
+.ui.divider.inverted:after,
+.ui.divider.inverted:before {
+  border-top-color: rgba(34, 36, 38, 0.15) !important;
+  border-left-color: rgba(34, 36, 38, 0.15) !important;
+  border-bottom-color: rgba(255, 255, 255, 0.15) !important;
+  border-right-color: rgba(255, 255, 255, 0.15) !important;
+}
+
+/*--------------
+    Fitted
+---------------*/
+
+.ui.fitted.divider {
+  margin: 0;
+}
+
+/*--------------
+    Clearing
+---------------*/
+
+.ui.clearing.divider {
+  clear: both;
+}
+
+/*--------------
+    Section
+---------------*/
+
+.ui.section.divider {
+  margin-top: 2rem;
+  margin-bottom: 2rem;
+}
+
+/*--------------
+     Sizes
+---------------*/
+
+.ui.divider {
+  font-size: 1rem;
+}
+
+
+/*******************************
+         Theme Overrides
+*******************************/
+
+.ui.horizontal.divider:before,
+.ui.horizontal.divider:after {
+  background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABaAAAAACCAYAAACuTHuKAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyFpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDE0IDc5LjE1MTQ4MSwgMjAxMy8wMy8xMy0xMjowOToxNSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo1OThBRDY4OUNDMTYxMUU0OUE3NUVGOEJDMzMzMjE2NyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo1OThBRDY4QUNDMTYxMUU0OUE3NUVGOEJDMzMzMjE2NyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjU5OEFENjg3Q0MxNjExRTQ5QTc1RUY4QkMzMzMyMTY3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjU5OEFENjg4Q0MxNjExRTQ5QTc1RUY4QkMzMzMyMTY3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+VU513gAAADVJREFUeNrs0DENACAQBDBIWLGBJQby/mUcJn5sJXQmOQMAAAAAAJqt+2prAAAAAACg2xdgANk6BEVuJgyMAAAAAElFTkSuQmCC');
+}
+@media only screen and (max-width: 767px) {
+  .ui.stackable.grid .ui.vertical.divider:before,
+  .ui.grid .stackable.row .ui.vertical.divider:before,
+  .ui.stackable.grid .ui.vertical.divider:after,
+  .ui.grid .stackable.row .ui.vertical.divider:after {
+    background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABaAAAAACCAYAAACuTHuKAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyFpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDE0IDc5LjE1MTQ4MSwgMjAxMy8wMy8xMy0xMjowOToxNSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo1OThBRDY4OUNDMTYxMUU0OUE3NUVGOEJDMzMzMjE2NyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo1OThBRDY4QUNDMTYxMUU0OUE3NUVGOEJDMzMzMjE2NyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjU5OEFENjg3Q0MxNjExRTQ5QTc1RUY4QkMzMzMyMTY3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjU5OEFENjg4Q0MxNjExRTQ5QTc1RUY4QkMzMzMyMTY3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+VU513gAAADVJREFUeNrs0DENACAQBDBIWLGBJQby/mUcJn5sJXQmOQMAAAAAAJqt+2prAAAAAACg2xdgANk6BEVuJgyMAAAAAElFTkSuQmCC');
+  }
+}
+
+
+/*******************************
+         Site Overrides
+*******************************/
+

Разлика између датотеке није приказан због своје велике величине
+ 8 - 0
website/semantic/dist/components/divider.min.css


Разлика између датотеке није приказан због своје велике величине
+ 1776 - 0
website/semantic/dist/components/dropdown.css


+ 4177 - 0
website/semantic/dist/components/dropdown.js

@@ -0,0 +1,4177 @@
+/*!
+ * # Fomantic-UI - Dropdown
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */
+
+;(function ($, window, document, undefined) {
+
+'use strict';
+
+$.isFunction = $.isFunction || function(obj) {
+  return typeof obj === "function" && typeof obj.nodeType !== "number";
+};
+
+window = (typeof window != 'undefined' && window.Math == Math)
+  ? window
+  : (typeof self != 'undefined' && self.Math == Math)
+    ? self
+    : Function('return this')()
+;
+
+$.fn.dropdown = function(parameters) {
+  var
+    $allModules    = $(this),
+    $document      = $(document),
+
+    moduleSelector = $allModules.selector || '',
+
+    hasTouch       = ('ontouchstart' in document.documentElement),
+    time           = new Date().getTime(),
+    performance    = [],
+
+    query          = arguments[0],
+    methodInvoked  = (typeof query == 'string'),
+    queryArguments = [].slice.call(arguments, 1),
+    returnedValue
+  ;
+
+  $allModules
+    .each(function(elementIndex) {
+      var
+        settings          = ( $.isPlainObject(parameters) )
+          ? $.extend(true, {}, $.fn.dropdown.settings, parameters)
+          : $.extend({}, $.fn.dropdown.settings),
+
+        className       = settings.className,
+        message         = settings.message,
+        fields          = settings.fields,
+        keys            = settings.keys,
+        metadata        = settings.metadata,
+        namespace       = settings.namespace,
+        regExp          = settings.regExp,
+        selector        = settings.selector,
+        error           = settings.error,
+        templates       = settings.templates,
+
+        eventNamespace  = '.' + namespace,
+        moduleNamespace = 'module-' + namespace,
+
+        $module         = $(this),
+        $context        = $(settings.context),
+        $text           = $module.find(selector.text),
+        $search         = $module.find(selector.search),
+        $sizer          = $module.find(selector.sizer),
+        $input          = $module.find(selector.input),
+        $icon           = $module.find(selector.icon),
+        $clear          = $module.find(selector.clearIcon),
+
+        $combo = ($module.prev().find(selector.text).length > 0)
+          ? $module.prev().find(selector.text)
+          : $module.prev(),
+
+        $menu           = $module.children(selector.menu),
+        $item           = $menu.find(selector.item),
+        $divider        = settings.hideDividers ? $item.parent().children(selector.divider) : $(),
+
+        activated       = false,
+        itemActivated   = false,
+        internalChange  = false,
+        iconClicked     = false,
+        element         = this,
+        instance        = $module.data(moduleNamespace),
+
+        selectActionActive,
+        initialLoad,
+        pageLostFocus,
+        willRefocus,
+        elementNamespace,
+        id,
+        selectObserver,
+        menuObserver,
+        module
+      ;
+
+      module = {
+
+        initialize: function() {
+          module.debug('Initializing dropdown', settings);
+
+          if( module.is.alreadySetup() ) {
+            module.setup.reference();
+          }
+          else {
+            if (settings.ignoreDiacritics && !String.prototype.normalize) {
+              settings.ignoreDiacritics = false;
+              module.error(error.noNormalize, element);
+            }
+
+            module.setup.layout();
+
+            if(settings.values) {
+              module.change.values(settings.values);
+            }
+
+            module.refreshData();
+
+            module.save.defaults();
+            module.restore.selected();
+
+            module.create.id();
+            module.bind.events();
+
+            module.observeChanges();
+            module.instantiate();
+          }
+
+        },
+
+        instantiate: function() {
+          module.verbose('Storing instance of dropdown', module);
+          instance = module;
+          $module
+            .data(moduleNamespace, module)
+          ;
+        },
+
+        destroy: function() {
+          module.verbose('Destroying previous dropdown', $module);
+          module.remove.tabbable();
+          module.remove.active();
+          $menu.transition('stop all');
+          $menu.removeClass(className.visible).addClass(className.hidden);
+          $module
+            .off(eventNamespace)
+            .removeData(moduleNamespace)
+          ;
+          $menu
+            .off(eventNamespace)
+          ;
+          $document
+            .off(elementNamespace)
+          ;
+          module.disconnect.menuObserver();
+          module.disconnect.selectObserver();
+        },
+
+        observeChanges: function() {
+          if('MutationObserver' in window) {
+            selectObserver = new MutationObserver(module.event.select.mutation);
+            menuObserver   = new MutationObserver(module.event.menu.mutation);
+            module.debug('Setting up mutation observer', selectObserver, menuObserver);
+            module.observe.select();
+            module.observe.menu();
+          }
+        },
+
+        disconnect: {
+          menuObserver: function() {
+            if(menuObserver) {
+              menuObserver.disconnect();
+            }
+          },
+          selectObserver: function() {
+            if(selectObserver) {
+              selectObserver.disconnect();
+            }
+          }
+        },
+        observe: {
+          select: function() {
+            if(module.has.input() && selectObserver) {
+              selectObserver.observe($module[0], {
+                childList : true,
+                subtree   : true
+              });
+            }
+          },
+          menu: function() {
+            if(module.has.menu() && menuObserver) {
+              menuObserver.observe($menu[0], {
+                childList : true,
+                subtree   : true
+              });
+            }
+          }
+        },
+
+        create: {
+          id: function() {
+            id = (Math.random().toString(16) + '000000000').substr(2, 8);
+            elementNamespace = '.' + id;
+            module.verbose('Creating unique id for element', id);
+          },
+          userChoice: function(values) {
+            var
+              $userChoices,
+              $userChoice,
+              isUserValue,
+              html
+            ;
+            values = values || module.get.userValues();
+            if(!values) {
+              return false;
+            }
+            values = Array.isArray(values)
+              ? values
+              : [values]
+            ;
+            $.each(values, function(index, value) {
+              if(module.get.item(value) === false) {
+                html         = settings.templates.addition( module.add.variables(message.addResult, value) );
+                $userChoice  = $('<div />')
+                  .html(html)
+                  .attr('data-' + metadata.value, value)
+                  .attr('data-' + metadata.text, value)
+                  .addClass(className.addition)
+                  .addClass(className.item)
+                ;
+                if(settings.hideAdditions) {
+                  $userChoice.addClass(className.hidden);
+                }
+                $userChoices = ($userChoices === undefined)
+                  ? $userChoice
+                  : $userChoices.add($userChoice)
+                ;
+                module.verbose('Creating user choices for value', value, $userChoice);
+              }
+            });
+            return $userChoices;
+          },
+          userLabels: function(value) {
+            var
+              userValues = module.get.userValues()
+            ;
+            if(userValues) {
+              module.debug('Adding user labels', userValues);
+              $.each(userValues, function(index, value) {
+                module.verbose('Adding custom user value');
+                module.add.label(value, value);
+              });
+            }
+          },
+          menu: function() {
+            $menu = $('<div />')
+              .addClass(className.menu)
+              .appendTo($module)
+            ;
+          },
+          sizer: function() {
+            $sizer = $('<span />')
+              .addClass(className.sizer)
+              .insertAfter($search)
+            ;
+          }
+        },
+
+        search: function(query) {
+          query = (query !== undefined)
+            ? query
+            : module.get.query()
+          ;
+          module.verbose('Searching for query', query);
+          if(module.has.minCharacters(query)) {
+            module.filter(query);
+          }
+          else {
+            module.hide();
+          }
+        },
+
+        select: {
+          firstUnfiltered: function() {
+            module.verbose('Selecting first non-filtered element');
+            module.remove.selectedItem();
+            $item
+              .not(selector.unselectable)
+              .not(selector.addition + selector.hidden)
+                .eq(0)
+                .addClass(className.selected)
+            ;
+          },
+          nextAvailable: function($selected) {
+            $selected = $selected.eq(0);
+            var
+              $nextAvailable = $selected.nextAll(selector.item).not(selector.unselectable).eq(0),
+              $prevAvailable = $selected.prevAll(selector.item).not(selector.unselectable).eq(0),
+              hasNext        = ($nextAvailable.length > 0)
+            ;
+            if(hasNext) {
+              module.verbose('Moving selection to', $nextAvailable);
+              $nextAvailable.addClass(className.selected);
+            }
+            else {
+              module.verbose('Moving selection to', $prevAvailable);
+              $prevAvailable.addClass(className.selected);
+            }
+          }
+        },
+
+        setup: {
+          api: function() {
+            var
+              apiSettings = {
+                debug   : settings.debug,
+                urlData : {
+                  value : module.get.value(),
+                  query : module.get.query()
+                },
+                on    : false
+              }
+            ;
+            module.verbose('First request, initializing API');
+            $module
+              .api(apiSettings)
+            ;
+          },
+          layout: function() {
+            if( $module.is('select') ) {
+              module.setup.select();
+              module.setup.returnedObject();
+            }
+            if( !module.has.menu() ) {
+              module.create.menu();
+            }
+            if ( module.is.selection() && module.is.clearable() && !module.has.clearItem() ) {
+              module.verbose('Adding clear icon');
+              $clear = $('<i />')
+                .addClass('remove icon')
+                .insertBefore($text)
+              ;
+            }
+            if( module.is.search() && !module.has.search() ) {
+              module.verbose('Adding search input');
+              $search = $('<input />')
+                .addClass(className.search)
+                .prop('autocomplete', 'off')
+                .insertBefore($text)
+              ;
+            }
+            if( module.is.multiple() && module.is.searchSelection() && !module.has.sizer()) {
+              module.create.sizer();
+            }
+            if(settings.allowTab) {
+              module.set.tabbable();
+            }
+          },
+          select: function() {
+            var
+              selectValues  = module.get.selectValues()
+            ;
+            module.debug('Dropdown initialized on a select', selectValues);
+            if( $module.is('select') ) {
+              $input = $module;
+            }
+            // see if select is placed correctly already
+            if($input.parent(selector.dropdown).length > 0) {
+              module.debug('UI dropdown already exists. Creating dropdown menu only');
+              $module = $input.closest(selector.dropdown);
+              if( !module.has.menu() ) {
+                module.create.menu();
+              }
+              $menu = $module.children(selector.menu);
+              module.setup.menu(selectValues);
+            }
+            else {
+              module.debug('Creating entire dropdown from select');
+              $module = $('<div />')
+                .attr('class', $input.attr('class') )
+                .addClass(className.selection)
+                .addClass(className.dropdown)
+                .html( templates.dropdown(selectValues, fields, settings.preserveHTML, settings.className) )
+                .insertBefore($input)
+              ;
+              if($input.hasClass(className.multiple) && $input.prop('multiple') === false) {
+                module.error(error.missingMultiple);
+                $input.prop('multiple', true);
+              }
+              if($input.is('[multiple]')) {
+                module.set.multiple();
+              }
+              if ($input.prop('disabled')) {
+                module.debug('Disabling dropdown');
+                $module.addClass(className.disabled);
+              }
+              $input
+                .removeAttr('class')
+                .detach()
+                .prependTo($module)
+              ;
+            }
+            module.refresh();
+          },
+          menu: function(values) {
+            $menu.html( templates.menu(values, fields,settings.preserveHTML,settings.className));
+            $item    = $menu.find(selector.item);
+            $divider = settings.hideDividers ? $item.parent().children(selector.divider) : $();
+          },
+          reference: function() {
+            module.debug('Dropdown behavior was called on select, replacing with closest dropdown');
+            // replace module reference
+            $module  = $module.parent(selector.dropdown);
+            instance = $module.data(moduleNamespace);
+            element  = $module.get(0);
+            module.refresh();
+            module.setup.returnedObject();
+          },
+          returnedObject: function() {
+            var
+              $firstModules = $allModules.slice(0, elementIndex),
+              $lastModules  = $allModules.slice(elementIndex + 1)
+            ;
+            // adjust all modules to use correct reference
+            $allModules = $firstModules.add($module).add($lastModules);
+          }
+        },
+
+        refresh: function() {
+          module.refreshSelectors();
+          module.refreshData();
+        },
+
+        refreshItems: function() {
+          $item    = $menu.find(selector.item);
+          $divider = settings.hideDividers ? $item.parent().children(selector.divider) : $();
+        },
+
+        refreshSelectors: function() {
+          module.verbose('Refreshing selector cache');
+          $text   = $module.find(selector.text);
+          $search = $module.find(selector.search);
+          $input  = $module.find(selector.input);
+          $icon   = $module.find(selector.icon);
+          $combo  = ($module.prev().find(selector.text).length > 0)
+            ? $module.prev().find(selector.text)
+            : $module.prev()
+          ;
+          $menu    = $module.children(selector.menu);
+          $item    = $menu.find(selector.item);
+          $divider = settings.hideDividers ? $item.parent().children(selector.divider) : $();
+        },
+
+        refreshData: function() {
+          module.verbose('Refreshing cached metadata');
+          $item
+            .removeData(metadata.text)
+            .removeData(metadata.value)
+          ;
+        },
+
+        clearData: function() {
+          module.verbose('Clearing metadata');
+          $item
+            .removeData(metadata.text)
+            .removeData(metadata.value)
+          ;
+          $module
+            .removeData(metadata.defaultText)
+            .removeData(metadata.defaultValue)
+            .removeData(metadata.placeholderText)
+          ;
+        },
+
+        toggle: function() {
+          module.verbose('Toggling menu visibility');
+          if( !module.is.active() ) {
+            module.show();
+          }
+          else {
+            module.hide();
+          }
+        },
+
+        show: function(callback) {
+          callback = $.isFunction(callback)
+            ? callback
+            : function(){}
+          ;
+          if(!module.can.show() && module.is.remote()) {
+            module.debug('No API results retrieved, searching before show');
+            module.queryRemote(module.get.query(), module.show);
+          }
+          if( module.can.show() && !module.is.active() ) {
+            module.debug('Showing dropdown');
+            if(module.has.message() && !(module.has.maxSelections() || module.has.allResultsFiltered()) ) {
+              module.remove.message();
+            }
+            if(module.is.allFiltered()) {
+              return true;
+            }
+            if(settings.onShow.call(element) !== false) {
+              module.animate.show(function() {
+                if( module.can.click() ) {
+                  module.bind.intent();
+                }
+                if(module.has.search()) {
+                  module.focusSearch();
+                }
+                module.set.visible();
+                callback.call(element);
+              });
+            }
+          }
+        },
+
+        hide: function(callback) {
+          callback = $.isFunction(callback)
+            ? callback
+            : function(){}
+          ;
+          if( module.is.active() && !module.is.animatingOutward() ) {
+            module.debug('Hiding dropdown');
+            if(settings.onHide.call(element) !== false) {
+              module.animate.hide(function() {
+                module.remove.visible();
+                // hidding search focus
+                if ( module.is.focusedOnSearch() ) {
+                  $search.blur();
+                }
+                callback.call(element);
+              });
+            }
+          } else if( module.can.click() ) {
+              module.unbind.intent();
+          }
+        },
+
+        hideOthers: function() {
+          module.verbose('Finding other dropdowns to hide');
+          $allModules
+            .not($module)
+              .has(selector.menu + '.' + className.visible)
+                .dropdown('hide')
+          ;
+        },
+
+        hideMenu: function() {
+          module.verbose('Hiding menu  instantaneously');
+          module.remove.active();
+          module.remove.visible();
+          $menu.transition('hide');
+        },
+
+        hideSubMenus: function() {
+          var
+            $subMenus = $menu.children(selector.item).find(selector.menu)
+          ;
+          module.verbose('Hiding sub menus', $subMenus);
+          $subMenus.transition('hide');
+        },
+
+        bind: {
+          events: function() {
+            if(hasTouch) {
+              module.bind.touchEvents();
+            }
+            module.bind.keyboardEvents();
+            module.bind.inputEvents();
+            module.bind.mouseEvents();
+          },
+          touchEvents: function() {
+            module.debug('Touch device detected binding additional touch events');
+            if( module.is.searchSelection() ) {
+              // do nothing special yet
+            }
+            else if( module.is.single() ) {
+              $module
+                .on('touchstart' + eventNamespace, module.event.test.toggle)
+              ;
+            }
+            $menu
+              .on('touchstart' + eventNamespace, selector.item, module.event.item.mouseenter)
+            ;
+          },
+          keyboardEvents: function() {
+            module.verbose('Binding keyboard events');
+            $module
+              .on('keydown' + eventNamespace, module.event.keydown)
+            ;
+            if( module.has.search() ) {
+              $module
+                .on(module.get.inputEvent() + eventNamespace, selector.search, module.event.input)
+              ;
+            }
+            if( module.is.multiple() ) {
+              $document
+                .on('keydown' + elementNamespace, module.event.document.keydown)
+              ;
+            }
+          },
+          inputEvents: function() {
+            module.verbose('Binding input change events');
+            $module
+              .on('change' + eventNamespace, selector.input, module.event.change)
+            ;
+          },
+          mouseEvents: function() {
+            module.verbose('Binding mouse events');
+            if(module.is.multiple()) {
+              $module
+                .on('click'   + eventNamespace, selector.label,  module.event.label.click)
+                .on('click'   + eventNamespace, selector.remove, module.event.remove.click)
+              ;
+            }
+            if( module.is.searchSelection() ) {
+              $module
+                .on('mousedown' + eventNamespace, module.event.mousedown)
+                .on('mouseup'   + eventNamespace, module.event.mouseup)
+                .on('mousedown' + eventNamespace, selector.menu,   module.event.menu.mousedown)
+                .on('mouseup'   + eventNamespace, selector.menu,   module.event.menu.mouseup)
+                .on('click'     + eventNamespace, selector.icon,   module.event.icon.click)
+                .on('click'     + eventNamespace, selector.clearIcon, module.event.clearIcon.click)
+                .on('focus'     + eventNamespace, selector.search, module.event.search.focus)
+                .on('click'     + eventNamespace, selector.search, module.event.search.focus)
+                .on('blur'      + eventNamespace, selector.search, module.event.search.blur)
+                .on('click'     + eventNamespace, selector.text,   module.event.text.focus)
+              ;
+              if(module.is.multiple()) {
+                $module
+                  .on('click' + eventNamespace, module.event.click)
+                ;
+              }
+            }
+            else {
+              if(settings.on == 'click') {
+                $module
+                  .on('click' + eventNamespace, selector.icon, module.event.icon.click)
+                  .on('click' + eventNamespace, module.event.test.toggle)
+                ;
+              }
+              else if(settings.on == 'hover') {
+                $module
+                  .on('mouseenter' + eventNamespace, module.delay.show)
+                  .on('mouseleave' + eventNamespace, module.delay.hide)
+                ;
+              }
+              else {
+                $module
+                  .on(settings.on + eventNamespace, module.toggle)
+                ;
+              }
+              $module
+                .on('mousedown' + eventNamespace, module.event.mousedown)
+                .on('mouseup'   + eventNamespace, module.event.mouseup)
+                .on('focus'     + eventNamespace, module.event.focus)
+                .on('click'     + eventNamespace, selector.clearIcon, module.event.clearIcon.click)
+              ;
+              if(module.has.menuSearch() ) {
+                $module
+                  .on('blur' + eventNamespace, selector.search, module.event.search.blur)
+                ;
+              }
+              else {
+                $module
+                  .on('blur' + eventNamespace, module.event.blur)
+                ;
+              }
+            }
+            $menu
+              .on('mouseenter' + eventNamespace, selector.item, module.event.item.mouseenter)
+              .on('mouseleave' + eventNamespace, selector.item, module.event.item.mouseleave)
+              .on('click'      + eventNamespace, selector.item, module.event.item.click)
+            ;
+          },
+          intent: function() {
+            module.verbose('Binding hide intent event to document');
+            if(hasTouch) {
+              $document
+                .on('touchstart' + elementNamespace, module.event.test.touch)
+                .on('touchmove'  + elementNamespace, module.event.test.touch)
+              ;
+            }
+            $document
+              .on('click' + elementNamespace, module.event.test.hide)
+            ;
+          }
+        },
+
+        unbind: {
+          intent: function() {
+            module.verbose('Removing hide intent event from document');
+            if(hasTouch) {
+              $document
+                .off('touchstart' + elementNamespace)
+                .off('touchmove' + elementNamespace)
+              ;
+            }
+            $document
+              .off('click' + elementNamespace)
+            ;
+          }
+        },
+
+        filter: function(query) {
+          var
+            searchTerm = (query !== undefined)
+              ? query
+              : module.get.query(),
+            afterFiltered = function() {
+              if(module.is.multiple()) {
+                module.filterActive();
+              }
+              if(query || (!query && module.get.activeItem().length == 0)) {
+                module.select.firstUnfiltered();
+              }
+              if( module.has.allResultsFiltered() ) {
+                if( settings.onNoResults.call(element, searchTerm) ) {
+                  if(settings.allowAdditions) {
+                    if(settings.hideAdditions) {
+                      module.verbose('User addition with no menu, setting empty style');
+                      module.set.empty();
+                      module.hideMenu();
+                    }
+                  }
+                  else {
+                    module.verbose('All items filtered, showing message', searchTerm);
+                    module.add.message(message.noResults);
+                  }
+                }
+                else {
+                  module.verbose('All items filtered, hiding dropdown', searchTerm);
+                  module.hideMenu();
+                }
+              }
+              else {
+                module.remove.empty();
+                module.remove.message();
+              }
+              if(settings.allowAdditions) {
+                module.add.userSuggestion(module.escape.htmlEntities(query));
+              }
+              if(module.is.searchSelection() && module.can.show() && module.is.focusedOnSearch() ) {
+                module.show();
+              }
+            }
+          ;
+          if(settings.useLabels && module.has.maxSelections()) {
+            return;
+          }
+          if(settings.apiSettings) {
+            if( module.can.useAPI() ) {
+              module.queryRemote(searchTerm, function() {
+                if(settings.filterRemoteData) {
+                  module.filterItems(searchTerm);
+                }
+                var preSelected = $input.val();
+                if(!Array.isArray(preSelected)) {
+                    preSelected = preSelected && preSelected!=="" ? preSelected.split(settings.delimiter) : [];
+                }
+                $.each(preSelected,function(index,value){
+                  $item.filter('[data-value="'+value+'"]')
+                      .addClass(className.filtered)
+                  ;
+                });
+                afterFiltered();
+              });
+            }
+            else {
+              module.error(error.noAPI);
+            }
+          }
+          else {
+            module.filterItems(searchTerm);
+            afterFiltered();
+          }
+        },
+
+        queryRemote: function(query, callback) {
+          var
+            apiSettings = {
+              errorDuration : false,
+              cache         : 'local',
+              throttle      : settings.throttle,
+              urlData       : {
+                query: query
+              },
+              onError: function() {
+                module.add.message(message.serverError);
+                callback();
+              },
+              onFailure: function() {
+                module.add.message(message.serverError);
+                callback();
+              },
+              onSuccess : function(response) {
+                var
+                  values          = response[fields.remoteValues]
+                ;
+                if (!Array.isArray(values)){
+                    values = [];
+                }
+                module.remove.message();
+                module.setup.menu({
+                  values: values
+                });
+
+                if(values.length===0 && !settings.allowAdditions) {
+                  module.add.message(message.noResults);
+                }
+                callback();
+              }
+            }
+          ;
+          if( !$module.api('get request') ) {
+            module.setup.api();
+          }
+          apiSettings = $.extend(true, {}, apiSettings, settings.apiSettings);
+          $module
+            .api('setting', apiSettings)
+            .api('query')
+          ;
+        },
+
+        filterItems: function(query) {
+          var
+            searchTerm = module.remove.diacritics(query !== undefined
+              ? query
+              : module.get.query()
+            ),
+            results          =  null,
+            escapedTerm      = module.escape.string(searchTerm),
+            beginsWithRegExp = new RegExp('^' + escapedTerm, 'igm')
+          ;
+          // avoid loop if we're matching nothing
+          if( module.has.query() ) {
+            results = [];
+
+            module.verbose('Searching for matching values', searchTerm);
+            $item
+              .each(function(){
+                var
+                  $choice = $(this),
+                  text,
+                  value
+                ;
+                if(settings.match === 'both' || settings.match === 'text') {
+                  text = module.remove.diacritics(String(module.get.choiceText($choice, false)));
+                  if(text.search(beginsWithRegExp) !== -1) {
+                    results.push(this);
+                    return true;
+                  }
+                  else if (settings.fullTextSearch === 'exact' && module.exactSearch(searchTerm, text)) {
+                    results.push(this);
+                    return true;
+                  }
+                  else if (settings.fullTextSearch === true && module.fuzzySearch(searchTerm, text)) {
+                    results.push(this);
+                    return true;
+                  }
+                }
+                if(settings.match === 'both' || settings.match === 'value') {
+                  value = module.remove.diacritics(String(module.get.choiceValue($choice, text)));
+                  if(value.search(beginsWithRegExp) !== -1) {
+                    results.push(this);
+                    return true;
+                  }
+                  else if (settings.fullTextSearch === 'exact' && module.exactSearch(searchTerm, value)) {
+                    results.push(this);
+                    return true;
+                  }
+                  else if (settings.fullTextSearch === true && module.fuzzySearch(searchTerm, value)) {
+                    results.push(this);
+                    return true;
+                  }
+                }
+              })
+            ;
+          }
+          module.debug('Showing only matched items', searchTerm);
+          module.remove.filteredItem();
+          if(results) {
+            $item
+              .not(results)
+              .addClass(className.filtered)
+            ;
+          }
+
+          if(!module.has.query()) {
+            $divider
+              .removeClass(className.hidden);
+          } else if(settings.hideDividers === true) {
+            $divider
+              .addClass(className.hidden);
+          } else if(settings.hideDividers === 'empty') {
+            $divider
+              .removeClass(className.hidden)
+              .filter(function() {
+                // First find the last divider in this divider group
+                // Dividers which are direct siblings are considered a group
+                var lastDivider = $(this).nextUntil(selector.item);
+
+                return (lastDivider.length ? lastDivider : $(this))
+                // Count all non-filtered items until the next divider (or end of the dropdown)
+                  .nextUntil(selector.divider)
+                  .filter(selector.item + ":not(." + className.filtered + ")")
+                  // Hide divider if no items are found
+                  .length === 0;
+              })
+              .addClass(className.hidden);
+          }
+        },
+
+        fuzzySearch: function(query, term) {
+          var
+            termLength  = term.length,
+            queryLength = query.length
+          ;
+          query = query.toLowerCase();
+          term  = term.toLowerCase();
+          if(queryLength > termLength) {
+            return false;
+          }
+          if(queryLength === termLength) {
+            return (query === term);
+          }
+          search: for (var characterIndex = 0, nextCharacterIndex = 0; characterIndex < queryLength; characterIndex++) {
+            var
+              queryCharacter = query.charCodeAt(characterIndex)
+            ;
+            while(nextCharacterIndex < termLength) {
+              if(term.charCodeAt(nextCharacterIndex++) === queryCharacter) {
+                continue search;
+              }
+            }
+            return false;
+          }
+          return true;
+        },
+        exactSearch: function (query, term) {
+          query = query.toLowerCase();
+          term  = term.toLowerCase();
+          return term.indexOf(query) > -1;
+
+        },
+        filterActive: function() {
+          if(settings.useLabels) {
+            $item.filter('.' + className.active)
+              .addClass(className.filtered)
+            ;
+          }
+        },
+
+        focusSearch: function(skipHandler) {
+          if( module.has.search() && !module.is.focusedOnSearch() ) {
+            if(skipHandler) {
+              $module.off('focus' + eventNamespace, selector.search);
+              $search.focus();
+              $module.on('focus'  + eventNamespace, selector.search, module.event.search.focus);
+            }
+            else {
+              $search.focus();
+            }
+          }
+        },
+
+        blurSearch: function() {
+          if( module.has.search() ) {
+            $search.blur();
+          }
+        },
+
+        forceSelection: function() {
+          var
+            $currentlySelected = $item.not(className.filtered).filter('.' + className.selected).eq(0),
+            $activeItem        = $item.not(className.filtered).filter('.' + className.active).eq(0),
+            $selectedItem      = ($currentlySelected.length > 0)
+              ? $currentlySelected
+              : $activeItem,
+            hasSelected = ($selectedItem.length > 0)
+          ;
+          if(hasSelected && !module.is.multiple()) {
+            module.debug('Forcing partial selection to selected item', $selectedItem);
+            module.event.item.click.call($selectedItem, {}, true);
+          }
+          else {
+            if(settings.allowAdditions) {
+              module.set.selected(module.get.query());
+              module.remove.searchTerm();
+            }
+            else {
+              module.remove.searchTerm();
+            }
+          }
+        },
+
+        change: {
+          values: function(values) {
+            if(!settings.allowAdditions) {
+              module.clear();
+            }
+            module.debug('Creating dropdown with specified values', values);
+            module.setup.menu({values: values});
+            $.each(values, function(index, item) {
+              if(item.selected == true) {
+                module.debug('Setting initial selection to', item[fields.value]);
+                module.set.selected(item[fields.value]);
+                if(!module.is.multiple()) {
+                  return false;
+                }
+              }
+            });
+          }
+        },
+
+        event: {
+          change: function() {
+            if(!internalChange) {
+              module.debug('Input changed, updating selection');
+              module.set.selected();
+            }
+          },
+          focus: function() {
+            if(settings.showOnFocus && !activated && module.is.hidden() && !pageLostFocus) {
+              module.show();
+            }
+          },
+          blur: function(event) {
+            pageLostFocus = (document.activeElement === this);
+            if(!activated && !pageLostFocus) {
+              module.remove.activeLabel();
+              module.hide();
+            }
+          },
+          mousedown: function() {
+            if(module.is.searchSelection()) {
+              // prevent menu hiding on immediate re-focus
+              willRefocus = true;
+            }
+            else {
+              // prevents focus callback from occurring on mousedown
+              activated = true;
+            }
+          },
+          mouseup: function() {
+            if(module.is.searchSelection()) {
+              // prevent menu hiding on immediate re-focus
+              willRefocus = false;
+            }
+            else {
+              activated = false;
+            }
+          },
+          click: function(event) {
+            var
+              $target = $(event.target)
+            ;
+            // focus search
+            if($target.is($module)) {
+              if(!module.is.focusedOnSearch()) {
+                module.focusSearch();
+              }
+              else {
+                module.show();
+              }
+            }
+          },
+          search: {
+            focus: function(event) {
+              activated = true;
+              if(module.is.multiple()) {
+                module.remove.activeLabel();
+              }
+              if(settings.showOnFocus || (event.type !== 'focus' && event.type !== 'focusin')) {
+                module.search();
+              }
+            },
+            blur: function(event) {
+              pageLostFocus = (document.activeElement === this);
+              if(module.is.searchSelection() && !willRefocus) {
+                if(!itemActivated && !pageLostFocus) {
+                  if(settings.forceSelection) {
+                    module.forceSelection();
+                  }
+                  module.hide();
+                }
+              }
+              willRefocus = false;
+            }
+          },
+          clearIcon: {
+            click: function(event) {
+              module.clear();
+              if(module.is.searchSelection()) {
+                module.remove.searchTerm();
+              }
+              module.hide();
+              event.stopPropagation();
+            }
+          },
+          icon: {
+            click: function(event) {
+              iconClicked=true;
+              if(module.has.search()) {
+                if(!module.is.active()) {
+                    if(settings.showOnFocus){
+                      module.focusSearch();
+                    } else {
+                      module.toggle();
+                    }
+                } else {
+                  module.blurSearch();
+                }
+              } else {
+                module.toggle();
+              }
+            }
+          },
+          text: {
+            focus: function(event) {
+              activated = true;
+              module.focusSearch();
+            }
+          },
+          input: function(event) {
+            if(module.is.multiple() || module.is.searchSelection()) {
+              module.set.filtered();
+            }
+            clearTimeout(module.timer);
+            module.timer = setTimeout(module.search, settings.delay.search);
+          },
+          label: {
+            click: function(event) {
+              var
+                $label        = $(this),
+                $labels       = $module.find(selector.label),
+                $activeLabels = $labels.filter('.' + className.active),
+                $nextActive   = $label.nextAll('.' + className.active),
+                $prevActive   = $label.prevAll('.' + className.active),
+                $range = ($nextActive.length > 0)
+                  ? $label.nextUntil($nextActive).add($activeLabels).add($label)
+                  : $label.prevUntil($prevActive).add($activeLabels).add($label)
+              ;
+              if(event.shiftKey) {
+                $activeLabels.removeClass(className.active);
+                $range.addClass(className.active);
+              }
+              else if(event.ctrlKey) {
+                $label.toggleClass(className.active);
+              }
+              else {
+                $activeLabels.removeClass(className.active);
+                $label.addClass(className.active);
+              }
+              settings.onLabelSelect.apply(this, $labels.filter('.' + className.active));
+            }
+          },
+          remove: {
+            click: function() {
+              var
+                $label = $(this).parent()
+              ;
+              if( $label.hasClass(className.active) ) {
+                // remove all selected labels
+                module.remove.activeLabels();
+              }
+              else {
+                // remove this label only
+                module.remove.activeLabels( $label );
+              }
+            }
+          },
+          test: {
+            toggle: function(event) {
+              var
+                toggleBehavior = (module.is.multiple())
+                  ? module.show
+                  : module.toggle
+              ;
+              if(module.is.bubbledLabelClick(event) || module.is.bubbledIconClick(event)) {
+                return;
+              }
+              if( module.determine.eventOnElement(event, toggleBehavior) ) {
+                event.preventDefault();
+              }
+            },
+            touch: function(event) {
+              module.determine.eventOnElement(event, function() {
+                if(event.type == 'touchstart') {
+                  module.timer = setTimeout(function() {
+                    module.hide();
+                  }, settings.delay.touch);
+                }
+                else if(event.type == 'touchmove') {
+                  clearTimeout(module.timer);
+                }
+              });
+              event.stopPropagation();
+            },
+            hide: function(event) {
+              if(module.determine.eventInModule(event, module.hide)){
+                if(element.id && $(event.target).attr('for') === element.id){
+                  event.preventDefault();
+                }
+              }
+            }
+          },
+          select: {
+            mutation: function(mutations) {
+              module.debug('<select> modified, recreating menu');
+              if(module.is.selectMutation(mutations)) {
+                module.disconnect.selectObserver();
+                module.refresh();
+                module.setup.select();
+                module.set.selected();
+                module.observe.select();
+              }
+            }
+          },
+          menu: {
+            mutation: function(mutations) {
+              var
+                mutation   = mutations[0],
+                $addedNode = mutation.addedNodes
+                  ? $(mutation.addedNodes[0])
+                  : $(false),
+                $removedNode = mutation.removedNodes
+                  ? $(mutation.removedNodes[0])
+                  : $(false),
+                $changedNodes  = $addedNode.add($removedNode),
+                isUserAddition = $changedNodes.is(selector.addition) || $changedNodes.closest(selector.addition).length > 0,
+                isMessage      = $changedNodes.is(selector.message)  || $changedNodes.closest(selector.message).length > 0
+              ;
+              if(isUserAddition || isMessage) {
+                module.debug('Updating item selector cache');
+                module.refreshItems();
+              }
+              else {
+                module.debug('Menu modified, updating selector cache');
+                module.refresh();
+              }
+            },
+            mousedown: function() {
+              itemActivated = true;
+            },
+            mouseup: function() {
+              itemActivated = false;
+            }
+          },
+          item: {
+            mouseenter: function(event) {
+              var
+                $target        = $(event.target),
+                $item          = $(this),
+                $subMenu       = $item.children(selector.menu),
+                $otherMenus    = $item.siblings(selector.item).children(selector.menu),
+                hasSubMenu     = ($subMenu.length > 0),
+                isBubbledEvent = ($subMenu.find($target).length > 0)
+              ;
+              if( !isBubbledEvent && hasSubMenu ) {
+                clearTimeout(module.itemTimer);
+                module.itemTimer = setTimeout(function() {
+                  module.verbose('Showing sub-menu', $subMenu);
+                  $.each($otherMenus, function() {
+                    module.animate.hide(false, $(this));
+                  });
+                  module.animate.show(false, $subMenu);
+                }, settings.delay.show);
+                event.preventDefault();
+              }
+            },
+            mouseleave: function(event) {
+              var
+                $subMenu = $(this).children(selector.menu)
+              ;
+              if($subMenu.length > 0) {
+                clearTimeout(module.itemTimer);
+                module.itemTimer = setTimeout(function() {
+                  module.verbose('Hiding sub-menu', $subMenu);
+                  module.animate.hide(false, $subMenu);
+                }, settings.delay.hide);
+              }
+            },
+            click: function (event, skipRefocus) {
+              var
+                $choice        = $(this),
+                $target        = (event)
+                  ? $(event.target)
+                  : $(''),
+                $subMenu       = $choice.find(selector.menu),
+                text           = module.get.choiceText($choice),
+                value          = module.get.choiceValue($choice, text),
+                hasSubMenu     = ($subMenu.length > 0),
+                isBubbledEvent = ($subMenu.find($target).length > 0)
+              ;
+              // prevents IE11 bug where menu receives focus even though `tabindex=-1`
+              if (document.activeElement.tagName.toLowerCase() !== 'input') {
+                $(document.activeElement).blur();
+              }
+              if(!isBubbledEvent && (!hasSubMenu || settings.allowCategorySelection)) {
+                if(module.is.searchSelection()) {
+                  if(settings.allowAdditions) {
+                    module.remove.userAddition();
+                  }
+                  module.remove.searchTerm();
+                  if(!module.is.focusedOnSearch() && !(skipRefocus == true)) {
+                    module.focusSearch(true);
+                  }
+                }
+                if(!settings.useLabels) {
+                  module.remove.filteredItem();
+                  module.set.scrollPosition($choice);
+                }
+                module.determine.selectAction.call(this, text, value);
+              }
+            }
+          },
+
+          document: {
+            // label selection should occur even when element has no focus
+            keydown: function(event) {
+              var
+                pressedKey    = event.which,
+                isShortcutKey = module.is.inObject(pressedKey, keys)
+              ;
+              if(isShortcutKey) {
+                var
+                  $label            = $module.find(selector.label),
+                  $activeLabel      = $label.filter('.' + className.active),
+                  activeValue       = $activeLabel.data(metadata.value),
+                  labelIndex        = $label.index($activeLabel),
+                  labelCount        = $label.length,
+                  hasActiveLabel    = ($activeLabel.length > 0),
+                  hasMultipleActive = ($activeLabel.length > 1),
+                  isFirstLabel      = (labelIndex === 0),
+                  isLastLabel       = (labelIndex + 1 == labelCount),
+                  isSearch          = module.is.searchSelection(),
+                  isFocusedOnSearch = module.is.focusedOnSearch(),
+                  isFocused         = module.is.focused(),
+                  caretAtStart      = (isFocusedOnSearch && module.get.caretPosition(false) === 0),
+                  isSelectedSearch  = (caretAtStart && module.get.caretPosition(true) !== 0),
+                  $nextLabel
+                ;
+                if(isSearch && !hasActiveLabel && !isFocusedOnSearch) {
+                  return;
+                }
+
+                if(pressedKey == keys.leftArrow) {
+                  // activate previous label
+                  if((isFocused || caretAtStart) && !hasActiveLabel) {
+                    module.verbose('Selecting previous label');
+                    $label.last().addClass(className.active);
+                  }
+                  else if(hasActiveLabel) {
+                    if(!event.shiftKey) {
+                      module.verbose('Selecting previous label');
+                      $label.removeClass(className.active);
+                    }
+                    else {
+                      module.verbose('Adding previous label to selection');
+                    }
+                    if(isFirstLabel && !hasMultipleActive) {
+                      $activeLabel.addClass(className.active);
+                    }
+                    else {
+                      $activeLabel.prev(selector.siblingLabel)
+                        .addClass(className.active)
+                        .end()
+                      ;
+                    }
+                    event.preventDefault();
+                  }
+                }
+                else if(pressedKey == keys.rightArrow) {
+                  // activate first label
+                  if(isFocused && !hasActiveLabel) {
+                    $label.first().addClass(className.active);
+                  }
+                  // activate next label
+                  if(hasActiveLabel) {
+                    if(!event.shiftKey) {
+                      module.verbose('Selecting next label');
+                      $label.removeClass(className.active);
+                    }
+                    else {
+                      module.verbose('Adding next label to selection');
+                    }
+                    if(isLastLabel) {
+                      if(isSearch) {
+                        if(!isFocusedOnSearch) {
+                          module.focusSearch();
+                        }
+                        else {
+                          $label.removeClass(className.active);
+                        }
+                      }
+                      else if(hasMultipleActive) {
+                        $activeLabel.next(selector.siblingLabel).addClass(className.active);
+                      }
+                      else {
+                        $activeLabel.addClass(className.active);
+                      }
+                    }
+                    else {
+                      $activeLabel.next(selector.siblingLabel).addClass(className.active);
+                    }
+                    event.preventDefault();
+                  }
+                }
+                else if(pressedKey == keys.deleteKey || pressedKey == keys.backspace) {
+                  if(hasActiveLabel) {
+                    module.verbose('Removing active labels');
+                    if(isLastLabel) {
+                      if(isSearch && !isFocusedOnSearch) {
+                        module.focusSearch();
+                      }
+                    }
+                    $activeLabel.last().next(selector.siblingLabel).addClass(className.active);
+                    module.remove.activeLabels($activeLabel);
+                    event.preventDefault();
+                  }
+                  else if(caretAtStart && !isSelectedSearch && !hasActiveLabel && pressedKey == keys.backspace) {
+                    module.verbose('Removing last label on input backspace');
+                    $activeLabel = $label.last().addClass(className.active);
+                    module.remove.activeLabels($activeLabel);
+                  }
+                }
+                else {
+                  $activeLabel.removeClass(className.active);
+                }
+              }
+            }
+          },
+
+          keydown: function(event) {
+            var
+              pressedKey    = event.which,
+              isShortcutKey = module.is.inObject(pressedKey, keys)
+            ;
+            if(isShortcutKey) {
+              var
+                $currentlySelected = $item.not(selector.unselectable).filter('.' + className.selected).eq(0),
+                $activeItem        = $menu.children('.' + className.active).eq(0),
+                $selectedItem      = ($currentlySelected.length > 0)
+                  ? $currentlySelected
+                  : $activeItem,
+                $visibleItems = ($selectedItem.length > 0)
+                  ? $selectedItem.siblings(':not(.' + className.filtered +')').addBack()
+                  : $menu.children(':not(.' + className.filtered +')'),
+                $subMenu              = $selectedItem.children(selector.menu),
+                $parentMenu           = $selectedItem.closest(selector.menu),
+                inVisibleMenu         = ($parentMenu.hasClass(className.visible) || $parentMenu.hasClass(className.animating) || $parentMenu.parent(selector.menu).length > 0),
+                hasSubMenu            = ($subMenu.length> 0),
+                hasSelectedItem       = ($selectedItem.length > 0),
+                selectedIsSelectable  = ($selectedItem.not(selector.unselectable).length > 0),
+                delimiterPressed      = (pressedKey == keys.delimiter && settings.allowAdditions && module.is.multiple()),
+                isAdditionWithoutMenu = (settings.allowAdditions && settings.hideAdditions && (pressedKey == keys.enter || delimiterPressed) && selectedIsSelectable),
+                $nextItem,
+                isSubMenuItem,
+                newIndex
+              ;
+              // allow selection with menu closed
+              if(isAdditionWithoutMenu) {
+                module.verbose('Selecting item from keyboard shortcut', $selectedItem);
+                module.event.item.click.call($selectedItem, event);
+                if(module.is.searchSelection()) {
+                  module.remove.searchTerm();
+                }
+                if(module.is.multiple()){
+                    event.preventDefault();
+                }
+              }
+
+              // visible menu keyboard shortcuts
+              if( module.is.visible() ) {
+
+                // enter (select or open sub-menu)
+                if(pressedKey == keys.enter || delimiterPressed) {
+                  if(pressedKey == keys.enter && hasSelectedItem && hasSubMenu && !settings.allowCategorySelection) {
+                    module.verbose('Pressed enter on unselectable category, opening sub menu');
+                    pressedKey = keys.rightArrow;
+                  }
+                  else if(selectedIsSelectable) {
+                    module.verbose('Selecting item from keyboard shortcut', $selectedItem);
+                    module.event.item.click.call($selectedItem, event);
+                    if(module.is.searchSelection()) {
+                      module.remove.searchTerm();
+                      if(module.is.multiple()) {
+                          $search.focus();
+                      }
+                    }
+                  }
+                  event.preventDefault();
+                }
+
+                // sub-menu actions
+                if(hasSelectedItem) {
+
+                  if(pressedKey == keys.leftArrow) {
+
+                    isSubMenuItem = ($parentMenu[0] !== $menu[0]);
+
+                    if(isSubMenuItem) {
+                      module.verbose('Left key pressed, closing sub-menu');
+                      module.animate.hide(false, $parentMenu);
+                      $selectedItem
+                        .removeClass(className.selected)
+                      ;
+                      $parentMenu
+                        .closest(selector.item)
+                          .addClass(className.selected)
+                      ;
+                      event.preventDefault();
+                    }
+                  }
+
+                  // right arrow (show sub-menu)
+                  if(pressedKey == keys.rightArrow) {
+                    if(hasSubMenu) {
+                      module.verbose('Right key pressed, opening sub-menu');
+                      module.animate.show(false, $subMenu);
+                      $selectedItem
+                        .removeClass(className.selected)
+                      ;
+                      $subMenu
+                        .find(selector.item).eq(0)
+                          .addClass(className.selected)
+                      ;
+                      event.preventDefault();
+                    }
+                  }
+                }
+
+                // up arrow (traverse menu up)
+                if(pressedKey == keys.upArrow) {
+                  $nextItem = (hasSelectedItem && inVisibleMenu)
+                    ? $selectedItem.prevAll(selector.item + ':not(' + selector.unselectable + ')').eq(0)
+                    : $item.eq(0)
+                  ;
+                  if($visibleItems.index( $nextItem ) < 0) {
+                    module.verbose('Up key pressed but reached top of current menu');
+                    event.preventDefault();
+                    return;
+                  }
+                  else {
+                    module.verbose('Up key pressed, changing active item');
+                    $selectedItem
+                      .removeClass(className.selected)
+                    ;
+                    $nextItem
+                      .addClass(className.selected)
+                    ;
+                    module.set.scrollPosition($nextItem);
+                    if(settings.selectOnKeydown && module.is.single()) {
+                      module.set.selectedItem($nextItem);
+                    }
+                  }
+                  event.preventDefault();
+                }
+
+                // down arrow (traverse menu down)
+                if(pressedKey == keys.downArrow) {
+                  $nextItem = (hasSelectedItem && inVisibleMenu)
+                    ? $nextItem = $selectedItem.nextAll(selector.item + ':not(' + selector.unselectable + ')').eq(0)
+                    : $item.eq(0)
+                  ;
+                  if($nextItem.length === 0) {
+                    module.verbose('Down key pressed but reached bottom of current menu');
+                    event.preventDefault();
+                    return;
+                  }
+                  else {
+                    module.verbose('Down key pressed, changing active item');
+                    $item
+                      .removeClass(className.selected)
+                    ;
+                    $nextItem
+                      .addClass(className.selected)
+                    ;
+                    module.set.scrollPosition($nextItem);
+                    if(settings.selectOnKeydown && module.is.single()) {
+                      module.set.selectedItem($nextItem);
+                    }
+                  }
+                  event.preventDefault();
+                }
+
+                // page down (show next page)
+                if(pressedKey == keys.pageUp) {
+                  module.scrollPage('up');
+                  event.preventDefault();
+                }
+                if(pressedKey == keys.pageDown) {
+                  module.scrollPage('down');
+                  event.preventDefault();
+                }
+
+                // escape (close menu)
+                if(pressedKey == keys.escape) {
+                  module.verbose('Escape key pressed, closing dropdown');
+                  module.hide();
+                }
+
+              }
+              else {
+                // delimiter key
+                if(delimiterPressed) {
+                  event.preventDefault();
+                }
+                // down arrow (open menu)
+                if(pressedKey == keys.downArrow && !module.is.visible()) {
+                  module.verbose('Down key pressed, showing dropdown');
+                  module.show();
+                  event.preventDefault();
+                }
+              }
+            }
+            else {
+              if( !module.has.search() ) {
+                module.set.selectedLetter( String.fromCharCode(pressedKey) );
+              }
+            }
+          }
+        },
+
+        trigger: {
+          change: function() {
+            var
+              events       = document.createEvent('HTMLEvents'),
+              inputElement = $input[0]
+            ;
+            if(inputElement) {
+              module.verbose('Triggering native change event');
+              events.initEvent('change', true, false);
+              inputElement.dispatchEvent(events);
+            }
+          }
+        },
+
+        determine: {
+          selectAction: function(text, value) {
+            selectActionActive = true;
+            module.verbose('Determining action', settings.action);
+            if( $.isFunction( module.action[settings.action] ) ) {
+              module.verbose('Triggering preset action', settings.action, text, value);
+              module.action[ settings.action ].call(element, text, value, this);
+            }
+            else if( $.isFunction(settings.action) ) {
+              module.verbose('Triggering user action', settings.action, text, value);
+              settings.action.call(element, text, value, this);
+            }
+            else {
+              module.error(error.action, settings.action);
+            }
+            selectActionActive = false;
+          },
+          eventInModule: function(event, callback) {
+            var
+              $target    = $(event.target),
+              inDocument = ($target.closest(document.documentElement).length > 0),
+              inModule   = ($target.closest($module).length > 0)
+            ;
+            callback = $.isFunction(callback)
+              ? callback
+              : function(){}
+            ;
+            if(inDocument && !inModule) {
+              module.verbose('Triggering event', callback);
+              callback();
+              return true;
+            }
+            else {
+              module.verbose('Event occurred in dropdown, canceling callback');
+              return false;
+            }
+          },
+          eventOnElement: function(event, callback) {
+            var
+              $target      = $(event.target),
+              $label       = $target.closest(selector.siblingLabel),
+              inVisibleDOM = document.body.contains(event.target),
+              notOnLabel   = ($module.find($label).length === 0 || !(module.is.multiple() && settings.useLabels)),
+              notInMenu    = ($target.closest($menu).length === 0)
+            ;
+            callback = $.isFunction(callback)
+              ? callback
+              : function(){}
+            ;
+            if(inVisibleDOM && notOnLabel && notInMenu) {
+              module.verbose('Triggering event', callback);
+              callback();
+              return true;
+            }
+            else {
+              module.verbose('Event occurred in dropdown menu, canceling callback');
+              return false;
+            }
+          }
+        },
+
+        action: {
+
+          nothing: function() {},
+
+          activate: function(text, value, element) {
+            value = (value !== undefined)
+              ? value
+              : text
+            ;
+            if( module.can.activate( $(element) ) ) {
+              module.set.selected(value, $(element));
+              if(!module.is.multiple()) {
+                module.hideAndClear();
+              }
+            }
+          },
+
+          select: function(text, value, element) {
+            value = (value !== undefined)
+              ? value
+              : text
+            ;
+            if( module.can.activate( $(element) ) ) {
+              module.set.value(value, text, $(element));
+              if(!module.is.multiple()) {
+                module.hideAndClear();
+              }
+            }
+          },
+
+          combo: function(text, value, element) {
+            value = (value !== undefined)
+              ? value
+              : text
+            ;
+            module.set.selected(value, $(element));
+            module.hideAndClear();
+          },
+
+          hide: function(text, value, element) {
+            module.set.value(value, text, $(element));
+            module.hideAndClear();
+          }
+
+        },
+
+        get: {
+          id: function() {
+            return id;
+          },
+          defaultText: function() {
+            return $module.data(metadata.defaultText);
+          },
+          defaultValue: function() {
+            return $module.data(metadata.defaultValue);
+          },
+          placeholderText: function() {
+            if(settings.placeholder != 'auto' && typeof settings.placeholder == 'string') {
+              return settings.placeholder;
+            }
+            return $module.data(metadata.placeholderText) || '';
+          },
+          text: function() {
+            return $text.text();
+          },
+          query: function() {
+            return $.trim($search.val());
+          },
+          searchWidth: function(value) {
+            value = (value !== undefined)
+              ? value
+              : $search.val()
+            ;
+            $sizer.text(value);
+            // prevent rounding issues
+            return Math.ceil( $sizer.width() + 1);
+          },
+          selectionCount: function() {
+            var
+              values = module.get.values(),
+              count
+            ;
+            count = ( module.is.multiple() )
+              ? Array.isArray(values)
+                ? values.length
+                : 0
+              : (module.get.value() !== '')
+                ? 1
+                : 0
+            ;
+            return count;
+          },
+          transition: function($subMenu) {
+            return (settings.transition == 'auto')
+              ? module.is.upward($subMenu)
+                ? 'slide up'
+                : 'slide down'
+              : settings.transition
+            ;
+          },
+          userValues: function() {
+            var
+              values = module.get.values()
+            ;
+            if(!values) {
+              return false;
+            }
+            values = Array.isArray(values)
+              ? values
+              : [values]
+            ;
+            return $.grep(values, function(value) {
+              return (module.get.item(value) === false);
+            });
+          },
+          uniqueArray: function(array) {
+            return $.grep(array, function (value, index) {
+                return $.inArray(value, array) === index;
+            });
+          },
+          caretPosition: function(returnEndPos) {
+            var
+              input = $search.get(0),
+              range,
+              rangeLength
+            ;
+            if(returnEndPos && 'selectionEnd' in input){
+              return input.selectionEnd;
+            }
+            else if(!returnEndPos && 'selectionStart' in input) {
+              return input.selectionStart;
+            }
+            if (document.selection) {
+              input.focus();
+              range       = document.selection.createRange();
+              rangeLength = range.text.length;
+              if(returnEndPos) {
+                return rangeLength;
+              }
+              range.moveStart('character', -input.value.length);
+              return range.text.length - rangeLength;
+            }
+          },
+          value: function() {
+            var
+              value = ($input.length > 0)
+                ? $input.val()
+                : $module.data(metadata.value),
+              isEmptyMultiselect = (Array.isArray(value) && value.length === 1 && value[0] === '')
+            ;
+            // prevents placeholder element from being selected when multiple
+            return (value === undefined || isEmptyMultiselect)
+              ? ''
+              : value
+            ;
+          },
+          values: function() {
+            var
+              value = module.get.value()
+            ;
+            if(value === '') {
+              return '';
+            }
+            return ( !module.has.selectInput() && module.is.multiple() )
+              ? (typeof value == 'string') // delimited string
+                ? module.escape.htmlEntities(value).split(settings.delimiter)
+                : ''
+              : value
+            ;
+          },
+          remoteValues: function() {
+            var
+              values = module.get.values(),
+              remoteValues = false
+            ;
+            if(values) {
+              if(typeof values == 'string') {
+                values = [values];
+              }
+              $.each(values, function(index, value) {
+                var
+                  name = module.read.remoteData(value)
+                ;
+                module.verbose('Restoring value from session data', name, value);
+                if(name) {
+                  if(!remoteValues) {
+                    remoteValues = {};
+                  }
+                  remoteValues[value] = name;
+                }
+              });
+            }
+            return remoteValues;
+          },
+          choiceText: function($choice, preserveHTML) {
+            preserveHTML = (preserveHTML !== undefined)
+              ? preserveHTML
+              : settings.preserveHTML
+            ;
+            if($choice) {
+              if($choice.find(selector.menu).length > 0) {
+                module.verbose('Retrieving text of element with sub-menu');
+                $choice = $choice.clone();
+                $choice.find(selector.menu).remove();
+                $choice.find(selector.menuIcon).remove();
+              }
+              return ($choice.data(metadata.text) !== undefined)
+                ? $choice.data(metadata.text)
+                : (preserveHTML)
+                  ? $.trim($choice.html())
+                  : $.trim($choice.text())
+              ;
+            }
+          },
+          choiceValue: function($choice, choiceText) {
+            choiceText = choiceText || module.get.choiceText($choice);
+            if(!$choice) {
+              return false;
+            }
+            return ($choice.data(metadata.value) !== undefined)
+              ? String( $choice.data(metadata.value) )
+              : (typeof choiceText === 'string')
+                ? $.trim(choiceText.toLowerCase())
+                : String(choiceText)
+            ;
+          },
+          inputEvent: function() {
+            var
+              input = $search[0]
+            ;
+            if(input) {
+              return (input.oninput !== undefined)
+                ? 'input'
+                : (input.onpropertychange !== undefined)
+                  ? 'propertychange'
+                  : 'keyup'
+              ;
+            }
+            return false;
+          },
+          selectValues: function() {
+            var
+              select = {},
+              oldGroup = []
+            ;
+            select.values = [];
+            $module
+              .find('option')
+                .each(function() {
+                  var
+                    $option  = $(this),
+                    name     = $option.html(),
+                    disabled = $option.attr('disabled'),
+                    value    = ( $option.attr('value') !== undefined )
+                      ? $option.attr('value')
+                      : name,
+                    group = $option.parent('optgroup')
+                  ;
+                  if(settings.placeholder === 'auto' && value === '') {
+                    select.placeholder = name;
+                  }
+                  else {
+                    if(group.length !== oldGroup.length || group[0] !== oldGroup[0]) {
+                      select.values.push({
+                        type: 'header',
+                        divider: settings.headerDivider,
+                        name: group.attr('label') || ''
+                      });
+                      oldGroup = group;
+                    }
+                    select.values.push({
+                      name     : name,
+                      value    : value,
+                      disabled : disabled
+                    });
+                  }
+                })
+            ;
+            if(settings.placeholder && settings.placeholder !== 'auto') {
+              module.debug('Setting placeholder value to', settings.placeholder);
+              select.placeholder = settings.placeholder;
+            }
+            if(settings.sortSelect) {
+              if(settings.sortSelect === true) {
+                select.values.sort(function(a, b) {
+                  return a.name.localeCompare(b.name);
+                });
+              } else if(settings.sortSelect === 'natural') {
+                select.values.sort(function(a, b) {
+                  return (a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
+                });
+              } else if($.isFunction(settings.sortSelect)) {
+                select.values.sort(settings.sortSelect);
+              }
+              module.debug('Retrieved and sorted values from select', select);
+            }
+            else {
+              module.debug('Retrieved values from select', select);
+            }
+            return select;
+          },
+          activeItem: function() {
+            return $item.filter('.'  + className.active);
+          },
+          selectedItem: function() {
+            var
+              $selectedItem = $item.not(selector.unselectable).filter('.'  + className.selected)
+            ;
+            return ($selectedItem.length > 0)
+              ? $selectedItem
+              : $item.eq(0)
+            ;
+          },
+          itemWithAdditions: function(value) {
+            var
+              $items       = module.get.item(value),
+              $userItems   = module.create.userChoice(value),
+              hasUserItems = ($userItems && $userItems.length > 0)
+            ;
+            if(hasUserItems) {
+              $items = ($items.length > 0)
+                ? $items.add($userItems)
+                : $userItems
+              ;
+            }
+            return $items;
+          },
+          item: function(value, strict) {
+            var
+              $selectedItem = false,
+              shouldSearch,
+              isMultiple
+            ;
+            value = (value !== undefined)
+              ? value
+              : ( module.get.values() !== undefined)
+                ? module.get.values()
+                : module.get.text()
+            ;
+            isMultiple = (module.is.multiple() && Array.isArray(value));
+            shouldSearch = (isMultiple)
+              ? (value.length > 0)
+              : (value !== undefined && value !== null)
+            ;
+            strict     = (value === '' || value === false  || value === true)
+              ? true
+              : strict || false
+            ;
+            if(shouldSearch) {
+              $item
+                .each(function() {
+                  var
+                    $choice       = $(this),
+                    optionText    = module.get.choiceText($choice),
+                    optionValue   = module.get.choiceValue($choice, optionText)
+                  ;
+                  // safe early exit
+                  if(optionValue === null || optionValue === undefined) {
+                    return;
+                  }
+                  if(isMultiple) {
+                    if($.inArray( String(optionValue), value) !== -1) {
+                      $selectedItem = ($selectedItem)
+                        ? $selectedItem.add($choice)
+                        : $choice
+                      ;
+                    }
+                  }
+                  else if(strict) {
+                    module.verbose('Ambiguous dropdown value using strict type check', $choice, value);
+                    if( optionValue === value) {
+                      $selectedItem = $choice;
+                      return true;
+                    }
+                  }
+                  else {
+                    if( String(optionValue) == String(value)) {
+                      module.verbose('Found select item by value', optionValue, value);
+                      $selectedItem = $choice;
+                      return true;
+                    }
+                  }
+                })
+              ;
+            }
+            return $selectedItem;
+          }
+        },
+
+        check: {
+          maxSelections: function(selectionCount) {
+            if(settings.maxSelections) {
+              selectionCount = (selectionCount !== undefined)
+                ? selectionCount
+                : module.get.selectionCount()
+              ;
+              if(selectionCount >= settings.maxSelections) {
+                module.debug('Maximum selection count reached');
+                if(settings.useLabels) {
+                  $item.addClass(className.filtered);
+                  module.add.message(message.maxSelections);
+                }
+                return true;
+              }
+              else {
+                module.verbose('No longer at maximum selection count');
+                module.remove.message();
+                module.remove.filteredItem();
+                if(module.is.searchSelection()) {
+                  module.filterItems();
+                }
+                return false;
+              }
+            }
+            return true;
+          }
+        },
+
+        restore: {
+          defaults: function() {
+            module.clear();
+            module.restore.defaultText();
+            module.restore.defaultValue();
+          },
+          defaultText: function() {
+            var
+              defaultText     = module.get.defaultText(),
+              placeholderText = module.get.placeholderText
+            ;
+            if(defaultText === placeholderText) {
+              module.debug('Restoring default placeholder text', defaultText);
+              module.set.placeholderText(defaultText);
+            }
+            else {
+              module.debug('Restoring default text', defaultText);
+              module.set.text(defaultText);
+            }
+          },
+          placeholderText: function() {
+            module.set.placeholderText();
+          },
+          defaultValue: function() {
+            var
+              defaultValue = module.get.defaultValue()
+            ;
+            if(defaultValue !== undefined) {
+              module.debug('Restoring default value', defaultValue);
+              if(defaultValue !== '') {
+                module.set.value(defaultValue);
+                module.set.selected();
+              }
+              else {
+                module.remove.activeItem();
+                module.remove.selectedItem();
+              }
+            }
+          },
+          labels: function() {
+            if(settings.allowAdditions) {
+              if(!settings.useLabels) {
+                module.error(error.labels);
+                settings.useLabels = true;
+              }
+              module.debug('Restoring selected values');
+              module.create.userLabels();
+            }
+            module.check.maxSelections();
+          },
+          selected: function() {
+            module.restore.values();
+            if(module.is.multiple()) {
+              module.debug('Restoring previously selected values and labels');
+              module.restore.labels();
+            }
+            else {
+              module.debug('Restoring previously selected values');
+            }
+          },
+          values: function() {
+            // prevents callbacks from occurring on initial load
+            module.set.initialLoad();
+            if(settings.apiSettings && settings.saveRemoteData && module.get.remoteValues()) {
+              module.restore.remoteValues();
+            }
+            else {
+              module.set.selected();
+            }
+            var value = module.get.value();
+            if(value && value !== '' && !(Array.isArray(value) && value.length === 0)) {
+              $input.removeClass(className.noselection);
+            } else {
+              $input.addClass(className.noselection);
+            }
+            module.remove.initialLoad();
+          },
+          remoteValues: function() {
+            var
+              values = module.get.remoteValues()
+            ;
+            module.debug('Recreating selected from session data', values);
+            if(values) {
+              if( module.is.single() ) {
+                $.each(values, function(value, name) {
+                  module.set.text(name);
+                });
+              }
+              else {
+                $.each(values, function(value, name) {
+                  module.add.label(value, name);
+                });
+              }
+            }
+          }
+        },
+
+        read: {
+          remoteData: function(value) {
+            var
+              name
+            ;
+            if(window.Storage === undefined) {
+              module.error(error.noStorage);
+              return;
+            }
+            name = sessionStorage.getItem(value);
+            return (name !== undefined)
+              ? name
+              : false
+            ;
+          }
+        },
+
+        save: {
+          defaults: function() {
+            module.save.defaultText();
+            module.save.placeholderText();
+            module.save.defaultValue();
+          },
+          defaultValue: function() {
+            var
+              value = module.get.value()
+            ;
+            module.verbose('Saving default value as', value);
+            $module.data(metadata.defaultValue, value);
+          },
+          defaultText: function() {
+            var
+              text = module.get.text()
+            ;
+            module.verbose('Saving default text as', text);
+            $module.data(metadata.defaultText, text);
+          },
+          placeholderText: function() {
+            var
+              text
+            ;
+            if(settings.placeholder !== false && $text.hasClass(className.placeholder)) {
+              text = module.get.text();
+              module.verbose('Saving placeholder text as', text);
+              $module.data(metadata.placeholderText, text);
+            }
+          },
+          remoteData: function(name, value) {
+            if(window.Storage === undefined) {
+              module.error(error.noStorage);
+              return;
+            }
+            module.verbose('Saving remote data to session storage', value, name);
+            sessionStorage.setItem(value, name);
+          }
+        },
+
+        clear: function() {
+          if(module.is.multiple() && settings.useLabels) {
+            module.remove.labels();
+          }
+          else {
+            module.remove.activeItem();
+            module.remove.selectedItem();
+            module.remove.filteredItem();
+          }
+          module.set.placeholderText();
+          module.clearValue();
+        },
+
+        clearValue: function() {
+          module.set.value('');
+        },
+
+        scrollPage: function(direction, $selectedItem) {
+          var
+            $currentItem  = $selectedItem || module.get.selectedItem(),
+            $menu         = $currentItem.closest(selector.menu),
+            menuHeight    = $menu.outerHeight(),
+            currentScroll = $menu.scrollTop(),
+            itemHeight    = $item.eq(0).outerHeight(),
+            itemsPerPage  = Math.floor(menuHeight / itemHeight),
+            maxScroll     = $menu.prop('scrollHeight'),
+            newScroll     = (direction == 'up')
+              ? currentScroll - (itemHeight * itemsPerPage)
+              : currentScroll + (itemHeight * itemsPerPage),
+            $selectableItem = $item.not(selector.unselectable),
+            isWithinRange,
+            $nextSelectedItem,
+            elementIndex
+          ;
+          elementIndex      = (direction == 'up')
+            ? $selectableItem.index($currentItem) - itemsPerPage
+            : $selectableItem.index($currentItem) + itemsPerPage
+          ;
+          isWithinRange = (direction == 'up')
+            ? (elementIndex >= 0)
+            : (elementIndex < $selectableItem.length)
+          ;
+          $nextSelectedItem = (isWithinRange)
+            ? $selectableItem.eq(elementIndex)
+            : (direction == 'up')
+              ? $selectableItem.first()
+              : $selectableItem.last()
+          ;
+          if($nextSelectedItem.length > 0) {
+            module.debug('Scrolling page', direction, $nextSelectedItem);
+            $currentItem
+              .removeClass(className.selected)
+            ;
+            $nextSelectedItem
+              .addClass(className.selected)
+            ;
+            if(settings.selectOnKeydown && module.is.single()) {
+              module.set.selectedItem($nextSelectedItem);
+            }
+            $menu
+              .scrollTop(newScroll)
+            ;
+          }
+        },
+
+        set: {
+          filtered: function() {
+            var
+              isMultiple       = module.is.multiple(),
+              isSearch         = module.is.searchSelection(),
+              isSearchMultiple = (isMultiple && isSearch),
+              searchValue      = (isSearch)
+                ? module.get.query()
+                : '',
+              hasSearchValue   = (typeof searchValue === 'string' && searchValue.length > 0),
+              searchWidth      = module.get.searchWidth(),
+              valueIsSet       = searchValue !== ''
+            ;
+            if(isMultiple && hasSearchValue) {
+              module.verbose('Adjusting input width', searchWidth, settings.glyphWidth);
+              $search.css('width', searchWidth);
+            }
+            if(hasSearchValue || (isSearchMultiple && valueIsSet)) {
+              module.verbose('Hiding placeholder text');
+              $text.addClass(className.filtered);
+            }
+            else if(!isMultiple || (isSearchMultiple && !valueIsSet)) {
+              module.verbose('Showing placeholder text');
+              $text.removeClass(className.filtered);
+            }
+          },
+          empty: function() {
+            $module.addClass(className.empty);
+          },
+          loading: function() {
+            $module.addClass(className.loading);
+          },
+          placeholderText: function(text) {
+            text = text || module.get.placeholderText();
+            module.debug('Setting placeholder text', text);
+            module.set.text(text);
+            $text.addClass(className.placeholder);
+          },
+          tabbable: function() {
+            if( module.is.searchSelection() ) {
+              module.debug('Added tabindex to searchable dropdown');
+              $search
+                .val('')
+                .attr('tabindex', 0)
+              ;
+              $menu
+                .attr('tabindex', -1)
+              ;
+            }
+            else {
+              module.debug('Added tabindex to dropdown');
+              if( $module.attr('tabindex') === undefined) {
+                $module
+                  .attr('tabindex', 0)
+                ;
+                $menu
+                  .attr('tabindex', -1)
+                ;
+              }
+            }
+          },
+          initialLoad: function() {
+            module.verbose('Setting initial load');
+            initialLoad = true;
+          },
+          activeItem: function($item) {
+            if( settings.allowAdditions && $item.filter(selector.addition).length > 0 ) {
+              $item.addClass(className.filtered);
+            }
+            else {
+              $item.addClass(className.active);
+            }
+          },
+          partialSearch: function(text) {
+            var
+              length = module.get.query().length
+            ;
+            $search.val( text.substr(0, length));
+          },
+          scrollPosition: function($item, forceScroll) {
+            var
+              edgeTolerance = 5,
+              $menu,
+              hasActive,
+              offset,
+              itemHeight,
+              itemOffset,
+              menuOffset,
+              menuScroll,
+              menuHeight,
+              abovePage,
+              belowPage
+            ;
+
+            $item       = $item || module.get.selectedItem();
+            $menu       = $item.closest(selector.menu);
+            hasActive   = ($item && $item.length > 0);
+            forceScroll = (forceScroll !== undefined)
+              ? forceScroll
+              : false
+            ;
+            if(module.get.activeItem().length === 0){
+              forceScroll = false;
+            }
+            if($item && $menu.length > 0 && hasActive) {
+              itemOffset = $item.position().top;
+
+              $menu.addClass(className.loading);
+              menuScroll = $menu.scrollTop();
+              menuOffset = $menu.offset().top;
+              itemOffset = $item.offset().top;
+              offset     = menuScroll - menuOffset + itemOffset;
+              if(!forceScroll) {
+                menuHeight = $menu.height();
+                belowPage  = menuScroll + menuHeight < (offset + edgeTolerance);
+                abovePage  = ((offset - edgeTolerance) < menuScroll);
+              }
+              module.debug('Scrolling to active item', offset);
+              if(forceScroll || abovePage || belowPage) {
+                $menu.scrollTop(offset);
+              }
+              $menu.removeClass(className.loading);
+            }
+          },
+          text: function(text) {
+            if(settings.action === 'combo') {
+              module.debug('Changing combo button text', text, $combo);
+              if(settings.preserveHTML) {
+                $combo.html(text);
+              }
+              else {
+                $combo.text(text);
+              }
+            }
+            else if(settings.action === 'activate') {
+              if(text !== module.get.placeholderText()) {
+                $text.removeClass(className.placeholder);
+              }
+              module.debug('Changing text', text, $text);
+              $text
+                .removeClass(className.filtered)
+              ;
+              if(settings.preserveHTML) {
+                $text.html(text);
+              }
+              else {
+                $text.text(text);
+              }
+            }
+          },
+          selectedItem: function($item) {
+            var
+              value      = module.get.choiceValue($item),
+              searchText = module.get.choiceText($item, false),
+              text       = module.get.choiceText($item, true)
+            ;
+            module.debug('Setting user selection to item', $item);
+            module.remove.activeItem();
+            module.set.partialSearch(searchText);
+            module.set.activeItem($item);
+            module.set.selected(value, $item);
+            module.set.text(text);
+          },
+          selectedLetter: function(letter) {
+            var
+              $selectedItem         = $item.filter('.' + className.selected),
+              alreadySelectedLetter = $selectedItem.length > 0 && module.has.firstLetter($selectedItem, letter),
+              $nextValue            = false,
+              $nextItem
+            ;
+            // check next of same letter
+            if(alreadySelectedLetter) {
+              $nextItem = $selectedItem.nextAll($item).eq(0);
+              if( module.has.firstLetter($nextItem, letter) ) {
+                $nextValue  = $nextItem;
+              }
+            }
+            // check all values
+            if(!$nextValue) {
+              $item
+                .each(function(){
+                  if(module.has.firstLetter($(this), letter)) {
+                    $nextValue = $(this);
+                    return false;
+                  }
+                })
+              ;
+            }
+            // set next value
+            if($nextValue) {
+              module.verbose('Scrolling to next value with letter', letter);
+              module.set.scrollPosition($nextValue);
+              $selectedItem.removeClass(className.selected);
+              $nextValue.addClass(className.selected);
+              if(settings.selectOnKeydown && module.is.single()) {
+                module.set.selectedItem($nextValue);
+              }
+            }
+          },
+          direction: function($menu) {
+            if(settings.direction == 'auto') {
+              // reset position, remove upward if it's base menu
+              if (!$menu) {
+                module.remove.upward();
+              } else if (module.is.upward($menu)) {
+                //we need make sure when make assertion openDownward for $menu, $menu does not have upward class
+                module.remove.upward($menu);
+              }
+
+              if(module.can.openDownward($menu)) {
+                module.remove.upward($menu);
+              }
+              else {
+                module.set.upward($menu);
+              }
+              if(!module.is.leftward($menu) && !module.can.openRightward($menu)) {
+                module.set.leftward($menu);
+              }
+            }
+            else if(settings.direction == 'upward') {
+              module.set.upward($menu);
+            }
+          },
+          upward: function($currentMenu) {
+            var $element = $currentMenu || $module;
+            $element.addClass(className.upward);
+          },
+          leftward: function($currentMenu) {
+            var $element = $currentMenu || $menu;
+            $element.addClass(className.leftward);
+          },
+          value: function(value, text, $selected) {
+            if(value !== undefined && value !== '' && !(Array.isArray(value) && value.length === 0)) {
+              $input.removeClass(className.noselection);
+            } else {
+              $input.addClass(className.noselection);
+            }
+            var
+              escapedValue = module.escape.value(value),
+              hasInput     = ($input.length > 0),
+              currentValue = module.get.values(),
+              stringValue  = (value !== undefined)
+                ? String(value)
+                : value,
+              newValue
+            ;
+            if(hasInput) {
+              if(!settings.allowReselection && stringValue == currentValue) {
+                module.verbose('Skipping value update already same value', value, currentValue);
+                if(!module.is.initialLoad()) {
+                  return;
+                }
+              }
+
+              if( module.is.single() && module.has.selectInput() && module.can.extendSelect() ) {
+                module.debug('Adding user option', value);
+                module.add.optionValue(value);
+              }
+              module.debug('Updating input value', escapedValue, currentValue);
+              internalChange = true;
+              $input
+                .val(escapedValue)
+              ;
+              if(settings.fireOnInit === false && module.is.initialLoad()) {
+                module.debug('Input native change event ignored on initial load');
+              }
+              else {
+                module.trigger.change();
+              }
+              internalChange = false;
+            }
+            else {
+              module.verbose('Storing value in metadata', escapedValue, $input);
+              if(escapedValue !== currentValue) {
+                $module.data(metadata.value, stringValue);
+              }
+            }
+            if(settings.fireOnInit === false && module.is.initialLoad()) {
+              module.verbose('No callback on initial load', settings.onChange);
+            }
+            else {
+              settings.onChange.call(element, value, text, $selected);
+            }
+          },
+          active: function() {
+            $module
+              .addClass(className.active)
+            ;
+          },
+          multiple: function() {
+            $module.addClass(className.multiple);
+          },
+          visible: function() {
+            $module.addClass(className.visible);
+          },
+          exactly: function(value, $selectedItem) {
+            module.debug('Setting selected to exact values');
+            module.clear();
+            module.set.selected(value, $selectedItem);
+          },
+          selected: function(value, $selectedItem) {
+            var
+              isMultiple = module.is.multiple()
+            ;
+            $selectedItem = (settings.allowAdditions)
+              ? $selectedItem || module.get.itemWithAdditions(value)
+              : $selectedItem || module.get.item(value)
+            ;
+            if(!$selectedItem) {
+              return;
+            }
+            module.debug('Setting selected menu item to', $selectedItem);
+            if(module.is.multiple()) {
+              module.remove.searchWidth();
+            }
+            if(module.is.single()) {
+              module.remove.activeItem();
+              module.remove.selectedItem();
+            }
+            else if(settings.useLabels) {
+              module.remove.selectedItem();
+            }
+            // select each item
+            $selectedItem
+              .each(function() {
+                var
+                  $selected      = $(this),
+                  selectedText   = module.get.choiceText($selected),
+                  selectedValue  = module.get.choiceValue($selected, selectedText),
+
+                  isFiltered     = $selected.hasClass(className.filtered),
+                  isActive       = $selected.hasClass(className.active),
+                  isUserValue    = $selected.hasClass(className.addition),
+                  shouldAnimate  = (isMultiple && $selectedItem.length == 1)
+                ;
+                if(isMultiple) {
+                  if(!isActive || isUserValue) {
+                    if(settings.apiSettings && settings.saveRemoteData) {
+                      module.save.remoteData(selectedText, selectedValue);
+                    }
+                    if(settings.useLabels) {
+                      module.add.label(selectedValue, selectedText, shouldAnimate);
+                      module.add.value(selectedValue, selectedText, $selected);
+                      module.set.activeItem($selected);
+                      module.filterActive();
+                      module.select.nextAvailable($selectedItem);
+                    }
+                    else {
+                      module.add.value(selectedValue, selectedText, $selected);
+                      module.set.text(module.add.variables(message.count));
+                      module.set.activeItem($selected);
+                    }
+                  }
+                  else if(!isFiltered && (settings.useLabels || selectActionActive)) {
+                    module.debug('Selected active value, removing label');
+                    module.remove.selected(selectedValue);
+                  }
+                }
+                else {
+                  if(settings.apiSettings && settings.saveRemoteData) {
+                    module.save.remoteData(selectedText, selectedValue);
+                  }
+                  module.set.text(selectedText);
+                  module.set.value(selectedValue, selectedText, $selected);
+                  $selected
+                    .addClass(className.active)
+                    .addClass(className.selected)
+                  ;
+                }
+              })
+            ;
+          },
+        },
+
+        add: {
+          label: function(value, text, shouldAnimate) {
+            var
+              $next  = module.is.searchSelection()
+                ? $search
+                : $text,
+              escapedValue = module.escape.value(value),
+              $label
+            ;
+            if(settings.ignoreCase) {
+              escapedValue = escapedValue.toLowerCase();
+            }
+            $label =  $('<a />')
+              .addClass(className.label)
+              .attr('data-' + metadata.value, escapedValue)
+              .html(templates.label(escapedValue, text, settings.preserveHTML, settings.className))
+            ;
+            $label = settings.onLabelCreate.call($label, escapedValue, text);
+
+            if(module.has.label(value)) {
+              module.debug('User selection already exists, skipping', escapedValue);
+              return;
+            }
+            if(settings.label.variation) {
+              $label.addClass(settings.label.variation);
+            }
+            if(shouldAnimate === true) {
+              module.debug('Animating in label', $label);
+              $label
+                .addClass(className.hidden)
+                .insertBefore($next)
+                .transition({
+                    animation  : settings.label.transition,
+                    debug      : settings.debug,
+                    verbose    : settings.verbose,
+                    duration   : settings.label.duration
+                })
+              ;
+            }
+            else {
+              module.debug('Adding selection label', $label);
+              $label
+                .insertBefore($next)
+              ;
+            }
+          },
+          message: function(message) {
+            var
+              $message = $menu.children(selector.message),
+              html     = settings.templates.message(module.add.variables(message))
+            ;
+            if($message.length > 0) {
+              $message
+                .html(html)
+              ;
+            }
+            else {
+              $message = $('<div/>')
+                .html(html)
+                .addClass(className.message)
+                .appendTo($menu)
+              ;
+            }
+          },
+          optionValue: function(value) {
+            var
+              escapedValue = module.escape.value(value),
+              $option      = $input.find('option[value="' + module.escape.string(escapedValue) + '"]'),
+              hasOption    = ($option.length > 0)
+            ;
+            if(hasOption) {
+              return;
+            }
+            // temporarily disconnect observer
+            module.disconnect.selectObserver();
+            if( module.is.single() ) {
+              module.verbose('Removing previous user addition');
+              $input.find('option.' + className.addition).remove();
+            }
+            $('<option/>')
+              .prop('value', escapedValue)
+              .addClass(className.addition)
+              .html(value)
+              .appendTo($input)
+            ;
+            module.verbose('Adding user addition as an <option>', value);
+            module.observe.select();
+          },
+          userSuggestion: function(value) {
+            var
+              $addition         = $menu.children(selector.addition),
+              $existingItem     = module.get.item(value),
+              alreadyHasValue   = $existingItem && $existingItem.not(selector.addition).length,
+              hasUserSuggestion = $addition.length > 0,
+              html
+            ;
+            if(settings.useLabels && module.has.maxSelections()) {
+              return;
+            }
+            if(value === '' || alreadyHasValue) {
+              $addition.remove();
+              return;
+            }
+            if(hasUserSuggestion) {
+              $addition
+                .data(metadata.value, value)
+                .data(metadata.text, value)
+                .attr('data-' + metadata.value, value)
+                .attr('data-' + metadata.text, value)
+                .removeClass(className.filtered)
+              ;
+              if(!settings.hideAdditions) {
+                html = settings.templates.addition( module.add.variables(message.addResult, value) );
+                $addition
+                  .html(html)
+                ;
+              }
+              module.verbose('Replacing user suggestion with new value', $addition);
+            }
+            else {
+              $addition = module.create.userChoice(value);
+              $addition
+                .prependTo($menu)
+              ;
+              module.verbose('Adding item choice to menu corresponding with user choice addition', $addition);
+            }
+            if(!settings.hideAdditions || module.is.allFiltered()) {
+              $addition
+                .addClass(className.selected)
+                .siblings()
+                .removeClass(className.selected)
+              ;
+            }
+            module.refreshItems();
+          },
+          variables: function(message, term) {
+            var
+              hasCount    = (message.search('{count}') !== -1),
+              hasMaxCount = (message.search('{maxCount}') !== -1),
+              hasTerm     = (message.search('{term}') !== -1),
+              count,
+              query
+            ;
+            module.verbose('Adding templated variables to message', message);
+            if(hasCount) {
+              count  = module.get.selectionCount();
+              message = message.replace('{count}', count);
+            }
+            if(hasMaxCount) {
+              count  = module.get.selectionCount();
+              message = message.replace('{maxCount}', settings.maxSelections);
+            }
+            if(hasTerm) {
+              query   = term || module.get.query();
+              message = message.replace('{term}', query);
+            }
+            return message;
+          },
+          value: function(addedValue, addedText, $selectedItem) {
+            var
+              currentValue = module.get.values(),
+              newValue
+            ;
+            if(module.has.value(addedValue)) {
+              module.debug('Value already selected');
+              return;
+            }
+            if(addedValue === '') {
+              module.debug('Cannot select blank values from multiselect');
+              return;
+            }
+            // extend current array
+            if(Array.isArray(currentValue)) {
+              newValue = currentValue.concat([addedValue]);
+              newValue = module.get.uniqueArray(newValue);
+            }
+            else {
+              newValue = [addedValue];
+            }
+            // add values
+            if( module.has.selectInput() ) {
+              if(module.can.extendSelect()) {
+                module.debug('Adding value to select', addedValue, newValue, $input);
+                module.add.optionValue(addedValue);
+              }
+            }
+            else {
+              newValue = newValue.join(settings.delimiter);
+              module.debug('Setting hidden input to delimited value', newValue, $input);
+            }
+
+            if(settings.fireOnInit === false && module.is.initialLoad()) {
+              module.verbose('Skipping onadd callback on initial load', settings.onAdd);
+            }
+            else {
+              settings.onAdd.call(element, addedValue, addedText, $selectedItem);
+            }
+            module.set.value(newValue, addedValue, addedText, $selectedItem);
+            module.check.maxSelections();
+          },
+        },
+
+        remove: {
+          active: function() {
+            $module.removeClass(className.active);
+          },
+          activeLabel: function() {
+            $module.find(selector.label).removeClass(className.active);
+          },
+          empty: function() {
+            $module.removeClass(className.empty);
+          },
+          loading: function() {
+            $module.removeClass(className.loading);
+          },
+          initialLoad: function() {
+            initialLoad = false;
+          },
+          upward: function($currentMenu) {
+            var $element = $currentMenu || $module;
+            $element.removeClass(className.upward);
+          },
+          leftward: function($currentMenu) {
+            var $element = $currentMenu || $menu;
+            $element.removeClass(className.leftward);
+          },
+          visible: function() {
+            $module.removeClass(className.visible);
+          },
+          activeItem: function() {
+            $item.removeClass(className.active);
+          },
+          filteredItem: function() {
+            if(settings.useLabels && module.has.maxSelections() ) {
+              return;
+            }
+            if(settings.useLabels && module.is.multiple()) {
+              $item.not('.' + className.active).removeClass(className.filtered);
+            }
+            else {
+              $item.removeClass(className.filtered);
+            }
+            if(settings.hideDividers) {
+              $divider.removeClass(className.hidden);
+            }
+            module.remove.empty();
+          },
+          optionValue: function(value) {
+            var
+              escapedValue = module.escape.value(value),
+              $option      = $input.find('option[value="' + module.escape.string(escapedValue) + '"]'),
+              hasOption    = ($option.length > 0)
+            ;
+            if(!hasOption || !$option.hasClass(className.addition)) {
+              return;
+            }
+            // temporarily disconnect observer
+            if(selectObserver) {
+              selectObserver.disconnect();
+              module.verbose('Temporarily disconnecting mutation observer');
+            }
+            $option.remove();
+            module.verbose('Removing user addition as an <option>', escapedValue);
+            if(selectObserver) {
+              selectObserver.observe($input[0], {
+                childList : true,
+                subtree   : true
+              });
+            }
+          },
+          message: function() {
+            $menu.children(selector.message).remove();
+          },
+          searchWidth: function() {
+            $search.css('width', '');
+          },
+          searchTerm: function() {
+            module.verbose('Cleared search term');
+            $search.val('');
+            module.set.filtered();
+          },
+          userAddition: function() {
+            $item.filter(selector.addition).remove();
+          },
+          selected: function(value, $selectedItem) {
+            $selectedItem = (settings.allowAdditions)
+              ? $selectedItem || module.get.itemWithAdditions(value)
+              : $selectedItem || module.get.item(value)
+            ;
+
+            if(!$selectedItem) {
+              return false;
+            }
+
+            $selectedItem
+              .each(function() {
+                var
+                  $selected     = $(this),
+                  selectedText  = module.get.choiceText($selected),
+                  selectedValue = module.get.choiceValue($selected, selectedText)
+                ;
+                if(module.is.multiple()) {
+                  if(settings.useLabels) {
+                    module.remove.value(selectedValue, selectedText, $selected);
+                    module.remove.label(selectedValue);
+                  }
+                  else {
+                    module.remove.value(selectedValue, selectedText, $selected);
+                    if(module.get.selectionCount() === 0) {
+                      module.set.placeholderText();
+                    }
+                    else {
+                      module.set.text(module.add.variables(message.count));
+                    }
+                  }
+                }
+                else {
+                  module.remove.value(selectedValue, selectedText, $selected);
+                }
+                $selected
+                  .removeClass(className.filtered)
+                  .removeClass(className.active)
+                ;
+                if(settings.useLabels) {
+                  $selected.removeClass(className.selected);
+                }
+              })
+            ;
+          },
+          selectedItem: function() {
+            $item.removeClass(className.selected);
+          },
+          value: function(removedValue, removedText, $removedItem) {
+            var
+              values = module.get.values(),
+              newValue
+            ;
+            if( module.has.selectInput() ) {
+              module.verbose('Input is <select> removing selected option', removedValue);
+              newValue = module.remove.arrayValue(removedValue, values);
+              module.remove.optionValue(removedValue);
+            }
+            else {
+              module.verbose('Removing from delimited values', removedValue);
+              newValue = module.remove.arrayValue(removedValue, values);
+              newValue = newValue.join(settings.delimiter);
+            }
+            if(settings.fireOnInit === false && module.is.initialLoad()) {
+              module.verbose('No callback on initial load', settings.onRemove);
+            }
+            else {
+              settings.onRemove.call(element, removedValue, removedText, $removedItem);
+            }
+            module.set.value(newValue, removedText, $removedItem);
+            module.check.maxSelections();
+          },
+          arrayValue: function(removedValue, values) {
+            if( !Array.isArray(values) ) {
+              values = [values];
+            }
+            values = $.grep(values, function(value){
+              return (removedValue != value);
+            });
+            module.verbose('Removed value from delimited string', removedValue, values);
+            return values;
+          },
+          label: function(value, shouldAnimate) {
+            var
+              $labels       = $module.find(selector.label),
+              $removedLabel = $labels.filter('[data-' + metadata.value + '="' + module.escape.string(value) +'"]')
+            ;
+            module.verbose('Removing label', $removedLabel);
+            $removedLabel.remove();
+          },
+          activeLabels: function($activeLabels) {
+            $activeLabels = $activeLabels || $module.find(selector.label).filter('.' + className.active);
+            module.verbose('Removing active label selections', $activeLabels);
+            module.remove.labels($activeLabels);
+          },
+          labels: function($labels) {
+            $labels = $labels || $module.find(selector.label);
+            module.verbose('Removing labels', $labels);
+            $labels
+              .each(function(){
+                var
+                  $label      = $(this),
+                  value       = $label.data(metadata.value),
+                  stringValue = (value !== undefined)
+                    ? String(value)
+                    : value,
+                  isUserValue = module.is.userValue(stringValue)
+                ;
+                if(settings.onLabelRemove.call($label, value) === false) {
+                  module.debug('Label remove callback cancelled removal');
+                  return;
+                }
+                module.remove.message();
+                if(isUserValue) {
+                  module.remove.value(stringValue);
+                  module.remove.label(stringValue);
+                }
+                else {
+                  // selected will also remove label
+                  module.remove.selected(stringValue);
+                }
+              })
+            ;
+          },
+          tabbable: function() {
+            if( module.is.searchSelection() ) {
+              module.debug('Searchable dropdown initialized');
+              $search
+                .removeAttr('tabindex')
+              ;
+              $menu
+                .removeAttr('tabindex')
+              ;
+            }
+            else {
+              module.debug('Simple selection dropdown initialized');
+              $module
+                .removeAttr('tabindex')
+              ;
+              $menu
+                .removeAttr('tabindex')
+              ;
+            }
+          },
+          diacritics: function(text) {
+            return settings.ignoreDiacritics ?  text.normalize('NFD').replace(/[\u0300-\u036f]/g, '') : text;
+          }
+        },
+
+        has: {
+          menuSearch: function() {
+            return (module.has.search() && $search.closest($menu).length > 0);
+          },
+          clearItem: function() {
+            return ($clear.length > 0);
+          },
+          search: function() {
+            return ($search.length > 0);
+          },
+          sizer: function() {
+            return ($sizer.length > 0);
+          },
+          selectInput: function() {
+            return ( $input.is('select') );
+          },
+          minCharacters: function(searchTerm) {
+            if(settings.minCharacters && !iconClicked) {
+              searchTerm = (searchTerm !== undefined)
+                ? String(searchTerm)
+                : String(module.get.query())
+              ;
+              return (searchTerm.length >= settings.minCharacters);
+            }
+            iconClicked=false;
+            return true;
+          },
+          firstLetter: function($item, letter) {
+            var
+              text,
+              firstLetter
+            ;
+            if(!$item || $item.length === 0 || typeof letter !== 'string') {
+              return false;
+            }
+            text        = module.get.choiceText($item, false);
+            letter      = letter.toLowerCase();
+            firstLetter = String(text).charAt(0).toLowerCase();
+            return (letter == firstLetter);
+          },
+          input: function() {
+            return ($input.length > 0);
+          },
+          items: function() {
+            return ($item.length > 0);
+          },
+          menu: function() {
+            return ($menu.length > 0);
+          },
+          message: function() {
+            return ($menu.children(selector.message).length !== 0);
+          },
+          label: function(value) {
+            var
+              escapedValue = module.escape.value(value),
+              $labels      = $module.find(selector.label)
+            ;
+            if(settings.ignoreCase) {
+              escapedValue = escapedValue.toLowerCase();
+            }
+            return ($labels.filter('[data-' + metadata.value + '="' + module.escape.string(escapedValue) +'"]').length > 0);
+          },
+          maxSelections: function() {
+            return (settings.maxSelections && module.get.selectionCount() >= settings.maxSelections);
+          },
+          allResultsFiltered: function() {
+            var
+              $normalResults = $item.not(selector.addition)
+            ;
+            return ($normalResults.filter(selector.unselectable).length === $normalResults.length);
+          },
+          userSuggestion: function() {
+            return ($menu.children(selector.addition).length > 0);
+          },
+          query: function() {
+            return (module.get.query() !== '');
+          },
+          value: function(value) {
+            return (settings.ignoreCase)
+              ? module.has.valueIgnoringCase(value)
+              : module.has.valueMatchingCase(value)
+            ;
+          },
+          valueMatchingCase: function(value) {
+            var
+              values   = module.get.values(),
+              hasValue = Array.isArray(values)
+               ? values && ($.inArray(value, values) !== -1)
+               : (values == value)
+            ;
+            return (hasValue)
+              ? true
+              : false
+            ;
+          },
+          valueIgnoringCase: function(value) {
+            var
+              values   = module.get.values(),
+              hasValue = false
+            ;
+            if(!Array.isArray(values)) {
+              values = [values];
+            }
+            $.each(values, function(index, existingValue) {
+              if(String(value).toLowerCase() == String(existingValue).toLowerCase()) {
+                hasValue = true;
+                return false;
+              }
+            });
+            return hasValue;
+          }
+        },
+
+        is: {
+          active: function() {
+            return $module.hasClass(className.active);
+          },
+          animatingInward: function() {
+            return $menu.transition('is inward');
+          },
+          animatingOutward: function() {
+            return $menu.transition('is outward');
+          },
+          bubbledLabelClick: function(event) {
+            return $(event.target).is('select, input') && $module.closest('label').length > 0;
+          },
+          bubbledIconClick: function(event) {
+            return $(event.target).closest($icon).length > 0;
+          },
+          alreadySetup: function() {
+            return ($module.is('select') && $module.parent(selector.dropdown).data(moduleNamespace) !== undefined && $module.prev().length === 0);
+          },
+          animating: function($subMenu) {
+            return ($subMenu)
+              ? $subMenu.transition && $subMenu.transition('is animating')
+              : $menu.transition    && $menu.transition('is animating')
+            ;
+          },
+          leftward: function($subMenu) {
+            var $selectedMenu = $subMenu || $menu;
+            return $selectedMenu.hasClass(className.leftward);
+          },
+          clearable: function() {
+            return ($module.hasClass(className.clearable) || settings.clearable);
+          },
+          disabled: function() {
+            return $module.hasClass(className.disabled);
+          },
+          focused: function() {
+            return (document.activeElement === $module[0]);
+          },
+          focusedOnSearch: function() {
+            return (document.activeElement === $search[0]);
+          },
+          allFiltered: function() {
+            return( (module.is.multiple() || module.has.search()) && !(settings.hideAdditions == false && module.has.userSuggestion()) && !module.has.message() && module.has.allResultsFiltered() );
+          },
+          hidden: function($subMenu) {
+            return !module.is.visible($subMenu);
+          },
+          initialLoad: function() {
+            return initialLoad;
+          },
+          inObject: function(needle, object) {
+            var
+              found = false
+            ;
+            $.each(object, function(index, property) {
+              if(property == needle) {
+                found = true;
+                return true;
+              }
+            });
+            return found;
+          },
+          multiple: function() {
+            return $module.hasClass(className.multiple);
+          },
+          remote: function() {
+            return settings.apiSettings && module.can.useAPI();
+          },
+          single: function() {
+            return !module.is.multiple();
+          },
+          selectMutation: function(mutations) {
+            var
+              selectChanged = false
+            ;
+            $.each(mutations, function(index, mutation) {
+              if($(mutation.target).is('select') || $(mutation.addedNodes).is('select')) {
+                selectChanged = true;
+                return false;
+              }
+            });
+            return selectChanged;
+          },
+          search: function() {
+            return $module.hasClass(className.search);
+          },
+          searchSelection: function() {
+            return ( module.has.search() && $search.parent(selector.dropdown).length === 1 );
+          },
+          selection: function() {
+            return $module.hasClass(className.selection);
+          },
+          userValue: function(value) {
+            return ($.inArray(value, module.get.userValues()) !== -1);
+          },
+          upward: function($menu) {
+            var $element = $menu || $module;
+            return $element.hasClass(className.upward);
+          },
+          visible: function($subMenu) {
+            return ($subMenu)
+              ? $subMenu.hasClass(className.visible)
+              : $menu.hasClass(className.visible)
+            ;
+          },
+          verticallyScrollableContext: function() {
+            var
+              overflowY = ($context.get(0) !== window)
+                ? $context.css('overflow-y')
+                : false
+            ;
+            return (overflowY == 'auto' || overflowY == 'scroll');
+          },
+          horizontallyScrollableContext: function() {
+            var
+              overflowX = ($context.get(0) !== window)
+                ? $context.css('overflow-X')
+                : false
+            ;
+            return (overflowX == 'auto' || overflowX == 'scroll');
+          }
+        },
+
+        can: {
+          activate: function($item) {
+            if(settings.useLabels) {
+              return true;
+            }
+            if(!module.has.maxSelections()) {
+              return true;
+            }
+            if(module.has.maxSelections() && $item.hasClass(className.active)) {
+              return true;
+            }
+            return false;
+          },
+          openDownward: function($subMenu) {
+            var
+              $currentMenu    = $subMenu || $menu,
+              canOpenDownward = true,
+              onScreen        = {},
+              calculations
+            ;
+            $currentMenu
+              .addClass(className.loading)
+            ;
+            calculations = {
+              context: {
+                offset    : ($context.get(0) === window)
+                  ? { top: 0, left: 0}
+                  : $context.offset(),
+                scrollTop : $context.scrollTop(),
+                height    : $context.outerHeight()
+              },
+              menu : {
+                offset: $currentMenu.offset(),
+                height: $currentMenu.outerHeight()
+              }
+            };
+            if(module.is.verticallyScrollableContext()) {
+              calculations.menu.offset.top += calculations.context.scrollTop;
+            }
+            onScreen = {
+              above : (calculations.context.scrollTop) <= calculations.menu.offset.top - calculations.context.offset.top - calculations.menu.height,
+              below : (calculations.context.scrollTop + calculations.context.height) >= calculations.menu.offset.top - calculations.context.offset.top + calculations.menu.height
+            };
+            if(onScreen.below) {
+              module.verbose('Dropdown can fit in context downward', onScreen);
+              canOpenDownward = true;
+            }
+            else if(!onScreen.below && !onScreen.above) {
+              module.verbose('Dropdown cannot fit in either direction, favoring downward', onScreen);
+              canOpenDownward = true;
+            }
+            else {
+              module.verbose('Dropdown cannot fit below, opening upward', onScreen);
+              canOpenDownward = false;
+            }
+            $currentMenu.removeClass(className.loading);
+            return canOpenDownward;
+          },
+          openRightward: function($subMenu) {
+            var
+              $currentMenu     = $subMenu || $menu,
+              canOpenRightward = true,
+              isOffscreenRight = false,
+              calculations
+            ;
+            $currentMenu
+              .addClass(className.loading)
+            ;
+            calculations = {
+              context: {
+                offset     : ($context.get(0) === window)
+                  ? { top: 0, left: 0}
+                  : $context.offset(),
+                scrollLeft : $context.scrollLeft(),
+                width      : $context.outerWidth()
+              },
+              menu: {
+                offset : $currentMenu.offset(),
+                width  : $currentMenu.outerWidth()
+              }
+            };
+            if(module.is.horizontallyScrollableContext()) {
+              calculations.menu.offset.left += calculations.context.scrollLeft;
+            }
+            isOffscreenRight = (calculations.menu.offset.left - calculations.context.offset.left + calculations.menu.width >= calculations.context.scrollLeft + calculations.context.width);
+            if(isOffscreenRight) {
+              module.verbose('Dropdown cannot fit in context rightward', isOffscreenRight);
+              canOpenRightward = false;
+            }
+            $currentMenu.removeClass(className.loading);
+            return canOpenRightward;
+          },
+          click: function() {
+            return (hasTouch || settings.on == 'click');
+          },
+          extendSelect: function() {
+            return settings.allowAdditions || settings.apiSettings;
+          },
+          show: function() {
+            return !module.is.disabled() && (module.has.items() || module.has.message());
+          },
+          useAPI: function() {
+            return $.fn.api !== undefined;
+          }
+        },
+
+        animate: {
+          show: function(callback, $subMenu) {
+            var
+              $currentMenu = $subMenu || $menu,
+              start = ($subMenu)
+                ? function() {}
+                : function() {
+                  module.hideSubMenus();
+                  module.hideOthers();
+                  module.set.active();
+                },
+              transition
+            ;
+            callback = $.isFunction(callback)
+              ? callback
+              : function(){}
+            ;
+            module.verbose('Doing menu show animation', $currentMenu);
+            module.set.direction($subMenu);
+            transition = module.get.transition($subMenu);
+            if( module.is.selection() ) {
+              module.set.scrollPosition(module.get.selectedItem(), true);
+            }
+            if( module.is.hidden($currentMenu) || module.is.animating($currentMenu) ) {
+              if(transition == 'none') {
+                start();
+                $currentMenu.transition('show');
+                callback.call(element);
+              }
+              else if($.fn.transition !== undefined && $module.transition('is supported')) {
+                $currentMenu
+                  .transition({
+                    animation  : transition + ' in',
+                    debug      : settings.debug,
+                    verbose    : settings.verbose,
+                    duration   : settings.duration,
+                    queue      : true,
+                    onStart    : start,
+                    onComplete : function() {
+                      callback.call(element);
+                    }
+                  })
+                ;
+              }
+              else {
+                module.error(error.noTransition, transition);
+              }
+            }
+          },
+          hide: function(callback, $subMenu) {
+            var
+              $currentMenu = $subMenu || $menu,
+              start = ($subMenu)
+                ? function() {}
+                : function() {
+                  if( module.can.click() ) {
+                    module.unbind.intent();
+                  }
+                  module.remove.active();
+                },
+              transition = module.get.transition($subMenu)
+            ;
+            callback = $.isFunction(callback)
+              ? callback
+              : function(){}
+            ;
+            if( module.is.visible($currentMenu) || module.is.animating($currentMenu) ) {
+              module.verbose('Doing menu hide animation', $currentMenu);
+
+              if(transition == 'none') {
+                start();
+                $currentMenu.transition('hide');
+                callback.call(element);
+              }
+              else if($.fn.transition !== undefined && $module.transition('is supported')) {
+                $currentMenu
+                  .transition({
+                    animation  : transition + ' out',
+                    duration   : settings.duration,
+                    debug      : settings.debug,
+                    verbose    : settings.verbose,
+                    queue      : false,
+                    onStart    : start,
+                    onComplete : function() {
+                      callback.call(element);
+                    }
+                  })
+                ;
+              }
+              else {
+                module.error(error.transition);
+              }
+            }
+          }
+        },
+
+        hideAndClear: function() {
+          module.remove.searchTerm();
+          if( module.has.maxSelections() ) {
+            return;
+          }
+          if(module.has.search()) {
+            module.hide(function() {
+              module.remove.filteredItem();
+            });
+          }
+          else {
+            module.hide();
+          }
+        },
+
+        delay: {
+          show: function() {
+            module.verbose('Delaying show event to ensure user intent');
+            clearTimeout(module.timer);
+            module.timer = setTimeout(module.show, settings.delay.show);
+          },
+          hide: function() {
+            module.verbose('Delaying hide event to ensure user intent');
+            clearTimeout(module.timer);
+            module.timer = setTimeout(module.hide, settings.delay.hide);
+          }
+        },
+
+        escape: {
+          value: function(value) {
+            var
+              multipleValues = Array.isArray(value),
+              stringValue    = (typeof value === 'string'),
+              isUnparsable   = (!stringValue && !multipleValues),
+              hasQuotes      = (stringValue && value.search(regExp.quote) !== -1),
+              values         = []
+            ;
+            if(isUnparsable || !hasQuotes) {
+              return value;
+            }
+            module.debug('Encoding quote values for use in select', value);
+            if(multipleValues) {
+              $.each(value, function(index, value){
+                values.push(value.replace(regExp.quote, '&quot;'));
+              });
+              return values;
+            }
+            return value.replace(regExp.quote, '&quot;');
+          },
+          string: function(text) {
+            text =  String(text);
+            return text.replace(regExp.escape, '\\$&');
+          },
+          htmlEntities: function(string) {
+              var
+                  badChars     = /[&<>"'`]/g,
+                  shouldEscape = /[&<>"'`]/,
+                  escape       = {
+                      "&": "&amp;",
+                      "<": "&lt;",
+                      ">": "&gt;",
+                      '"': "&quot;",
+                      "'": "&#x27;",
+                      "`": "&#x60;"
+                  },
+                  escapedChar  = function(chr) {
+                      return escape[chr];
+                  }
+              ;
+              if(shouldEscape.test(string)) {
+                  return string.replace(badChars, escapedChar);
+              }
+              return string;
+          }
+        },
+
+        setting: function(name, value) {
+          module.debug('Changing setting', name, value);
+          if( $.isPlainObject(name) ) {
+            $.extend(true, settings, name);
+          }
+          else if(value !== undefined) {
+            if($.isPlainObject(settings[name])) {
+              $.extend(true, settings[name], value);
+            }
+            else {
+              settings[name] = value;
+            }
+          }
+          else {
+            return settings[name];
+          }
+        },
+        internal: function(name, value) {
+          if( $.isPlainObject(name) ) {
+            $.extend(true, module, name);
+          }
+          else if(value !== undefined) {
+            module[name] = value;
+          }
+          else {
+            return module[name];
+          }
+        },
+        debug: function() {
+          if(!settings.silent && settings.debug) {
+            if(settings.performance) {
+              module.performance.log(arguments);
+            }
+            else {
+              module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
+              module.debug.apply(console, arguments);
+            }
+          }
+        },
+        verbose: function() {
+          if(!settings.silent && settings.verbose && settings.debug) {
+            if(settings.performance) {
+              module.performance.log(arguments);
+            }
+            else {
+              module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
+              module.verbose.apply(console, arguments);
+            }
+          }
+        },
+        error: function() {
+          if(!settings.silent) {
+            module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
+            module.error.apply(console, arguments);
+          }
+        },
+        performance: {
+          log: function(message) {
+            var
+              currentTime,
+              executionTime,
+              previousTime
+            ;
+            if(settings.performance) {
+              currentTime   = new Date().getTime();
+              previousTime  = time || currentTime;
+              executionTime = currentTime - previousTime;
+              time          = currentTime;
+              performance.push({
+                'Name'           : message[0],
+                'Arguments'      : [].slice.call(message, 1) || '',
+                'Element'        : element,
+                'Execution Time' : executionTime
+              });
+            }
+            clearTimeout(module.performance.timer);
+            module.performance.timer = setTimeout(module.performance.display, 500);
+          },
+          display: function() {
+            var
+              title = settings.name + ':',
+              totalTime = 0
+            ;
+            time = false;
+            clearTimeout(module.performance.timer);
+            $.each(performance, function(index, data) {
+              totalTime += data['Execution Time'];
+            });
+            title += ' ' + totalTime + 'ms';
+            if(moduleSelector) {
+              title += ' \'' + moduleSelector + '\'';
+            }
+            if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
+              console.groupCollapsed(title);
+              if(console.table) {
+                console.table(performance);
+              }
+              else {
+                $.each(performance, function(index, data) {
+                  console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
+                });
+              }
+              console.groupEnd();
+            }
+            performance = [];
+          }
+        },
+        invoke: function(query, passedArguments, context) {
+          var
+            object = instance,
+            maxDepth,
+            found,
+            response
+          ;
+          passedArguments = passedArguments || queryArguments;
+          context         = element         || context;
+          if(typeof query == 'string' && object !== undefined) {
+            query    = query.split(/[\. ]/);
+            maxDepth = query.length - 1;
+            $.each(query, function(depth, value) {
+              var camelCaseValue = (depth != maxDepth)
+                ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
+                : query
+              ;
+              if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
+                object = object[camelCaseValue];
+              }
+              else if( object[camelCaseValue] !== undefined ) {
+                found = object[camelCaseValue];
+                return false;
+              }
+              else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
+                object = object[value];
+              }
+              else if( object[value] !== undefined ) {
+                found = object[value];
+                return false;
+              }
+              else {
+                module.error(error.method, query);
+                return false;
+              }
+            });
+          }
+          if ( $.isFunction( found ) ) {
+            response = found.apply(context, passedArguments);
+          }
+          else if(found !== undefined) {
+            response = found;
+          }
+          if(Array.isArray(returnedValue)) {
+            returnedValue.push(response);
+          }
+          else if(returnedValue !== undefined) {
+            returnedValue = [returnedValue, response];
+          }
+          else if(response !== undefined) {
+            returnedValue = response;
+          }
+          return found;
+        }
+      };
+
+      if(methodInvoked) {
+        if(instance === undefined) {
+          module.initialize();
+        }
+        module.invoke(query);
+      }
+      else {
+        if(instance !== undefined) {
+          instance.invoke('destroy');
+        }
+        module.initialize();
+      }
+    })
+  ;
+  return (returnedValue !== undefined)
+    ? returnedValue
+    : $allModules
+  ;
+};
+
+$.fn.dropdown.settings = {
+
+  silent                 : false,
+  debug                  : false,
+  verbose                : false,
+  performance            : true,
+
+  on                     : 'click',    // what event should show menu action on item selection
+  action                 : 'activate', // action on item selection (nothing, activate, select, combo, hide, function(){})
+
+  values                 : false,      // specify values to use for dropdown
+
+  clearable              : false,      // whether the value of the dropdown can be cleared
+
+  apiSettings            : false,
+  selectOnKeydown        : true,       // Whether selection should occur automatically when keyboard shortcuts used
+  minCharacters          : 0,          // Minimum characters required to trigger API call
+
+  filterRemoteData       : false,      // Whether API results should be filtered after being returned for query term
+  saveRemoteData         : true,       // Whether remote name/value pairs should be stored in sessionStorage to allow remote data to be restored on page refresh
+
+  throttle               : 200,        // How long to wait after last user input to search remotely
+
+  context                : window,     // Context to use when determining if on screen
+  direction              : 'auto',     // Whether dropdown should always open in one direction
+  keepOnScreen           : true,       // Whether dropdown should check whether it is on screen before showing
+
+  match                  : 'both',     // what to match against with search selection (both, text, or label)
+  fullTextSearch         : false,      // search anywhere in value (set to 'exact' to require exact matches)
+  ignoreDiacritics       : false,      // match results also if they contain diacritics of the same base character (for example searching for "a" will also match "á" or "â" or "à", etc...)
+  hideDividers           : false,      // Whether to hide any divider elements (specified in selector.divider) that are sibling to any items when searched (set to true will hide all dividers, set to 'empty' will hide them when they are not followed by a visible item)
+
+  placeholder            : 'auto',     // whether to convert blank <select> values to placeholder text
+  preserveHTML           : true,       // preserve html when selecting value
+  sortSelect             : false,      // sort selection on init
+
+  forceSelection         : true,       // force a choice on blur with search selection
+
+  allowAdditions         : false,      // whether multiple select should allow user added values
+  ignoreCase             : false,       // whether to consider values not matching in case to be the same
+  hideAdditions          : true,       // whether or not to hide special message prompting a user they can enter a value
+
+  maxSelections          : false,      // When set to a number limits the number of selections to this count
+  useLabels              : true,       // whether multiple select should filter currently active selections from choices
+  delimiter              : ',',        // when multiselect uses normal <input> the values will be delimited with this character
+
+  showOnFocus            : true,       // show menu on focus
+  allowReselection       : false,      // whether current value should trigger callbacks when reselected
+  allowTab               : true,       // add tabindex to element
+  allowCategorySelection : false,      // allow elements with sub-menus to be selected
+
+  fireOnInit             : false,      // Whether callbacks should fire when initializing dropdown values
+
+  transition             : 'auto',     // auto transition will slide down or up based on direction
+  duration               : 200,        // duration of transition
+
+  glyphWidth             : 1.037,      // widest glyph width in em (W is 1.037 em) used to calculate multiselect input width
+
+  headerDivider          : true,       // whether option headers should have an additional divider line underneath when converted from <select> <optgroup>
+
+  // label settings on multi-select
+  label: {
+    transition : 'scale',
+    duration   : 200,
+    variation  : false
+  },
+
+  // delay before event
+  delay : {
+    hide   : 300,
+    show   : 200,
+    search : 20,
+    touch  : 50
+  },
+
+  /* Callbacks */
+  onChange      : function(value, text, $selected){},
+  onAdd         : function(value, text, $selected){},
+  onRemove      : function(value, text, $selected){},
+
+  onLabelSelect : function($selectedLabels){},
+  onLabelCreate : function(value, text) { return $(this); },
+  onLabelRemove : function(value) { return true; },
+  onNoResults   : function(searchTerm) { return true; },
+  onShow        : function(){},
+  onHide        : function(){},
+
+  /* Component */
+  name           : 'Dropdown',
+  namespace      : 'dropdown',
+
+  message: {
+    addResult     : 'Add <b>{term}</b>',
+    count         : '{count} selected',
+    maxSelections : 'Max {maxCount} selections',
+    noResults     : 'No results found.',
+    serverError   : 'There was an error contacting the server'
+  },
+
+  error : {
+    action          : 'You called a dropdown action that was not defined',
+    alreadySetup    : 'Once a select has been initialized behaviors must be called on the created ui dropdown',
+    labels          : 'Allowing user additions currently requires the use of labels.',
+    missingMultiple : '<select> requires multiple property to be set to correctly preserve multiple values',
+    method          : 'The method you called is not defined.',
+    noAPI           : 'The API module is required to load resources remotely',
+    noStorage       : 'Saving remote data requires session storage',
+    noTransition    : 'This module requires ui transitions <https://github.com/Semantic-Org/UI-Transition>',
+    noNormalize     : '"ignoreDiacritics" setting will be ignored. Browser does not support String().normalize(). You may consider including <https://cdn.jsdelivr.net/npm/unorm@1.4.1/lib/unorm.min.js> as a polyfill.'
+  },
+
+  regExp : {
+    escape   : /[-[\]{}()*+?.,\\^$|#\s:=@]/g,
+    quote    : /"/g
+  },
+
+  metadata : {
+    defaultText     : 'defaultText',
+    defaultValue    : 'defaultValue',
+    placeholderText : 'placeholder',
+    text            : 'text',
+    value           : 'value'
+  },
+
+  // property names for remote query
+  fields: {
+    remoteValues : 'results',  // grouping for api results
+    values       : 'values',   // grouping for all dropdown values
+    disabled     : 'disabled', // whether value should be disabled
+    name         : 'name',     // displayed dropdown text
+    value        : 'value',    // actual dropdown value
+    text         : 'text',     // displayed text when selected
+    type         : 'type',     // type of dropdown element
+    image        : 'image',    // optional image path
+    imageClass   : 'imageClass', // optional individual class for image
+    icon         : 'icon',     // optional icon name
+    iconClass    : 'iconClass', // optional individual class for icon (for example to use flag instead)
+    class        : 'class',    // optional individual class for item/header
+    divider      : 'divider'   // optional divider append for group headers
+  },
+
+  keys : {
+    backspace  : 8,
+    delimiter  : 188, // comma
+    deleteKey  : 46,
+    enter      : 13,
+    escape     : 27,
+    pageUp     : 33,
+    pageDown   : 34,
+    leftArrow  : 37,
+    upArrow    : 38,
+    rightArrow : 39,
+    downArrow  : 40
+  },
+
+  selector : {
+    addition     : '.addition',
+    divider      : '.divider, .header',
+    dropdown     : '.ui.dropdown',
+    hidden       : '.hidden',
+    icon         : '> .dropdown.icon',
+    input        : '> input[type="hidden"], > select',
+    item         : '.item',
+    label        : '> .label',
+    remove       : '> .label > .delete.icon',
+    siblingLabel : '.label',
+    menu         : '.menu',
+    message      : '.message',
+    menuIcon     : '.dropdown.icon',
+    search       : 'input.search, .menu > .search > input, .menu input.search',
+    sizer        : '> input.sizer',
+    text         : '> .text:not(.icon)',
+    unselectable : '.disabled, .filtered',
+    clearIcon    : '> .remove.icon'
+  },
+
+  className : {
+    active      : 'active',
+    addition    : 'addition',
+    animating   : 'animating',
+    disabled    : 'disabled',
+    empty       : 'empty',
+    dropdown    : 'ui dropdown',
+    filtered    : 'filtered',
+    hidden      : 'hidden transition',
+    icon        : 'icon',
+    image       : 'image',
+    item        : 'item',
+    label       : 'ui label',
+    loading     : 'loading',
+    menu        : 'menu',
+    message     : 'message',
+    multiple    : 'multiple',
+    placeholder : 'default',
+    sizer       : 'sizer',
+    search      : 'search',
+    selected    : 'selected',
+    selection   : 'selection',
+    upward      : 'upward',
+    leftward    : 'left',
+    visible     : 'visible',
+    clearable   : 'clearable',
+    noselection : 'noselection',
+    delete      : 'delete',
+    header      : 'header',
+    divider     : 'divider',
+    groupIcon   : ''
+  }
+
+};
+
+/* Templates */
+$.fn.dropdown.settings.templates = {
+  deQuote: function(string) {
+      return String(string).replace(/"/g,"");
+  },
+  escape: function(string, preserveHTML) {
+    if (preserveHTML){
+      return string;
+    }
+    var
+        badChars     = /[&<>"'`]/g,
+        shouldEscape = /[&<>"'`]/,
+        escape       = {
+          "&": "&amp;",
+          "<": "&lt;",
+          ">": "&gt;",
+          '"': "&quot;",
+          "'": "&#x27;",
+          "`": "&#x60;"
+        },
+        escapedChar  = function(chr) {
+          return escape[chr];
+        }
+    ;
+    if(shouldEscape.test(string)) {
+      return string.replace(badChars, escapedChar);
+    }
+    return string;
+  },
+  // generates dropdown from select values
+  dropdown: function(select, fields, preserveHTML, className) {
+    var
+      placeholder = select.placeholder || false,
+      html        = '',
+      escape = $.fn.dropdown.settings.templates.escape
+    ;
+    html +=  '<i class="dropdown icon"></i>';
+    if(placeholder) {
+      html += '<div class="default text">' + escape(placeholder,preserveHTML) + '</div>';
+    }
+    else {
+      html += '<div class="text"></div>';
+    }
+    html += '<div class="'+className.menu+'">';
+    html += $.fn.dropdown.settings.templates.menu(select, fields, preserveHTML,className);
+    html += '</div>';
+    return html;
+  },
+
+  // generates just menu from select
+  menu: function(response, fields, preserveHTML, className) {
+    var
+      values = response[fields.values] || [],
+      html   = '',
+      escape = $.fn.dropdown.settings.templates.escape,
+      deQuote = $.fn.dropdown.settings.templates.deQuote
+    ;
+    $.each(values, function(index, option) {
+      var
+        itemType = (option[fields.type])
+          ? option[fields.type]
+          : 'item'
+      ;
+
+      if( itemType === 'item' ) {
+        var
+          maybeText = (option[fields.text])
+            ? ' data-text="' + deQuote(option[fields.text]) + '"'
+            : '',
+          maybeDisabled = (option[fields.disabled])
+            ? className.disabled+' '
+            : ''
+        ;
+        html += '<div class="'+ maybeDisabled + (option[fields.class] ? deQuote(option[fields.class]) : className.item)+'" data-value="' + deQuote(option[fields.value]) + '"' + maybeText + '>';
+        if(option[fields.image]) {
+          html += '<img class="'+(option[fields.imageClass] ? deQuote(option[fields.imageClass]) : className.image)+'" src="' + deQuote(option[fields.image]) + '">';
+        }
+        if(option[fields.icon]) {
+          html += '<i class="'+deQuote(option[fields.icon])+' '+(option[fields.iconClass] ? deQuote(option[fields.iconClass]) : className.icon)+'"></i>';
+        }
+        html +=   escape(option[fields.name],preserveHTML);
+        html += '</div>';
+      } else if (itemType === 'header') {
+        var groupName = escape(option[fields.name],preserveHTML),
+            groupIcon = option[fields.icon] ? deQuote(option[fields.icon]) : className.groupIcon
+        ;
+        if(groupName !== '' || groupIcon !== '') {
+          html += '<div class="' + (option[fields.class] ? deQuote(option[fields.class]) : className.header) + '">';
+          if (groupIcon !== '') {
+            html += '<i class="' + groupIcon + ' ' + (option[fields.iconClass] ? deQuote(option[fields.iconClass]) : className.icon) + '"></i>';
+          }
+          html += groupName;
+          html += '</div>';
+        }
+        if(option[fields.divider]){
+          html += '<div class="'+className.divider+'"></div>';
+        }
+      }
+    });
+    return html;
+  },
+
+  // generates label for multiselect
+  label: function(value, text, preserveHTML, className) {
+    var
+        escape = $.fn.dropdown.settings.templates.escape;
+    return escape(text,preserveHTML) + '<i class="'+className.delete+' icon"></i>';
+  },
+
+
+  // generates messages like "No results"
+  message: function(message) {
+    return message;
+  },
+
+  // generates user addition to selection menu
+  addition: function(choice) {
+    return choice;
+  }
+
+};
+
+})( jQuery, window, document );

Разлика између датотеке није приказан због своје велике величине
+ 8 - 0
website/semantic/dist/components/dropdown.min.css


Разлика између датотеке није приказан због своје велике величине
+ 10 - 0
website/semantic/dist/components/dropdown.min.js


+ 166 - 0
website/semantic/dist/components/embed.css

@@ -0,0 +1,166 @@
+/*!
+ * # Fomantic-UI - Video
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */
+
+
+/*******************************
+            Types
+*******************************/
+
+.ui.embed {
+  position: relative;
+  max-width: 100%;
+  height: 0;
+  overflow: hidden;
+  background: #DCDDDE;
+  padding-bottom: 56.25%;
+}
+
+/*-----------------
+  Embedded Content
+------------------*/
+
+.ui.embed iframe,
+.ui.embed embed,
+.ui.embed object {
+  position: absolute;
+  border: none;
+  width: 100%;
+  height: 100%;
+  top: 0;
+  left: 0;
+  margin: 0;
+  padding: 0;
+  overflow: hidden;
+}
+
+/*-----------------
+      Embed
+------------------*/
+
+.ui.embed > .embed {
+  display: none;
+}
+
+/*--------------
+   Placeholder
+---------------*/
+
+.ui.embed > .placeholder {
+  position: absolute;
+  cursor: pointer;
+  top: 0;
+  left: 0;
+  display: block;
+  width: 100%;
+  height: 100%;
+  background-color: radial-gradient(transparent 45%, rgba(0, 0, 0, 0.3));
+}
+
+/*--------------
+      Icon
+---------------*/
+
+.ui.embed > .icon {
+  cursor: pointer;
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 2;
+}
+.ui.embed > .icon:after {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 3;
+  content: '';
+  background: -webkit-radial-gradient(transparent 45%, rgba(0, 0, 0, 0.3));
+  background: radial-gradient(transparent 45%, rgba(0, 0, 0, 0.3));
+  opacity: 0.5;
+  -webkit-transition: opacity 0.5s ease;
+  transition: opacity 0.5s ease;
+}
+.ui.embed > .icon:before {
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  -webkit-transform: translateX(-50%) translateY(-50%);
+          transform: translateX(-50%) translateY(-50%);
+  color: #FFFFFF;
+  font-size: 6rem;
+  text-shadow: 0 2px 10px rgba(34, 36, 38, 0.2);
+  -webkit-transition: opacity 0.5s ease, color 0.5s ease;
+  transition: opacity 0.5s ease, color 0.5s ease;
+  z-index: 10;
+}
+
+
+/*******************************
+            States
+*******************************/
+
+
+/*--------------
+     Hover
+---------------*/
+
+.ui.embed .icon:hover:after {
+  background: -webkit-radial-gradient(transparent 45%, rgba(0, 0, 0, 0.3));
+  background: radial-gradient(transparent 45%, rgba(0, 0, 0, 0.3));
+  opacity: 1;
+}
+.ui.embed .icon:hover:before {
+  color: #FFFFFF;
+}
+
+/*--------------
+     Active
+---------------*/
+
+.ui.active.embed > .icon,
+.ui.active.embed > .placeholder {
+  display: none;
+}
+.ui.active.embed > .embed {
+  display: block;
+}
+
+
+/*******************************
+        Video Overrides
+*******************************/
+
+
+
+/*******************************
+         Site Overrides
+*******************************/
+
+
+
+/*******************************
+          Variations
+*******************************/
+
+.ui.square.embed {
+  padding-bottom: 100%;
+}
+.ui[class*="4:3"].embed {
+  padding-bottom: 75%;
+}
+.ui[class*="16:9"].embed {
+  padding-bottom: 56.25%;
+}
+.ui[class*="21:9"].embed {
+  padding-bottom: 42.85714286%;
+}

+ 709 - 0
website/semantic/dist/components/embed.js

@@ -0,0 +1,709 @@
+/*!
+ * # Fomantic-UI - Embed
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */
+
+;(function ($, window, document, undefined) {
+
+"use strict";
+
+$.isFunction = $.isFunction || function(obj) {
+  return typeof obj === "function" && typeof obj.nodeType !== "number";
+};
+
+window = (typeof window != 'undefined' && window.Math == Math)
+  ? window
+  : (typeof self != 'undefined' && self.Math == Math)
+    ? self
+    : Function('return this')()
+;
+
+$.fn.embed = function(parameters) {
+
+  var
+    $allModules     = $(this),
+
+    moduleSelector  = $allModules.selector || '',
+
+    time            = new Date().getTime(),
+    performance     = [],
+
+    query           = arguments[0],
+    methodInvoked   = (typeof query == 'string'),
+    queryArguments  = [].slice.call(arguments, 1),
+
+    returnedValue
+  ;
+
+  $allModules
+    .each(function() {
+      var
+        settings        = ( $.isPlainObject(parameters) )
+          ? $.extend(true, {}, $.fn.embed.settings, parameters)
+          : $.extend({}, $.fn.embed.settings),
+
+        selector        = settings.selector,
+        className       = settings.className,
+        sources         = settings.sources,
+        error           = settings.error,
+        metadata        = settings.metadata,
+        namespace       = settings.namespace,
+        templates       = settings.templates,
+
+        eventNamespace  = '.' + namespace,
+        moduleNamespace = 'module-' + namespace,
+
+        $module         = $(this),
+        $placeholder    = $module.find(selector.placeholder),
+        $icon           = $module.find(selector.icon),
+        $embed          = $module.find(selector.embed),
+
+        element         = this,
+        instance        = $module.data(moduleNamespace),
+        module
+      ;
+
+      module = {
+
+        initialize: function() {
+          module.debug('Initializing embed');
+          module.determine.autoplay();
+          module.create();
+          module.bind.events();
+          module.instantiate();
+        },
+
+        instantiate: function() {
+          module.verbose('Storing instance of module', module);
+          instance = module;
+          $module
+            .data(moduleNamespace, module)
+          ;
+        },
+
+        destroy: function() {
+          module.verbose('Destroying previous instance of embed');
+          module.reset();
+          $module
+            .removeData(moduleNamespace)
+            .off(eventNamespace)
+          ;
+        },
+
+        refresh: function() {
+          module.verbose('Refreshing selector cache');
+          $placeholder = $module.find(selector.placeholder);
+          $icon        = $module.find(selector.icon);
+          $embed       = $module.find(selector.embed);
+        },
+
+        bind: {
+          events: function() {
+            if( module.has.placeholder() ) {
+              module.debug('Adding placeholder events');
+              $module
+                .on('click' + eventNamespace, selector.placeholder, module.createAndShow)
+                .on('click' + eventNamespace, selector.icon, module.createAndShow)
+              ;
+            }
+          }
+        },
+
+        create: function() {
+          var
+            placeholder = module.get.placeholder()
+          ;
+          if(placeholder) {
+            module.createPlaceholder();
+          }
+          else {
+            module.createAndShow();
+          }
+        },
+
+        createPlaceholder: function(placeholder) {
+          var
+            icon  = module.get.icon(),
+            url   = module.get.url(),
+            embed = module.generate.embed(url)
+          ;
+          placeholder = placeholder || module.get.placeholder();
+          $module.html( templates.placeholder(placeholder, icon) );
+          module.debug('Creating placeholder for embed', placeholder, icon);
+        },
+
+        createEmbed: function(url) {
+          module.refresh();
+          url = url || module.get.url();
+          $embed = $('<div/>')
+            .addClass(className.embed)
+            .html( module.generate.embed(url) )
+            .appendTo($module)
+          ;
+          settings.onCreate.call(element, url);
+          module.debug('Creating embed object', $embed);
+        },
+
+        changeEmbed: function(url) {
+          $embed
+            .html( module.generate.embed(url) )
+          ;
+        },
+
+        createAndShow: function() {
+          module.createEmbed();
+          module.show();
+        },
+
+        // sets new embed
+        change: function(source, id, url) {
+          module.debug('Changing video to ', source, id, url);
+          $module
+            .data(metadata.source, source)
+            .data(metadata.id, id)
+          ;
+          if(url) {
+            $module.data(metadata.url, url);
+          }
+          else {
+            $module.removeData(metadata.url);
+          }
+          if(module.has.embed()) {
+            module.changeEmbed();
+          }
+          else {
+            module.create();
+          }
+        },
+
+        // clears embed
+        reset: function() {
+          module.debug('Clearing embed and showing placeholder');
+          module.remove.data();
+          module.remove.active();
+          module.remove.embed();
+          module.showPlaceholder();
+          settings.onReset.call(element);
+        },
+
+        // shows current embed
+        show: function() {
+          module.debug('Showing embed');
+          module.set.active();
+          settings.onDisplay.call(element);
+        },
+
+        hide: function() {
+          module.debug('Hiding embed');
+          module.showPlaceholder();
+        },
+
+        showPlaceholder: function() {
+          module.debug('Showing placeholder image');
+          module.remove.active();
+          settings.onPlaceholderDisplay.call(element);
+        },
+
+        get: {
+          id: function() {
+            return settings.id || $module.data(metadata.id);
+          },
+          placeholder: function() {
+            return settings.placeholder || $module.data(metadata.placeholder);
+          },
+          icon: function() {
+            return (settings.icon)
+              ? settings.icon
+              : ($module.data(metadata.icon) !== undefined)
+                ? $module.data(metadata.icon)
+                : module.determine.icon()
+            ;
+          },
+          source: function(url) {
+            return (settings.source)
+              ? settings.source
+              : ($module.data(metadata.source) !== undefined)
+                ? $module.data(metadata.source)
+                : module.determine.source()
+            ;
+          },
+          type: function() {
+            var source = module.get.source();
+            return (sources[source] !== undefined)
+              ? sources[source].type
+              : false
+            ;
+          },
+          url: function() {
+            return (settings.url)
+              ? settings.url
+              : ($module.data(metadata.url) !== undefined)
+                ? $module.data(metadata.url)
+                : module.determine.url()
+            ;
+          }
+        },
+
+        determine: {
+          autoplay: function() {
+            if(module.should.autoplay()) {
+              settings.autoplay = true;
+            }
+          },
+          source: function(url) {
+            var
+              matchedSource = false
+            ;
+            url = url || module.get.url();
+            if(url) {
+              $.each(sources, function(name, source) {
+                if(url.search(source.domain) !== -1) {
+                  matchedSource = name;
+                  return false;
+                }
+              });
+            }
+            return matchedSource;
+          },
+          icon: function() {
+            var
+              source = module.get.source()
+            ;
+            return (sources[source] !== undefined)
+              ? sources[source].icon
+              : false
+            ;
+          },
+          url: function() {
+            var
+              id     = settings.id     || $module.data(metadata.id),
+              source = settings.source || $module.data(metadata.source),
+              url
+            ;
+            url = (sources[source] !== undefined)
+              ? sources[source].url.replace('{id}', id)
+              : false
+            ;
+            if(url) {
+              $module.data(metadata.url, url);
+            }
+            return url;
+          }
+        },
+
+
+        set: {
+          active: function() {
+            $module.addClass(className.active);
+          }
+        },
+
+        remove: {
+          data: function() {
+            $module
+              .removeData(metadata.id)
+              .removeData(metadata.icon)
+              .removeData(metadata.placeholder)
+              .removeData(metadata.source)
+              .removeData(metadata.url)
+            ;
+          },
+          active: function() {
+            $module.removeClass(className.active);
+          },
+          embed: function() {
+            $embed.empty();
+          }
+        },
+
+        encode: {
+          parameters: function(parameters) {
+            var
+              urlString = [],
+              index
+            ;
+            for (index in parameters) {
+              urlString.push( encodeURIComponent(index) + '=' + encodeURIComponent( parameters[index] ) );
+            }
+            return urlString.join('&amp;');
+          }
+        },
+
+        generate: {
+          embed: function(url) {
+            module.debug('Generating embed html');
+            var
+              source = module.get.source(),
+              html,
+              parameters
+            ;
+            url = module.get.url(url);
+            if(url) {
+              parameters = module.generate.parameters(source);
+              html       = templates.iframe(url, parameters);
+            }
+            else {
+              module.error(error.noURL, $module);
+            }
+            return html;
+          },
+          parameters: function(source, extraParameters) {
+            var
+              parameters = (sources[source] && sources[source].parameters !== undefined)
+                ? sources[source].parameters(settings)
+                : {}
+            ;
+            extraParameters = extraParameters || settings.parameters;
+            if(extraParameters) {
+              parameters = $.extend({}, parameters, extraParameters);
+            }
+            parameters = settings.onEmbed(parameters);
+            return module.encode.parameters(parameters);
+          }
+        },
+
+        has: {
+          embed: function() {
+            return ($embed.length > 0);
+          },
+          placeholder: function() {
+            return settings.placeholder || $module.data(metadata.placeholder);
+          }
+        },
+
+        should: {
+          autoplay: function() {
+            return (settings.autoplay === 'auto')
+              ? (settings.placeholder || $module.data(metadata.placeholder) !== undefined)
+              : settings.autoplay
+            ;
+          }
+        },
+
+        is: {
+          video: function() {
+            return module.get.type() == 'video';
+          }
+        },
+
+        setting: function(name, value) {
+          module.debug('Changing setting', name, value);
+          if( $.isPlainObject(name) ) {
+            $.extend(true, settings, name);
+          }
+          else if(value !== undefined) {
+            if($.isPlainObject(settings[name])) {
+              $.extend(true, settings[name], value);
+            }
+            else {
+              settings[name] = value;
+            }
+          }
+          else {
+            return settings[name];
+          }
+        },
+        internal: function(name, value) {
+          if( $.isPlainObject(name) ) {
+            $.extend(true, module, name);
+          }
+          else if(value !== undefined) {
+            module[name] = value;
+          }
+          else {
+            return module[name];
+          }
+        },
+        debug: function() {
+          if(!settings.silent && settings.debug) {
+            if(settings.performance) {
+              module.performance.log(arguments);
+            }
+            else {
+              module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
+              module.debug.apply(console, arguments);
+            }
+          }
+        },
+        verbose: function() {
+          if(!settings.silent && settings.verbose && settings.debug) {
+            if(settings.performance) {
+              module.performance.log(arguments);
+            }
+            else {
+              module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
+              module.verbose.apply(console, arguments);
+            }
+          }
+        },
+        error: function() {
+          if(!settings.silent) {
+            module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
+            module.error.apply(console, arguments);
+          }
+        },
+        performance: {
+          log: function(message) {
+            var
+              currentTime,
+              executionTime,
+              previousTime
+            ;
+            if(settings.performance) {
+              currentTime   = new Date().getTime();
+              previousTime  = time || currentTime;
+              executionTime = currentTime - previousTime;
+              time          = currentTime;
+              performance.push({
+                'Name'           : message[0],
+                'Arguments'      : [].slice.call(message, 1) || '',
+                'Element'        : element,
+                'Execution Time' : executionTime
+              });
+            }
+            clearTimeout(module.performance.timer);
+            module.performance.timer = setTimeout(module.performance.display, 500);
+          },
+          display: function() {
+            var
+              title = settings.name + ':',
+              totalTime = 0
+            ;
+            time = false;
+            clearTimeout(module.performance.timer);
+            $.each(performance, function(index, data) {
+              totalTime += data['Execution Time'];
+            });
+            title += ' ' + totalTime + 'ms';
+            if(moduleSelector) {
+              title += ' \'' + moduleSelector + '\'';
+            }
+            if($allModules.length > 1) {
+              title += ' ' + '(' + $allModules.length + ')';
+            }
+            if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
+              console.groupCollapsed(title);
+              if(console.table) {
+                console.table(performance);
+              }
+              else {
+                $.each(performance, function(index, data) {
+                  console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
+                });
+              }
+              console.groupEnd();
+            }
+            performance = [];
+          }
+        },
+        invoke: function(query, passedArguments, context) {
+          var
+            object = instance,
+            maxDepth,
+            found,
+            response
+          ;
+          passedArguments = passedArguments || queryArguments;
+          context         = element         || context;
+          if(typeof query == 'string' && object !== undefined) {
+            query    = query.split(/[\. ]/);
+            maxDepth = query.length - 1;
+            $.each(query, function(depth, value) {
+              var camelCaseValue = (depth != maxDepth)
+                ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
+                : query
+              ;
+              if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
+                object = object[camelCaseValue];
+              }
+              else if( object[camelCaseValue] !== undefined ) {
+                found = object[camelCaseValue];
+                return false;
+              }
+              else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
+                object = object[value];
+              }
+              else if( object[value] !== undefined ) {
+                found = object[value];
+                return false;
+              }
+              else {
+                module.error(error.method, query);
+                return false;
+              }
+            });
+          }
+          if ( $.isFunction( found ) ) {
+            response = found.apply(context, passedArguments);
+          }
+          else if(found !== undefined) {
+            response = found;
+          }
+          if(Array.isArray(returnedValue)) {
+            returnedValue.push(response);
+          }
+          else if(returnedValue !== undefined) {
+            returnedValue = [returnedValue, response];
+          }
+          else if(response !== undefined) {
+            returnedValue = response;
+          }
+          return found;
+        }
+      };
+
+      if(methodInvoked) {
+        if(instance === undefined) {
+          module.initialize();
+        }
+        module.invoke(query);
+      }
+      else {
+        if(instance !== undefined) {
+          instance.invoke('destroy');
+        }
+        module.initialize();
+      }
+    })
+  ;
+  return (returnedValue !== undefined)
+    ? returnedValue
+    : this
+  ;
+};
+
+$.fn.embed.settings = {
+
+  name        : 'Embed',
+  namespace   : 'embed',
+
+  silent      : false,
+  debug       : false,
+  verbose     : false,
+  performance : true,
+
+  icon     : false,
+  source   : false,
+  url      : false,
+  id       : false,
+
+  // standard video settings
+  autoplay  : 'auto',
+  color     : '#444444',
+  hd        : true,
+  brandedUI : false,
+
+  // additional parameters to include with the embed
+  parameters: false,
+
+  onDisplay            : function() {},
+  onPlaceholderDisplay : function() {},
+  onReset              : function() {},
+  onCreate             : function(url) {},
+  onEmbed              : function(parameters) {
+    return parameters;
+  },
+
+  metadata    : {
+    id          : 'id',
+    icon        : 'icon',
+    placeholder : 'placeholder',
+    source      : 'source',
+    url         : 'url'
+  },
+
+  error : {
+    noURL  : 'No URL specified',
+    method : 'The method you called is not defined'
+  },
+
+  className : {
+    active : 'active',
+    embed  : 'embed'
+  },
+
+  selector : {
+    embed       : '.embed',
+    placeholder : '.placeholder',
+    icon        : '.icon'
+  },
+
+  sources: {
+    youtube: {
+      name   : 'youtube',
+      type   : 'video',
+      icon   : 'video play',
+      domain : 'youtube.com',
+      url    : '//www.youtube.com/embed/{id}',
+      parameters: function(settings) {
+        return {
+          autohide       : !settings.brandedUI,
+          autoplay       : settings.autoplay,
+          color          : settings.color || undefined,
+          hq             : settings.hd,
+          jsapi          : settings.api,
+          modestbranding : !settings.brandedUI
+        };
+      }
+    },
+    vimeo: {
+      name   : 'vimeo',
+      type   : 'video',
+      icon   : 'video play',
+      domain : 'vimeo.com',
+      url    : '//player.vimeo.com/video/{id}',
+      parameters: function(settings) {
+        return {
+          api      : settings.api,
+          autoplay : settings.autoplay,
+          byline   : settings.brandedUI,
+          color    : settings.color || undefined,
+          portrait : settings.brandedUI,
+          title    : settings.brandedUI
+        };
+      }
+    }
+  },
+
+  templates: {
+    iframe : function(url, parameters) {
+      var src = url;
+      if (parameters) {
+          src += '?' + parameters;
+      }
+      return ''
+        + '<iframe src="' + src + '"'
+        + ' width="100%" height="100%"'
+        + ' webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>'
+      ;
+    },
+    placeholder : function(image, icon) {
+      var
+        html = ''
+      ;
+      if(icon) {
+        html += '<i class="' + icon + ' icon"></i>';
+      }
+      if(image) {
+        html += '<img class="placeholder" src="' + image + '">';
+      }
+      return html;
+    }
+  },
+
+  // NOT YET IMPLEMENTED
+  api     : false,
+  onPause : function() {},
+  onPlay  : function() {},
+  onStop  : function() {}
+
+};
+
+
+
+})( jQuery, window, document );

+ 9 - 0
website/semantic/dist/components/embed.min.css

@@ -0,0 +1,9 @@
+/*!
+ * # Fomantic-UI - Video
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */.ui.embed{position:relative;max-width:100%;height:0;overflow:hidden;background:#dcddde;padding-bottom:56.25%}.ui.embed embed,.ui.embed iframe,.ui.embed object{position:absolute;border:none;width:100%;height:100%;top:0;left:0;margin:0;padding:0;overflow:hidden}.ui.embed>.embed{display:none}.ui.embed>.placeholder{position:absolute;cursor:pointer;top:0;left:0;display:block;width:100%;height:100%;background-color:radial-gradient(transparent 45%,rgba(0,0,0,.3))}.ui.embed>.icon{cursor:pointer;position:absolute;top:0;left:0;width:100%;height:100%;z-index:2}.ui.embed>.icon:after{position:absolute;top:0;left:0;width:100%;height:100%;z-index:3;content:'';background:-webkit-radial-gradient(transparent 45%,rgba(0,0,0,.3));background:radial-gradient(transparent 45%,rgba(0,0,0,.3));opacity:.5;-webkit-transition:opacity .5s ease;transition:opacity .5s ease}.ui.embed>.icon:before{position:absolute;top:50%;left:50%;-webkit-transform:translateX(-50%) translateY(-50%);transform:translateX(-50%) translateY(-50%);color:#fff;font-size:6rem;text-shadow:0 2px 10px rgba(34,36,38,.2);-webkit-transition:opacity .5s ease,color .5s ease;transition:opacity .5s ease,color .5s ease;z-index:10}.ui.embed .icon:hover:after{background:-webkit-radial-gradient(transparent 45%,rgba(0,0,0,.3));background:radial-gradient(transparent 45%,rgba(0,0,0,.3));opacity:1}.ui.embed .icon:hover:before{color:#fff}.ui.active.embed>.icon,.ui.active.embed>.placeholder{display:none}.ui.active.embed>.embed{display:block}.ui.square.embed{padding-bottom:100%}.ui[class*="4:3"].embed{padding-bottom:75%}.ui[class*="16:9"].embed{padding-bottom:56.25%}.ui[class*="21:9"].embed{padding-bottom:42.85714286%}

Разлика између датотеке није приказан због своје велике величине
+ 10 - 0
website/semantic/dist/components/embed.min.js


+ 314 - 0
website/semantic/dist/components/feed.css

@@ -0,0 +1,314 @@
+/*!
+ * # Fomantic-UI - Feed
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */
+
+
+/*******************************
+         Activity Feed
+*******************************/
+
+.ui.feed {
+  margin: 1em 0;
+}
+.ui.feed:first-child {
+  margin-top: 0;
+}
+.ui.feed:last-child {
+  margin-bottom: 0;
+}
+
+
+/*******************************
+            Content
+*******************************/
+
+
+/* Event */
+.ui.feed > .event {
+  display: -webkit-box;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-box-orient: horizontal;
+  -webkit-box-direction: normal;
+      -ms-flex-direction: row;
+          flex-direction: row;
+  width: 100%;
+  padding: 0.21428571rem 0;
+  margin: 0;
+  background: none;
+  border-top: none;
+}
+.ui.feed > .event:first-child {
+  border-top: 0;
+  padding-top: 0;
+}
+.ui.feed > .event:last-child {
+  padding-bottom: 0;
+}
+
+/* Event Label */
+.ui.feed > .event > .label {
+  display: block;
+  -webkit-box-flex: 0;
+      -ms-flex: 0 0 auto;
+          flex: 0 0 auto;
+  width: 2.5em;
+  height: auto;
+  -ms-flex-item-align: stretch;
+      align-self: stretch;
+  text-align: left;
+}
+.ui.feed > .event > .label .icon {
+  opacity: 1;
+  font-size: 1.5em;
+  width: 100%;
+  padding: 0.25em;
+  background: none;
+  border: none;
+  border-radius: none;
+  color: rgba(0, 0, 0, 0.6);
+}
+.ui.feed > .event > .label img {
+  width: 100%;
+  height: auto;
+  border-radius: 500rem;
+}
+.ui.feed > .event > .label + .content {
+  margin: 0.5em 0 0.35714286em 1.14285714em;
+}
+
+/*--------------
+     Content
+---------------*/
+
+
+/* Content */
+.ui.feed > .event > .content {
+  display: block;
+  -webkit-box-flex: 1;
+      -ms-flex: 1 1 auto;
+          flex: 1 1 auto;
+  -ms-flex-item-align: stretch;
+      align-self: stretch;
+  text-align: left;
+  word-wrap: break-word;
+}
+.ui.feed > .event:last-child > .content {
+  padding-bottom: 0;
+}
+
+/* Link */
+.ui.feed > .event > .content a {
+  cursor: pointer;
+}
+
+/*--------------
+      Date
+---------------*/
+
+.ui.feed > .event > .content .date {
+  margin: -0.5rem 0 0;
+  padding: 0;
+  color: rgba(0, 0, 0, 0.4);
+  font-weight: normal;
+  font-size: 1em;
+  font-style: normal;
+}
+
+/*--------------
+     Summary
+---------------*/
+
+.ui.feed > .event > .content .summary {
+  margin: 0;
+  font-size: 1em;
+  font-weight: bold;
+  color: #AAAAAA;
+}
+
+/* Summary Image */
+.ui.feed > .event > .content .summary img {
+  display: inline-block;
+  width: auto;
+  height: 10em;
+  margin: -0.25em 0.25em 0 0;
+  border-radius: 0.25em;
+  vertical-align: middle;
+}
+
+/*--------------
+      User
+---------------*/
+
+.ui.feed > .event > .content .user {
+  display: inline-block;
+  font-weight: bold;
+  margin-right: 0;
+  vertical-align: baseline;
+}
+.ui.feed > .event > .content .user img {
+  margin: -0.25em 0.25em 0 0;
+  width: auto;
+  height: 10em;
+  vertical-align: middle;
+}
+
+/*--------------
+   Inline Date
+---------------*/
+
+
+/* Date inside Summary */
+.ui.feed > .event > .content .summary > .date {
+  display: inline-block;
+  float: none;
+  font-weight: normal;
+  font-size: 0.85714286em;
+  font-style: normal;
+  margin: 0 0 0 0.5em;
+  padding: 0;
+  color: rgba(0, 0, 0, 0.4);
+}
+
+/*--------------
+  Extra Summary
+---------------*/
+
+.ui.feed > .event > .content .extra {
+  margin: 0.5em 0 0;
+  background: none;
+  padding: 0;
+  color: #AAAAAA;
+}
+
+/* Images */
+.ui.feed > .event > .content .extra.images img {
+  display: inline-block;
+  margin: 0 0.25em 0 0;
+  width: 6em;
+}
+
+/* Text */
+.ui.feed > .event > .content .extra.text {
+  padding: 0;
+  border-left: none;
+  font-size: 1em;
+  max-width: 500px;
+  line-height: 1.4285em;
+}
+
+/*--------------
+      Meta
+---------------*/
+
+.ui.feed > .event > .content .meta {
+  display: inline-block;
+  font-size: 0.85714286em;
+  margin: 0.5em 0 0;
+  background: none;
+  border: none;
+  border-radius: 0;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+  padding: 0;
+  color: rgba(0, 0, 0, 0.6);
+}
+.ui.feed > .event > .content .meta > * {
+  position: relative;
+  margin-left: 0.75em;
+}
+.ui.feed > .event > .content .meta > *:after {
+  content: '';
+  color: rgba(0, 0, 0, 0.2);
+  top: 0;
+  left: -1em;
+  opacity: 1;
+  position: absolute;
+  vertical-align: top;
+}
+.ui.feed > .event > .content .meta .like {
+  color: '';
+  -webkit-transition: 0.2s color ease;
+  transition: 0.2s color ease;
+}
+.ui.feed > .event > .content .meta .like:hover .icon {
+  color: #FF2733;
+}
+.ui.feed > .event > .content .meta .active.like .icon {
+  color: #EF404A;
+}
+
+/* First element */
+.ui.feed > .event > .content .meta > :first-child {
+  margin-left: 0;
+}
+.ui.feed > .event > .content .meta > :first-child::after {
+  display: none;
+}
+
+/* Action */
+.ui.feed > .event > .content .meta a,
+.ui.feed > .event > .content .meta > .icon {
+  cursor: pointer;
+  opacity: 1;
+  color: rgba(0, 0, 0, 0.5);
+  -webkit-transition: color 0.1s ease;
+  transition: color 0.1s ease;
+}
+.ui.feed > .event > .content .meta a:hover,
+.ui.feed > .event > .content .meta a:hover .icon,
+.ui.feed > .event > .content .meta > .icon:hover {
+  color: rgba(0, 0, 0, 0.95);
+}
+
+
+/*******************************
+            Variations
+*******************************/
+
+.ui.small.feed {
+  font-size: 0.92857143rem;
+}
+.ui.feed {
+  font-size: 1rem;
+}
+.ui.large.feed {
+  font-size: 1.14285714rem;
+}
+
+/*------------------
+      Inverted
+-------------------*/
+
+.ui.inverted.feed > .event {
+  background: #1B1C1D;
+}
+.ui.inverted.feed > .event > .content .date,
+.ui.inverted.feed > .event > .content .meta .like {
+  color: rgba(255, 255, 255, 0.7);
+}
+.ui.inverted.feed > .event > .content .summary,
+.ui.inverted.feed > .event > .content .extra.text {
+  color: rgba(255, 255, 255, 0.9);
+}
+.ui.inverted.feed > .event > .content .meta .like:hover {
+  color: #ffffff;
+}
+
+
+/*******************************
+         Theme Overrides
+*******************************/
+
+
+
+/*******************************
+    User Variable Overrides
+*******************************/
+

Разлика између датотеке није приказан због своје велике величине
+ 8 - 0
website/semantic/dist/components/feed.min.css


+ 1038 - 0
website/semantic/dist/components/flag.css

@@ -0,0 +1,1038 @@
+/*!
+ * # Fomantic-UI - Flag
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */
+
+
+/*******************************
+             Flag
+*******************************/
+
+i.flag:not(.icon) {
+  display: inline-block;
+  width: 16px;
+  height: 11px;
+  line-height: 11px;
+  vertical-align: baseline;
+  margin: 0 0.5em 0 0;
+  text-decoration: inherit;
+  speak: none;
+  -webkit-font-smoothing: antialiased;
+  -webkit-backface-visibility: hidden;
+          backface-visibility: hidden;
+}
+
+/* Sprite */
+i.flag:not(.icon):before {
+  display: inline-block;
+  content: '';
+  background: url("./../themes/default/assets/images/flags.png") no-repeat -108px -1976px;
+  width: 16px;
+  height: 11px;
+}
+
+/* Flag Sprite Based On http://www.famfamfam.com/lab/icons/flags/ */
+
+
+/*******************************
+         Theme Overrides
+*******************************/
+
+/*rtl:begin:ignore*/
+i.flag.ad:before,
+i.flag.andorra:before {
+  background-position: 0 0;
+}
+i.flag.ae:before,
+i.flag.united.arab.emirates:before,
+i.flag.uae:before {
+  background-position: 0 -26px;
+}
+i.flag.af:before,
+i.flag.afghanistan:before {
+  background-position: 0 -52px;
+}
+i.flag.ag:before,
+i.flag.antigua:before {
+  background-position: 0 -78px;
+}
+i.flag.ai:before,
+i.flag.anguilla:before {
+  background-position: 0 -104px;
+}
+i.flag.al:before,
+i.flag.albania:before {
+  background-position: 0 -130px;
+}
+i.flag.am:before,
+i.flag.armenia:before {
+  background-position: 0 -156px;
+}
+i.flag.an:before,
+i.flag.netherlands.antilles:before {
+  background-position: 0 -182px;
+}
+i.flag.ao:before,
+i.flag.angola:before {
+  background-position: 0 -208px;
+}
+i.flag.ar:before,
+i.flag.argentina:before {
+  background-position: 0 -234px;
+}
+i.flag.as:before,
+i.flag.american.samoa:before {
+  background-position: 0 -260px;
+}
+i.flag.at:before,
+i.flag.austria:before {
+  background-position: 0 -286px;
+}
+i.flag.au:before,
+i.flag.australia:before {
+  background-position: 0 -312px;
+}
+i.flag.aw:before,
+i.flag.aruba:before {
+  background-position: 0 -338px;
+}
+i.flag.ax:before,
+i.flag.aland.islands:before {
+  background-position: 0 -364px;
+}
+i.flag.az:before,
+i.flag.azerbaijan:before {
+  background-position: 0 -390px;
+}
+i.flag.ba:before,
+i.flag.bosnia:before {
+  background-position: 0 -416px;
+}
+i.flag.bb:before,
+i.flag.barbados:before {
+  background-position: 0 -442px;
+}
+i.flag.bd:before,
+i.flag.bangladesh:before {
+  background-position: 0 -468px;
+}
+i.flag.be:before,
+i.flag.belgium:before {
+  background-position: 0 -494px;
+}
+i.flag.bf:before,
+i.flag.burkina.faso:before {
+  background-position: 0 -520px;
+}
+i.flag.bg:before,
+i.flag.bulgaria:before {
+  background-position: 0 -546px;
+}
+i.flag.bh:before,
+i.flag.bahrain:before {
+  background-position: 0 -572px;
+}
+i.flag.bi:before,
+i.flag.burundi:before {
+  background-position: 0 -598px;
+}
+i.flag.bj:before,
+i.flag.benin:before {
+  background-position: 0 -624px;
+}
+i.flag.bm:before,
+i.flag.bermuda:before {
+  background-position: 0 -650px;
+}
+i.flag.bn:before,
+i.flag.brunei:before {
+  background-position: 0 -676px;
+}
+i.flag.bo:before,
+i.flag.bolivia:before {
+  background-position: 0 -702px;
+}
+i.flag.br:before,
+i.flag.brazil:before {
+  background-position: 0 -728px;
+}
+i.flag.bs:before,
+i.flag.bahamas:before {
+  background-position: 0 -754px;
+}
+i.flag.bt:before,
+i.flag.bhutan:before {
+  background-position: 0 -780px;
+}
+i.flag.bv:before,
+i.flag.bouvet.island:before {
+  background-position: 0 -806px;
+}
+i.flag.bw:before,
+i.flag.botswana:before {
+  background-position: 0 -832px;
+}
+i.flag.by:before,
+i.flag.belarus:before {
+  background-position: 0 -858px;
+}
+i.flag.bz:before,
+i.flag.belize:before {
+  background-position: 0 -884px;
+}
+i.flag.ca:before,
+i.flag.canada:before {
+  background-position: 0 -910px;
+}
+i.flag.cc:before,
+i.flag.cocos.islands:before {
+  background-position: 0 -962px;
+}
+i.flag.cd:before,
+i.flag.congo:before {
+  background-position: 0 -988px;
+}
+i.flag.cf:before,
+i.flag.central.african.republic:before {
+  background-position: 0 -1014px;
+}
+i.flag.cg:before,
+i.flag.congo.brazzaville:before {
+  background-position: 0 -1040px;
+}
+i.flag.ch:before,
+i.flag.switzerland:before {
+  background-position: 0 -1066px;
+}
+i.flag.ci:before,
+i.flag.cote.divoire:before {
+  background-position: 0 -1092px;
+}
+i.flag.ck:before,
+i.flag.cook.islands:before {
+  background-position: 0 -1118px;
+}
+i.flag.cl:before,
+i.flag.chile:before {
+  background-position: 0 -1144px;
+}
+i.flag.cm:before,
+i.flag.cameroon:before {
+  background-position: 0 -1170px;
+}
+i.flag.cn:before,
+i.flag.china:before {
+  background-position: 0 -1196px;
+}
+i.flag.co:before,
+i.flag.colombia:before {
+  background-position: 0 -1222px;
+}
+i.flag.cr:before,
+i.flag.costa.rica:before {
+  background-position: 0 -1248px;
+}
+i.flag.cs:before,
+i.flag.serbia:before {
+  background-position: 0 -1274px;
+}
+i.flag.cu:before,
+i.flag.cuba:before {
+  background-position: 0 -1300px;
+}
+i.flag.cv:before,
+i.flag.cape.verde:before {
+  background-position: 0 -1326px;
+}
+i.flag.cx:before,
+i.flag.christmas.island:before {
+  background-position: 0 -1352px;
+}
+i.flag.cy:before,
+i.flag.cyprus:before {
+  background-position: 0 -1378px;
+}
+i.flag.cz:before,
+i.flag.czech.republic:before {
+  background-position: 0 -1404px;
+}
+i.flag.de:before,
+i.flag.germany:before {
+  background-position: 0 -1430px;
+}
+i.flag.dj:before,
+i.flag.djibouti:before {
+  background-position: 0 -1456px;
+}
+i.flag.dk:before,
+i.flag.denmark:before {
+  background-position: 0 -1482px;
+}
+i.flag.dm:before,
+i.flag.dominica:before {
+  background-position: 0 -1508px;
+}
+i.flag.do:before,
+i.flag.dominican.republic:before {
+  background-position: 0 -1534px;
+}
+i.flag.dz:before,
+i.flag.algeria:before {
+  background-position: 0 -1560px;
+}
+i.flag.ec:before,
+i.flag.ecuador:before {
+  background-position: 0 -1586px;
+}
+i.flag.ee:before,
+i.flag.estonia:before {
+  background-position: 0 -1612px;
+}
+i.flag.eg:before,
+i.flag.egypt:before {
+  background-position: 0 -1638px;
+}
+i.flag.eh:before,
+i.flag.western.sahara:before {
+  background-position: 0 -1664px;
+}
+i.flag.gb.eng:before,
+i.flag.england:before {
+  background-position: 0 -1690px;
+}
+i.flag.er:before,
+i.flag.eritrea:before {
+  background-position: 0 -1716px;
+}
+i.flag.es:before,
+i.flag.spain:before {
+  background-position: 0 -1742px;
+}
+i.flag.et:before,
+i.flag.ethiopia:before {
+  background-position: 0 -1768px;
+}
+i.flag.eu:before,
+i.flag.european.union:before {
+  background-position: 0 -1794px;
+}
+i.flag.fi:before,
+i.flag.finland:before {
+  background-position: 0 -1846px;
+}
+i.flag.fj:before,
+i.flag.fiji:before {
+  background-position: 0 -1872px;
+}
+i.flag.fk:before,
+i.flag.falkland.islands:before {
+  background-position: 0 -1898px;
+}
+i.flag.fm:before,
+i.flag.micronesia:before {
+  background-position: 0 -1924px;
+}
+i.flag.fo:before,
+i.flag.faroe.islands:before {
+  background-position: 0 -1950px;
+}
+i.flag.fr:before,
+i.flag.france:before {
+  background-position: 0 -1976px;
+}
+i.flag.ga:before,
+i.flag.gabon:before {
+  background-position: -36px 0;
+}
+i.flag.gb:before,
+i.flag.uk:before,
+i.flag.united.kingdom:before {
+  background-position: -36px -26px;
+}
+i.flag.gd:before,
+i.flag.grenada:before {
+  background-position: -36px -52px;
+}
+i.flag.ge:before,
+i.flag.georgia:before {
+  background-position: -36px -78px;
+}
+i.flag.gf:before,
+i.flag.french.guiana:before {
+  background-position: -36px -104px;
+}
+i.flag.gh:before,
+i.flag.ghana:before {
+  background-position: -36px -130px;
+}
+i.flag.gi:before,
+i.flag.gibraltar:before {
+  background-position: -36px -156px;
+}
+i.flag.gl:before,
+i.flag.greenland:before {
+  background-position: -36px -182px;
+}
+i.flag.gm:before,
+i.flag.gambia:before {
+  background-position: -36px -208px;
+}
+i.flag.gn:before,
+i.flag.guinea:before {
+  background-position: -36px -234px;
+}
+i.flag.gp:before,
+i.flag.guadeloupe:before {
+  background-position: -36px -260px;
+}
+i.flag.gq:before,
+i.flag.equatorial.guinea:before {
+  background-position: -36px -286px;
+}
+i.flag.gr:before,
+i.flag.greece:before {
+  background-position: -36px -312px;
+}
+i.flag.gs:before,
+i.flag.sandwich.islands:before {
+  background-position: -36px -338px;
+}
+i.flag.gt:before,
+i.flag.guatemala:before {
+  background-position: -36px -364px;
+}
+i.flag.gu:before,
+i.flag.guam:before {
+  background-position: -36px -390px;
+}
+i.flag.gw:before,
+i.flag.guinea-bissau:before {
+  background-position: -36px -416px;
+}
+i.flag.gy:before,
+i.flag.guyana:before {
+  background-position: -36px -442px;
+}
+i.flag.hk:before,
+i.flag.hong.kong:before {
+  background-position: -36px -468px;
+}
+i.flag.hm:before,
+i.flag.heard.island:before {
+  background-position: -36px -494px;
+}
+i.flag.hn:before,
+i.flag.honduras:before {
+  background-position: -36px -520px;
+}
+i.flag.hr:before,
+i.flag.croatia:before {
+  background-position: -36px -546px;
+}
+i.flag.ht:before,
+i.flag.haiti:before {
+  background-position: -36px -572px;
+}
+i.flag.hu:before,
+i.flag.hungary:before {
+  background-position: -36px -598px;
+}
+i.flag.id:before,
+i.flag.indonesia:before {
+  background-position: -36px -624px;
+}
+i.flag.ie:before,
+i.flag.ireland:before {
+  background-position: -36px -650px;
+}
+i.flag.il:before,
+i.flag.israel:before {
+  background-position: -36px -676px;
+}
+i.flag.in:before,
+i.flag.india:before {
+  background-position: -36px -702px;
+}
+i.flag.io:before,
+i.flag.indian.ocean.territory:before {
+  background-position: -36px -728px;
+}
+i.flag.iq:before,
+i.flag.iraq:before {
+  background-position: -36px -754px;
+}
+i.flag.ir:before,
+i.flag.iran:before {
+  background-position: -36px -780px;
+}
+i.flag.is:before,
+i.flag.iceland:before {
+  background-position: -36px -806px;
+}
+i.flag.it:before,
+i.flag.italy:before {
+  background-position: -36px -832px;
+}
+i.flag.jm:before,
+i.flag.jamaica:before {
+  background-position: -36px -858px;
+}
+i.flag.jo:before,
+i.flag.jordan:before {
+  background-position: -36px -884px;
+}
+i.flag.jp:before,
+i.flag.japan:before {
+  background-position: -36px -910px;
+}
+i.flag.ke:before,
+i.flag.kenya:before {
+  background-position: -36px -936px;
+}
+i.flag.kg:before,
+i.flag.kyrgyzstan:before {
+  background-position: -36px -962px;
+}
+i.flag.kh:before,
+i.flag.cambodia:before {
+  background-position: -36px -988px;
+}
+i.flag.ki:before,
+i.flag.kiribati:before {
+  background-position: -36px -1014px;
+}
+i.flag.km:before,
+i.flag.comoros:before {
+  background-position: -36px -1040px;
+}
+i.flag.kn:before,
+i.flag.saint.kitts.and.nevis:before {
+  background-position: -36px -1066px;
+}
+i.flag.kp:before,
+i.flag.north.korea:before {
+  background-position: -36px -1092px;
+}
+i.flag.kr:before,
+i.flag.south.korea:before {
+  background-position: -36px -1118px;
+}
+i.flag.kw:before,
+i.flag.kuwait:before {
+  background-position: -36px -1144px;
+}
+i.flag.ky:before,
+i.flag.cayman.islands:before {
+  background-position: -36px -1170px;
+}
+i.flag.kz:before,
+i.flag.kazakhstan:before {
+  background-position: -36px -1196px;
+}
+i.flag.la:before,
+i.flag.laos:before {
+  background-position: -36px -1222px;
+}
+i.flag.lb:before,
+i.flag.lebanon:before {
+  background-position: -36px -1248px;
+}
+i.flag.lc:before,
+i.flag.saint.lucia:before {
+  background-position: -36px -1274px;
+}
+i.flag.li:before,
+i.flag.liechtenstein:before {
+  background-position: -36px -1300px;
+}
+i.flag.lk:before,
+i.flag.sri.lanka:before {
+  background-position: -36px -1326px;
+}
+i.flag.lr:before,
+i.flag.liberia:before {
+  background-position: -36px -1352px;
+}
+i.flag.ls:before,
+i.flag.lesotho:before {
+  background-position: -36px -1378px;
+}
+i.flag.lt:before,
+i.flag.lithuania:before {
+  background-position: -36px -1404px;
+}
+i.flag.lu:before,
+i.flag.luxembourg:before {
+  background-position: -36px -1430px;
+}
+i.flag.lv:before,
+i.flag.latvia:before {
+  background-position: -36px -1456px;
+}
+i.flag.ly:before,
+i.flag.libya:before {
+  background-position: -36px -1482px;
+}
+i.flag.ma:before,
+i.flag.morocco:before {
+  background-position: -36px -1508px;
+}
+i.flag.mc:before,
+i.flag.monaco:before {
+  background-position: -36px -1534px;
+}
+i.flag.md:before,
+i.flag.moldova:before {
+  background-position: -36px -1560px;
+}
+i.flag.me:before,
+i.flag.montenegro:before {
+  background-position: -36px -1586px;
+}
+i.flag.mg:before,
+i.flag.madagascar:before {
+  background-position: -36px -1613px;
+}
+i.flag.mh:before,
+i.flag.marshall.islands:before {
+  background-position: -36px -1639px;
+}
+i.flag.mk:before,
+i.flag.macedonia:before {
+  background-position: -36px -1665px;
+}
+i.flag.ml:before,
+i.flag.mali:before {
+  background-position: -36px -1691px;
+}
+i.flag.mm:before,
+i.flag.myanmar:before,
+i.flag.burma:before {
+  background-position: -36px -1717px;
+}
+i.flag.mn:before,
+i.flag.mongolia:before {
+  background-position: -36px -1743px;
+}
+i.flag.mo:before,
+i.flag.macau:before {
+  background-position: -36px -1769px;
+}
+i.flag.mp:before,
+i.flag.northern.mariana.islands:before {
+  background-position: -36px -1795px;
+}
+i.flag.mq:before,
+i.flag.martinique:before {
+  background-position: -36px -1821px;
+}
+i.flag.mr:before,
+i.flag.mauritania:before {
+  background-position: -36px -1847px;
+}
+i.flag.ms:before,
+i.flag.montserrat:before {
+  background-position: -36px -1873px;
+}
+i.flag.mt:before,
+i.flag.malta:before {
+  background-position: -36px -1899px;
+}
+i.flag.mu:before,
+i.flag.mauritius:before {
+  background-position: -36px -1925px;
+}
+i.flag.mv:before,
+i.flag.maldives:before {
+  background-position: -36px -1951px;
+}
+i.flag.mw:before,
+i.flag.malawi:before {
+  background-position: -36px -1977px;
+}
+i.flag.mx:before,
+i.flag.mexico:before {
+  background-position: -72px 0;
+}
+i.flag.my:before,
+i.flag.malaysia:before {
+  background-position: -72px -26px;
+}
+i.flag.mz:before,
+i.flag.mozambique:before {
+  background-position: -72px -52px;
+}
+i.flag.na:before,
+i.flag.namibia:before {
+  background-position: -72px -78px;
+}
+i.flag.nc:before,
+i.flag.new.caledonia:before {
+  background-position: -72px -104px;
+}
+i.flag.ne:before,
+i.flag.niger:before {
+  background-position: -72px -130px;
+}
+i.flag.nf:before,
+i.flag.norfolk.island:before {
+  background-position: -72px -156px;
+}
+i.flag.ng:before,
+i.flag.nigeria:before {
+  background-position: -72px -182px;
+}
+i.flag.ni:before,
+i.flag.nicaragua:before {
+  background-position: -72px -208px;
+}
+i.flag.nl:before,
+i.flag.netherlands:before {
+  background-position: -72px -234px;
+}
+i.flag.no:before,
+i.flag.norway:before {
+  background-position: -72px -260px;
+}
+i.flag.np:before,
+i.flag.nepal:before {
+  background-position: -72px -286px;
+}
+i.flag.nr:before,
+i.flag.nauru:before {
+  background-position: -72px -312px;
+}
+i.flag.nu:before,
+i.flag.niue:before {
+  background-position: -72px -338px;
+}
+i.flag.nz:before,
+i.flag.new.zealand:before {
+  background-position: -72px -364px;
+}
+i.flag.om:before,
+i.flag.oman:before {
+  background-position: -72px -390px;
+}
+i.flag.pa:before,
+i.flag.panama:before {
+  background-position: -72px -416px;
+}
+i.flag.pe:before,
+i.flag.peru:before {
+  background-position: -72px -442px;
+}
+i.flag.pf:before,
+i.flag.french.polynesia:before {
+  background-position: -72px -468px;
+}
+i.flag.pg:before,
+i.flag.new.guinea:before {
+  background-position: -72px -494px;
+}
+i.flag.ph:before,
+i.flag.philippines:before {
+  background-position: -72px -520px;
+}
+i.flag.pk:before,
+i.flag.pakistan:before {
+  background-position: -72px -546px;
+}
+i.flag.pl:before,
+i.flag.poland:before {
+  background-position: -72px -572px;
+}
+i.flag.pm:before,
+i.flag.saint.pierre:before {
+  background-position: -72px -598px;
+}
+i.flag.pn:before,
+i.flag.pitcairn.islands:before {
+  background-position: -72px -624px;
+}
+i.flag.pr:before,
+i.flag.puerto.rico:before {
+  background-position: -72px -650px;
+}
+i.flag.ps:before,
+i.flag.palestine:before {
+  background-position: -72px -676px;
+}
+i.flag.pt:before,
+i.flag.portugal:before {
+  background-position: -72px -702px;
+}
+i.flag.pw:before,
+i.flag.palau:before {
+  background-position: -72px -728px;
+}
+i.flag.py:before,
+i.flag.paraguay:before {
+  background-position: -72px -754px;
+}
+i.flag.qa:before,
+i.flag.qatar:before {
+  background-position: -72px -780px;
+}
+i.flag.re:before,
+i.flag.reunion:before {
+  background-position: -72px -806px;
+}
+i.flag.ro:before,
+i.flag.romania:before {
+  background-position: -72px -832px;
+}
+i.flag.rs:before,
+i.flag.serbia:before {
+  background-position: -72px -858px;
+}
+i.flag.ru:before,
+i.flag.russia:before {
+  background-position: -72px -884px;
+}
+i.flag.rw:before,
+i.flag.rwanda:before {
+  background-position: -72px -910px;
+}
+i.flag.sa:before,
+i.flag.saudi.arabia:before {
+  background-position: -72px -936px;
+}
+i.flag.sb:before,
+i.flag.solomon.islands:before {
+  background-position: -72px -962px;
+}
+i.flag.sc:before,
+i.flag.seychelles:before {
+  background-position: -72px -988px;
+}
+i.flag.gb.sct:before,
+i.flag.scotland:before {
+  background-position: -72px -1014px;
+}
+i.flag.sd:before,
+i.flag.sudan:before {
+  background-position: -72px -1040px;
+}
+i.flag.se:before,
+i.flag.sweden:before {
+  background-position: -72px -1066px;
+}
+i.flag.sg:before,
+i.flag.singapore:before {
+  background-position: -72px -1092px;
+}
+i.flag.sh:before,
+i.flag.saint.helena:before {
+  background-position: -72px -1118px;
+}
+i.flag.si:before,
+i.flag.slovenia:before {
+  background-position: -72px -1144px;
+}
+i.flag.sj:before,
+i.flag.svalbard:before,
+i.flag.jan.mayen:before {
+  background-position: -72px -1170px;
+}
+i.flag.sk:before,
+i.flag.slovakia:before {
+  background-position: -72px -1196px;
+}
+i.flag.sl:before,
+i.flag.sierra.leone:before {
+  background-position: -72px -1222px;
+}
+i.flag.sm:before,
+i.flag.san.marino:before {
+  background-position: -72px -1248px;
+}
+i.flag.sn:before,
+i.flag.senegal:before {
+  background-position: -72px -1274px;
+}
+i.flag.so:before,
+i.flag.somalia:before {
+  background-position: -72px -1300px;
+}
+i.flag.sr:before,
+i.flag.suriname:before {
+  background-position: -72px -1326px;
+}
+i.flag.st:before,
+i.flag.sao.tome:before {
+  background-position: -72px -1352px;
+}
+i.flag.sv:before,
+i.flag.el.salvador:before {
+  background-position: -72px -1378px;
+}
+i.flag.sy:before,
+i.flag.syria:before {
+  background-position: -72px -1404px;
+}
+i.flag.sz:before,
+i.flag.swaziland:before {
+  background-position: -72px -1430px;
+}
+i.flag.tc:before,
+i.flag.caicos.islands:before {
+  background-position: -72px -1456px;
+}
+i.flag.td:before,
+i.flag.chad:before {
+  background-position: -72px -1482px;
+}
+i.flag.tf:before,
+i.flag.french.territories:before {
+  background-position: -72px -1508px;
+}
+i.flag.tg:before,
+i.flag.togo:before {
+  background-position: -72px -1534px;
+}
+i.flag.th:before,
+i.flag.thailand:before {
+  background-position: -72px -1560px;
+}
+i.flag.tj:before,
+i.flag.tajikistan:before {
+  background-position: -72px -1586px;
+}
+i.flag.tk:before,
+i.flag.tokelau:before {
+  background-position: -72px -1612px;
+}
+i.flag.tl:before,
+i.flag.timorleste:before {
+  background-position: -72px -1638px;
+}
+i.flag.tm:before,
+i.flag.turkmenistan:before {
+  background-position: -72px -1664px;
+}
+i.flag.tn:before,
+i.flag.tunisia:before {
+  background-position: -72px -1690px;
+}
+i.flag.to:before,
+i.flag.tonga:before {
+  background-position: -72px -1716px;
+}
+i.flag.tr:before,
+i.flag.turkey:before {
+  background-position: -72px -1742px;
+}
+i.flag.tt:before,
+i.flag.trinidad:before {
+  background-position: -72px -1768px;
+}
+i.flag.tv:before,
+i.flag.tuvalu:before {
+  background-position: -72px -1794px;
+}
+i.flag.tw:before,
+i.flag.taiwan:before {
+  background-position: -72px -1820px;
+}
+i.flag.tz:before,
+i.flag.tanzania:before {
+  background-position: -72px -1846px;
+}
+i.flag.ua:before,
+i.flag.ukraine:before {
+  background-position: -72px -1872px;
+}
+i.flag.ug:before,
+i.flag.uganda:before {
+  background-position: -72px -1898px;
+}
+i.flag.um:before,
+i.flag.us.minor.islands:before {
+  background-position: -72px -1924px;
+}
+i.flag.us:before,
+i.flag.america:before,
+i.flag.united.states:before {
+  background-position: -72px -1950px;
+}
+i.flag.uy:before,
+i.flag.uruguay:before {
+  background-position: -72px -1976px;
+}
+i.flag.uz:before,
+i.flag.uzbekistan:before {
+  background-position: -108px 0;
+}
+i.flag.va:before,
+i.flag.vatican.city:before {
+  background-position: -108px -26px;
+}
+i.flag.vc:before,
+i.flag.saint.vincent:before {
+  background-position: -108px -52px;
+}
+i.flag.ve:before,
+i.flag.venezuela:before {
+  background-position: -108px -78px;
+}
+i.flag.vg:before,
+i.flag.british.virgin.islands:before {
+  background-position: -108px -104px;
+}
+i.flag.vi:before,
+i.flag.us.virgin.islands:before {
+  background-position: -108px -130px;
+}
+i.flag.vn:before,
+i.flag.vietnam:before {
+  background-position: -108px -156px;
+}
+i.flag.vu:before,
+i.flag.vanuatu:before {
+  background-position: -108px -182px;
+}
+i.flag.gb.wls:before,
+i.flag.wales:before {
+  background-position: -108px -208px;
+}
+i.flag.wf:before,
+i.flag.wallis.and.futuna:before {
+  background-position: -108px -234px;
+}
+i.flag.ws:before,
+i.flag.samoa:before {
+  background-position: -108px -260px;
+}
+i.flag.ye:before,
+i.flag.yemen:before {
+  background-position: -108px -286px;
+}
+i.flag.yt:before,
+i.flag.mayotte:before {
+  background-position: -108px -312px;
+}
+i.flag.za:before,
+i.flag.south.africa:before {
+  background-position: -108px -338px;
+}
+i.flag.zm:before,
+i.flag.zambia:before {
+  background-position: -108px -364px;
+}
+i.flag.zw:before,
+i.flag.zimbabwe:before {
+  background-position: -108px -390px;
+}
+/*rtl:end:ignore*/
+
+
+/*******************************
+         Site Overrides
+*******************************/
+

Разлика између датотеке није приказан због своје велике величине
+ 8 - 0
website/semantic/dist/components/flag.min.css


+ 1172 - 0
website/semantic/dist/components/form.css

@@ -0,0 +1,1172 @@
+/*!
+ * # Fomantic-UI - Form
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */
+
+
+/*******************************
+            Elements
+*******************************/
+
+
+/*--------------------
+        Form
+---------------------*/
+
+.ui.form {
+  position: relative;
+  max-width: 100%;
+}
+
+/*--------------------
+        Content
+---------------------*/
+
+.ui.form > p {
+  margin: 1em 0;
+}
+
+/*--------------------
+        Field
+---------------------*/
+
+.ui.form .field {
+  clear: both;
+  margin: 0 0 1em;
+}
+.ui.form .field:last-child,
+.ui.form .fields:last-child .field {
+  margin-bottom: 0;
+}
+.ui.form .fields .field {
+  clear: both;
+  margin: 0;
+}
+
+/*--------------------
+        Labels
+---------------------*/
+
+.ui.form .field > label {
+  display: block;
+  margin: 0 0 0.28571429rem 0;
+  color: #AAAAAA;
+  font-size: 0.92857143em;
+  font-weight: bold;
+  text-transform: none;
+}
+
+/*--------------------
+    Standard Inputs
+---------------------*/
+
+.ui.form textarea,
+.ui.form input:not([type]),
+.ui.form input[type="date"],
+.ui.form input[type="datetime-local"],
+.ui.form input[type="email"],
+.ui.form input[type="number"],
+.ui.form input[type="password"],
+.ui.form input[type="search"],
+.ui.form input[type="tel"],
+.ui.form input[type="time"],
+.ui.form input[type="text"],
+.ui.form input[type="file"],
+.ui.form input[type="url"] {
+  width: 100%;
+  vertical-align: top;
+}
+
+/* Set max height on unusual input */
+.ui.form ::-webkit-datetime-edit,
+.ui.form ::-webkit-inner-spin-button {
+  height: 1.21428571em;
+}
+.ui.form input:not([type]),
+.ui.form input[type="date"],
+.ui.form input[type="datetime-local"],
+.ui.form input[type="email"],
+.ui.form input[type="number"],
+.ui.form input[type="password"],
+.ui.form input[type="search"],
+.ui.form input[type="tel"],
+.ui.form input[type="time"],
+.ui.form input[type="text"],
+.ui.form input[type="file"],
+.ui.form input[type="url"] {
+  font-family: 'Lato', 'Helvetica Neue', Arial, Helvetica, sans-serif;
+  margin: 0;
+  outline: none;
+  -webkit-appearance: none;
+  -webkit-tap-highlight-color: rgba(255, 255, 255, 0);
+  line-height: 1.21428571em;
+  padding: 0.67857143em 1em;
+  font-size: 1em;
+  background: #FFFFFF;
+  border: 1px solid rgba(34, 36, 38, 0.15);
+  color: #AAAAAA;
+  border-radius: 0.28571429rem;
+  -webkit-box-shadow: 0 0 0 0 transparent inset;
+          box-shadow: 0 0 0 0 transparent inset;
+  -webkit-transition: color 0.1s ease, border-color 0.1s ease;
+  transition: color 0.1s ease, border-color 0.1s ease;
+}
+
+/* Text Area */
+.ui.input textarea,
+.ui.form textarea {
+  margin: 0;
+  -webkit-appearance: none;
+  -webkit-tap-highlight-color: rgba(255, 255, 255, 0);
+  padding: 0.78571429em 1em;
+  background: #FFFFFF;
+  border: 1px solid rgba(34, 36, 38, 0.15);
+  outline: none;
+  color: #AAAAAA;
+  border-radius: 0.28571429rem;
+  -webkit-box-shadow: 0 0 0 0 transparent inset;
+          box-shadow: 0 0 0 0 transparent inset;
+  -webkit-transition: color 0.1s ease, border-color 0.1s ease;
+  transition: color 0.1s ease, border-color 0.1s ease;
+  font-size: 1em;
+  font-family: 'Lato', 'Helvetica Neue', Arial, Helvetica, sans-serif;
+  line-height: 1.2857;
+  resize: vertical;
+}
+.ui.form textarea:not([rows]) {
+  height: 12em;
+  min-height: 8em;
+  max-height: 24em;
+}
+.ui.form textarea,
+.ui.form input[type="checkbox"] {
+  vertical-align: top;
+}
+
+/*--------------------
+    Checkbox margin
+---------------------*/
+
+.ui.form .fields:not(.grouped):not(.inline) .field:not(:only-child) label + .ui.ui.checkbox {
+  margin-top: 0.7em;
+}
+.ui.form .fields:not(.grouped):not(.inline) .field:not(:only-child) .ui.checkbox {
+  margin-top: 2.41428571em;
+}
+.ui.form .fields:not(.grouped):not(.inline) .field:not(:only-child) .ui.toggle.checkbox {
+  margin-top: 2.21428571em;
+}
+.ui.form .fields:not(.grouped):not(.inline) .field:not(:only-child) .ui.slider.checkbox {
+  margin-top: 2.61428571em;
+}
+.ui.ui.form .field .fields .field:not(:only-child) .ui.checkbox {
+  margin-top: 0.6em;
+}
+.ui.ui.form .field .fields .field:not(:only-child) .ui.toggle.checkbox {
+  margin-top: 0.5em;
+}
+.ui.ui.form .field .fields .field:not(:only-child) .ui.slider.checkbox {
+  margin-top: 0.7em;
+}
+
+/*--------------------
+      Transparent
+---------------------*/
+
+.ui.form .field .transparent.input:not(.icon) input,
+.ui.form .field input.transparent,
+.ui.form .field textarea.transparent {
+  padding: 0.67857143em 1em;
+}
+.ui.form .field input.transparent,
+.ui.form .field textarea.transparent {
+  border-color: transparent !important;
+  background-color: transparent !important;
+  -webkit-box-shadow: none !important;
+          box-shadow: none !important;
+}
+.ui.form .field.error .transparent.input input,
+.ui.form .field.error .transparent.input textarea,
+.ui.form .field.error input.transparent,
+.ui.form .field.error textarea.transparent {
+  background-color: #FFF6F6 !important;
+  color: #9F3A38 !important;
+}
+
+/*--------------------------
+  Input w/ attached Button
+---------------------------*/
+
+.ui.form input.attached {
+  width: auto;
+}
+
+/*--------------------
+     Basic Select
+---------------------*/
+
+.ui.form select {
+  display: block;
+  height: auto;
+  width: 100%;
+  background: #FFFFFF;
+  border: 1px solid rgba(34, 36, 38, 0.15);
+  border-radius: 0.28571429rem;
+  -webkit-box-shadow: 0 0 0 0 transparent inset;
+          box-shadow: 0 0 0 0 transparent inset;
+  padding: 0.62em 1em;
+  color: #AAAAAA;
+  -webkit-transition: color 0.1s ease, border-color 0.1s ease;
+  transition: color 0.1s ease, border-color 0.1s ease;
+}
+
+/*--------------------
+       Dropdown
+---------------------*/
+
+
+/* Block */
+.ui.form .field > .selection.dropdown {
+  min-width: auto;
+  width: 100%;
+}
+.ui.form .field > .selection.dropdown > .dropdown.icon {
+  float: right;
+}
+
+/* Inline */
+.ui.form .inline.fields .field > .selection.dropdown,
+.ui.form .inline.field > .selection.dropdown {
+  width: auto;
+}
+.ui.form .inline.fields .field > .selection.dropdown > .dropdown.icon,
+.ui.form .inline.field > .selection.dropdown > .dropdown.icon {
+  float: none;
+}
+
+/*--------------------
+       UI Input
+---------------------*/
+
+
+/* Block */
+.ui.form .field .ui.input,
+.ui.form .fields .field .ui.input,
+.ui.form .wide.field .ui.input {
+  width: 100%;
+}
+
+/* Inline  */
+.ui.form .inline.fields .field:not(.wide) .ui.input,
+.ui.form .inline.field:not(.wide) .ui.input {
+  width: auto;
+  vertical-align: middle;
+}
+
+/* Auto Input */
+.ui.form .fields .field .ui.input input,
+.ui.form .field .ui.input input {
+  width: auto;
+}
+
+/* Full Width Input */
+.ui.form .ten.fields .ui.input input,
+.ui.form .nine.fields .ui.input input,
+.ui.form .eight.fields .ui.input input,
+.ui.form .seven.fields .ui.input input,
+.ui.form .six.fields .ui.input input,
+.ui.form .five.fields .ui.input input,
+.ui.form .four.fields .ui.input input,
+.ui.form .three.fields .ui.input input,
+.ui.form .two.fields .ui.input input,
+.ui.form .wide.field .ui.input input {
+  -webkit-box-flex: 1;
+      -ms-flex: 1 0 auto;
+          flex: 1 0 auto;
+  width: 0;
+}
+
+/*--------------------
+   Types of Messages
+---------------------*/
+
+.ui.form .success.message,
+.ui.form .warning.message,
+.ui.form .error.message,
+.ui.form .error.message:empty {
+  display: none;
+}
+
+/* Assumptions */
+.ui.form .message:first-child {
+  margin-top: 0;
+}
+
+/*--------------------
+   Validation Prompt
+---------------------*/
+
+.ui.form .field .prompt.label {
+  white-space: normal;
+  background: #FFFFFF !important;
+  border: 1px solid #E0B4B4 !important;
+  color: #9F3A38 !important;
+}
+.ui.form .inline.fields .field .prompt,
+.ui.form .inline.field .prompt {
+  vertical-align: top;
+  margin: -0.25em 0 -0.5em 0.5em;
+}
+.ui.form .inline.fields .field .prompt:before,
+.ui.form .inline.field .prompt:before {
+  border-width: 0 0 1px 1px;
+  bottom: auto;
+  right: auto;
+  top: 50%;
+  left: 0;
+}
+
+
+/*******************************
+            States
+*******************************/
+
+
+/*--------------------
+      Autofilled
+---------------------*/
+
+.ui.form .field.field input:-webkit-autofill {
+  -webkit-box-shadow: 0 0 0 100px #FFFFF0 inset !important;
+          box-shadow: 0 0 0 100px #FFFFF0 inset !important;
+  border-color: #E5DFA1 !important;
+}
+
+/* Focus */
+.ui.form .field.field input:-webkit-autofill:focus {
+  -webkit-box-shadow: 0 0 0 100px #FFFFF0 inset !important;
+          box-shadow: 0 0 0 100px #FFFFF0 inset !important;
+  border-color: #D5C315 !important;
+}
+
+/* Error */
+.ui.form .error.error input:-webkit-autofill {
+  -webkit-box-shadow: 0 0 0 100px #FFFAF0 inset !important;
+          box-shadow: 0 0 0 100px #FFFAF0 inset !important;
+  border-color: #E0B4B4 !important;
+}
+
+/*--------------------
+      Placeholder
+---------------------*/
+
+
+/* browsers require these rules separate */
+.ui.form ::-webkit-input-placeholder {
+  color: #ffffff;
+}
+.ui.form :-ms-input-placeholder {
+  color: #ffffff !important;
+}
+.ui.form ::-moz-placeholder {
+  color: #ffffff;
+}
+.ui.form :focus::-webkit-input-placeholder {
+  color: #ffffff;
+}
+.ui.form :focus:-ms-input-placeholder {
+  color: #ffffff !important;
+}
+.ui.form :focus::-moz-placeholder {
+  color: #ffffff;
+}
+
+/* Error Placeholder */
+.ui.form .error ::-webkit-input-placeholder {
+  color: #e7bdbc;
+}
+.ui.form .error :-ms-input-placeholder {
+  color: #e7bdbc !important;
+}
+.ui.form .error ::-moz-placeholder {
+  color: #e7bdbc;
+}
+.ui.form .error :focus::-webkit-input-placeholder {
+  color: #da9796;
+}
+.ui.form .error :focus:-ms-input-placeholder {
+  color: #da9796 !important;
+}
+.ui.form .error :focus::-moz-placeholder {
+  color: #da9796;
+}
+
+/*--------------------
+        Focus
+---------------------*/
+
+.ui.form input:not([type]):focus,
+.ui.form input[type="date"]:focus,
+.ui.form input[type="datetime-local"]:focus,
+.ui.form input[type="email"]:focus,
+.ui.form input[type="number"]:focus,
+.ui.form input[type="password"]:focus,
+.ui.form input[type="search"]:focus,
+.ui.form input[type="tel"]:focus,
+.ui.form input[type="time"]:focus,
+.ui.form input[type="text"]:focus,
+.ui.form input[type="file"]:focus,
+.ui.form input[type="url"]:focus {
+  color: rgba(0, 0, 0, 0.95);
+  border-color: #85B7D9;
+  border-radius: 0.28571429rem;
+  background: #FFFFFF;
+  -webkit-box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.35) inset;
+          box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.35) inset;
+}
+.ui.form textarea:focus {
+  color: rgba(0, 0, 0, 0.95);
+  border-color: #85B7D9;
+  border-radius: 0.28571429rem;
+  background: #FFFFFF;
+  -webkit-box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.35) inset;
+          box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.35) inset;
+  -webkit-appearance: none;
+}
+
+/*--------------------
+        Success
+---------------------*/
+
+
+/* On Form */
+.ui.form.success .success.message:not(:empty) {
+  display: block;
+}
+.ui.form.success .compact.success.message:not(:empty) {
+  display: inline-block;
+}
+.ui.form.success .icon.success.message:not(:empty) {
+  display: -webkit-box;
+  display: -ms-flexbox;
+  display: flex;
+}
+
+/*--------------------
+        Warning
+---------------------*/
+
+
+/* On Form */
+.ui.form.warning .warning.message:not(:empty) {
+  display: block;
+}
+.ui.form.warning .compact.warning.message:not(:empty) {
+  display: inline-block;
+}
+.ui.form.warning .icon.warning.message:not(:empty) {
+  display: -webkit-box;
+  display: -ms-flexbox;
+  display: flex;
+}
+
+/*--------------------
+        Error
+---------------------*/
+
+
+/* On Form */
+.ui.form.error .error.message:not(:empty) {
+  display: block;
+}
+.ui.form.error .compact.error.message:not(:empty) {
+  display: inline-block;
+}
+.ui.form.error .icon.error.message:not(:empty) {
+  display: -webkit-box;
+  display: -ms-flexbox;
+  display: flex;
+}
+
+/* On Field(s) */
+.ui.ui.form .fields.error .field label,
+.ui.ui.form .field.error label,
+.ui.ui.form .fields.error .field .input,
+.ui.ui.form .field.error .input {
+  color: #9F3A38;
+}
+.ui.form .fields.error .field .corner.label,
+.ui.form .field.error .corner.label {
+  border-color: #9F3A38;
+  color: #FFFFFF;
+}
+.ui.form .fields.error .field textarea,
+.ui.form .fields.error .field select,
+.ui.form .fields.error .field input:not([type]),
+.ui.form .fields.error .field input[type="date"],
+.ui.form .fields.error .field input[type="datetime-local"],
+.ui.form .fields.error .field input[type="email"],
+.ui.form .fields.error .field input[type="number"],
+.ui.form .fields.error .field input[type="password"],
+.ui.form .fields.error .field input[type="search"],
+.ui.form .fields.error .field input[type="tel"],
+.ui.form .fields.error .field input[type="time"],
+.ui.form .fields.error .field input[type="text"],
+.ui.form .fields.error .field input[type="file"],
+.ui.form .fields.error .field input[type="url"],
+.ui.form .field.error textarea,
+.ui.form .field.error select,
+.ui.form .field.error input:not([type]),
+.ui.form .field.error input[type="date"],
+.ui.form .field.error input[type="datetime-local"],
+.ui.form .field.error input[type="email"],
+.ui.form .field.error input[type="number"],
+.ui.form .field.error input[type="password"],
+.ui.form .field.error input[type="search"],
+.ui.form .field.error input[type="tel"],
+.ui.form .field.error input[type="time"],
+.ui.form .field.error input[type="text"],
+.ui.form .field.error input[type="file"],
+.ui.form .field.error input[type="url"] {
+  background: #FFF6F6;
+  border-color: #E0B4B4;
+  color: #9F3A38;
+  border-radius: '';
+  -webkit-box-shadow: none;
+          box-shadow: none;
+}
+.ui.form .field.error textarea:focus,
+.ui.form .field.error select:focus,
+.ui.form .field.error input:not([type]):focus,
+.ui.form .field.error input[type="date"]:focus,
+.ui.form .field.error input[type="datetime-local"]:focus,
+.ui.form .field.error input[type="email"]:focus,
+.ui.form .field.error input[type="number"]:focus,
+.ui.form .field.error input[type="password"]:focus,
+.ui.form .field.error input[type="search"]:focus,
+.ui.form .field.error input[type="tel"]:focus,
+.ui.form .field.error input[type="time"]:focus,
+.ui.form .field.error input[type="text"]:focus,
+.ui.form .field.error input[type="file"]:focus,
+.ui.form .field.error input[type="url"]:focus {
+  background: #FFF6F6;
+  border-color: #E0B4B4;
+  color: #9F3A38;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+}
+
+/* Preserve Native Select Stylings */
+.ui.form .field.error select {
+  -webkit-appearance: menulist-button;
+}
+
+/*------------------
+    Dropdown Error
+--------------------*/
+
+.ui.form .fields.error .field .ui.dropdown,
+.ui.form .fields.error .field .ui.dropdown .item,
+.ui.form .field.error .ui.dropdown,
+.ui.form .field.error .ui.dropdown .text,
+.ui.form .field.error .ui.dropdown .item {
+  background: #FFF6F6;
+  color: #9F3A38;
+}
+.ui.form .fields.error .field .ui.dropdown,
+.ui.form .field.error .ui.dropdown {
+  border-color: #E0B4B4 !important;
+}
+.ui.form .fields.error .field .ui.dropdown:hover,
+.ui.form .field.error .ui.dropdown:hover {
+  border-color: #E0B4B4 !important;
+}
+.ui.form .fields.error .field .ui.dropdown:hover .menu,
+.ui.form .field.error .ui.dropdown:hover .menu {
+  border-color: #E0B4B4;
+}
+.ui.form .fields.error .field .ui.multiple.selection.dropdown > .label,
+.ui.form .field.error .ui.multiple.selection.dropdown > .label {
+  background-color: #EACBCB;
+  color: #9F3A38;
+}
+
+/* Hover */
+.ui.form .fields.error .field .ui.dropdown .menu .item:hover,
+.ui.form .field.error .ui.dropdown .menu .item:hover {
+  background-color: #FBE7E7;
+}
+
+/* Selected */
+.ui.form .fields.error .field .ui.dropdown .menu .selected.item,
+.ui.form .field.error .ui.dropdown .menu .selected.item {
+  background-color: #FBE7E7;
+}
+
+/* Active */
+.ui.form .fields.error .field .ui.dropdown .menu .active.item,
+.ui.form .field.error .ui.dropdown .menu .active.item {
+  background-color: #FDCFCF !important;
+}
+
+/*--------------------
+    Checkbox Error
+---------------------*/
+
+.ui.form .fields.error .field .checkbox:not(.toggle):not(.slider) label,
+.ui.form .field.error .checkbox:not(.toggle):not(.slider) label,
+.ui.form .fields.error .field .checkbox:not(.toggle):not(.slider) .box,
+.ui.form .field.error .checkbox:not(.toggle):not(.slider) .box {
+  color: #9F3A38;
+}
+.ui.form .fields.error .field .checkbox:not(.toggle):not(.slider) label:before,
+.ui.form .field.error .checkbox:not(.toggle):not(.slider) label:before,
+.ui.form .fields.error .field .checkbox:not(.toggle):not(.slider) .box:before,
+.ui.form .field.error .checkbox:not(.toggle):not(.slider) .box:before {
+  background: #FFF6F6;
+  border-color: #E0B4B4;
+}
+.ui.form .fields.error .field .checkbox label:after,
+.ui.form .field.error .checkbox label:after,
+.ui.form .fields.error .field .checkbox .box:after,
+.ui.form .field.error .checkbox .box:after {
+  color: #9F3A38;
+}
+
+/*--------------------
+       Disabled
+---------------------*/
+
+.ui.form .disabled.fields .field,
+.ui.form .disabled.field,
+.ui.form .field :disabled {
+  pointer-events: none;
+  opacity: 0.45;
+}
+.ui.form .field.disabled > label,
+.ui.form .fields.disabled > label {
+  opacity: 0.45;
+}
+.ui.form .field.disabled :disabled {
+  opacity: 1;
+}
+
+/*--------------
+    Loading
+---------------*/
+
+.ui.loading.form {
+  position: relative;
+  cursor: default;
+  pointer-events: none;
+}
+.ui.loading.form:before {
+  position: absolute;
+  content: '';
+  top: 0;
+  left: 0;
+  background: rgba(255, 255, 255, 0.8);
+  width: 100%;
+  height: 100%;
+  z-index: 100;
+}
+.ui.loading.form.segments:before {
+  border-radius: 0.28571429rem;
+}
+.ui.loading.form:after {
+  position: absolute;
+  content: '';
+  top: 50%;
+  left: 50%;
+  margin: -1.5em 0 0 -1.5em;
+  width: 3em;
+  height: 3em;
+  -webkit-animation: loader 0.6s infinite linear;
+          animation: loader 0.6s infinite linear;
+  border: 0.2em solid #767676;
+  border-radius: 500rem;
+  -webkit-box-shadow: 0 0 0 1px transparent;
+          box-shadow: 0 0 0 1px transparent;
+  visibility: visible;
+  z-index: 101;
+}
+
+
+/*******************************
+         Element Types
+*******************************/
+
+
+/*--------------------
+     Required Field
+---------------------*/
+
+.ui.form .required.fields:not(.grouped) > .field > label:after,
+.ui.form .required.fields.grouped > label:after,
+.ui.form .required.field > label:after,
+.ui.form .required.fields:not(.grouped) > .field > .checkbox:after,
+.ui.form .required.field > .checkbox:after,
+.ui.form label.required:after {
+  margin: -0.2em 0 0 0.2em;
+  content: '*';
+  color: #DB2828;
+}
+.ui.form .required.fields:not(.grouped) > .field > label:after,
+.ui.form .required.fields.grouped > label:after,
+.ui.form .required.field > label:after,
+.ui.form label.required:after {
+  display: inline-block;
+  vertical-align: top;
+}
+.ui.form .required.fields:not(.grouped) > .field > .checkbox:after,
+.ui.form .required.field > .checkbox:after {
+  position: absolute;
+  top: 0;
+  left: 100%;
+}
+
+
+/*******************************
+           Variations
+*******************************/
+
+
+/*--------------------
+    Inverted Colors
+---------------------*/
+
+.ui.inverted.form label,
+.ui.form .inverted.segment label,
+.ui.form .inverted.segment .ui.checkbox label,
+.ui.form .inverted.segment .ui.checkbox .box,
+.ui.inverted.form .ui.checkbox label,
+.ui.inverted.form .ui.checkbox .box,
+.ui.inverted.form .inline.fields > label,
+.ui.inverted.form .inline.fields .field > label,
+.ui.inverted.form .inline.fields .field > p,
+.ui.inverted.form .inline.field > label,
+.ui.inverted.form .inline.field > p {
+  color: rgba(255, 255, 255, 0.9);
+}
+.ui.inverted.loading.form {
+  color: #FFFFFF;
+}
+.ui.inverted.loading.form:before {
+  background: rgba(0, 0, 0, 0.85);
+}
+
+/* Inverted Field */
+.ui.inverted.form input:not([type]),
+.ui.inverted.form input[type="date"],
+.ui.inverted.form input[type="datetime-local"],
+.ui.inverted.form input[type="email"],
+.ui.inverted.form input[type="number"],
+.ui.inverted.form input[type="password"],
+.ui.inverted.form input[type="search"],
+.ui.inverted.form input[type="tel"],
+.ui.inverted.form input[type="time"],
+.ui.inverted.form input[type="text"],
+.ui.inverted.form input[type="file"],
+.ui.inverted.form input[type="url"] {
+  background: #FFFFFF;
+  border-color: rgba(255, 255, 255, 0.1);
+  color: #AAAAAA;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+}
+
+/*--------------------
+     Field Groups
+---------------------*/
+
+
+/* Grouped Vertically */
+.ui.form .grouped.fields {
+  display: block;
+  margin: 0 0 1em;
+}
+.ui.form .grouped.fields:last-child {
+  margin-bottom: 0;
+}
+.ui.form .grouped.fields > label {
+  margin: 0 0 0.28571429rem 0;
+  color: #AAAAAA;
+  font-size: 0.92857143em;
+  font-weight: bold;
+  text-transform: none;
+}
+.ui.form .grouped.fields .field,
+.ui.form .grouped.inline.fields .field {
+  display: block;
+  margin: 0.5em 0;
+  padding: 0;
+}
+
+/*--------------------
+        Fields
+---------------------*/
+
+
+/* Split fields */
+.ui.form .fields {
+  display: -webkit-box;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-box-orient: horizontal;
+  -webkit-box-direction: normal;
+      -ms-flex-direction: row;
+          flex-direction: row;
+  margin: 0 -0.5em 1em;
+}
+.ui.form .fields > .field {
+  -webkit-box-flex: 0;
+      -ms-flex: 0 1 auto;
+          flex: 0 1 auto;
+  padding-left: 0.5em;
+  padding-right: 0.5em;
+}
+.ui.form .fields > .field:first-child {
+  border-left: none;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+}
+
+/* Other Combinations */
+.ui.form .two.fields > .fields,
+.ui.form .two.fields > .field {
+  width: 50%;
+}
+.ui.form .three.fields > .fields,
+.ui.form .three.fields > .field {
+  width: 33.33333333%;
+}
+.ui.form .four.fields > .fields,
+.ui.form .four.fields > .field {
+  width: 25%;
+}
+.ui.form .five.fields > .fields,
+.ui.form .five.fields > .field {
+  width: 20%;
+}
+.ui.form .six.fields > .fields,
+.ui.form .six.fields > .field {
+  width: 16.66666667%;
+}
+.ui.form .seven.fields > .fields,
+.ui.form .seven.fields > .field {
+  width: 14.28571429%;
+}
+.ui.form .eight.fields > .fields,
+.ui.form .eight.fields > .field {
+  width: 12.5%;
+}
+.ui.form .nine.fields > .fields,
+.ui.form .nine.fields > .field {
+  width: 11.11111111%;
+}
+.ui.form .ten.fields > .fields,
+.ui.form .ten.fields > .field {
+  width: 10%;
+}
+
+/* Swap to full width on mobile */
+@media only screen and (max-width: 767.98px) {
+  .ui.form .fields {
+    -ms-flex-wrap: wrap;
+        flex-wrap: wrap;
+  }
+  .ui[class*="equal width"].form:not(.unstackable) .fields > .field,
+  .ui.form:not(.unstackable) [class*="equal width"].fields:not(.unstackable) > .field,
+  .ui.form:not(.unstackable) .two.fields:not(.unstackable) > .fields,
+  .ui.form:not(.unstackable) .two.fields:not(.unstackable) > .field,
+  .ui.form:not(.unstackable) .three.fields:not(.unstackable) > .fields,
+  .ui.form:not(.unstackable) .three.fields:not(.unstackable) > .field,
+  .ui.form:not(.unstackable) .four.fields:not(.unstackable) > .fields,
+  .ui.form:not(.unstackable) .four.fields:not(.unstackable) > .field,
+  .ui.form:not(.unstackable) .five.fields:not(.unstackable) > .fields,
+  .ui.form:not(.unstackable) .five.fields:not(.unstackable) > .field,
+  .ui.form:not(.unstackable) .six.fields:not(.unstackable) > .fields,
+  .ui.form:not(.unstackable) .six.fields:not(.unstackable) > .field,
+  .ui.form:not(.unstackable) .seven.fields:not(.unstackable) > .fields,
+  .ui.form:not(.unstackable) .seven.fields:not(.unstackable) > .field,
+  .ui.form:not(.unstackable) .eight.fields:not(.unstackable) > .fields,
+  .ui.form:not(.unstackable) .eight.fields:not(.unstackable) > .field,
+  .ui.form:not(.unstackable) .nine.fields:not(.unstackable) > .fields,
+  .ui.form:not(.unstackable) .nine.fields:not(.unstackable) > .field,
+  .ui.form:not(.unstackable) .ten.fields:not(.unstackable) > .fields,
+  .ui.form:not(.unstackable) .ten.fields:not(.unstackable) > .field {
+    width: 100% !important;
+    margin: 0 0 1em;
+  }
+}
+
+/* Sizing Combinations */
+.ui.form .fields .wide.field {
+  width: 6.25%;
+  padding-left: 0.5em;
+  padding-right: 0.5em;
+}
+.ui.form .one.wide.field {
+  width: 6.25% !important;
+}
+.ui.form .two.wide.field {
+  width: 12.5% !important;
+}
+.ui.form .three.wide.field {
+  width: 18.75% !important;
+}
+.ui.form .four.wide.field {
+  width: 25% !important;
+}
+.ui.form .five.wide.field {
+  width: 31.25% !important;
+}
+.ui.form .six.wide.field {
+  width: 37.5% !important;
+}
+.ui.form .seven.wide.field {
+  width: 43.75% !important;
+}
+.ui.form .eight.wide.field {
+  width: 50% !important;
+}
+.ui.form .nine.wide.field {
+  width: 56.25% !important;
+}
+.ui.form .ten.wide.field {
+  width: 62.5% !important;
+}
+.ui.form .eleven.wide.field {
+  width: 68.75% !important;
+}
+.ui.form .twelve.wide.field {
+  width: 75% !important;
+}
+.ui.form .thirteen.wide.field {
+  width: 81.25% !important;
+}
+.ui.form .fourteen.wide.field {
+  width: 87.5% !important;
+}
+.ui.form .fifteen.wide.field {
+  width: 93.75% !important;
+}
+.ui.form .sixteen.wide.field {
+  width: 100% !important;
+}
+
+/* Swap to full width on mobile */
+@media only screen and (max-width: 767.98px) {
+  .ui.form:not(.unstackable) .two.fields:not(.unstackable) > .fields,
+  .ui.form:not(.unstackable) .two.fields:not(.unstackable) > .field,
+  .ui.form:not(.unstackable) .three.fields:not(.unstackable) > .fields,
+  .ui.form:not(.unstackable) .three.fields:not(.unstackable) > .field,
+  .ui.form:not(.unstackable) .four.fields:not(.unstackable) > .fields,
+  .ui.form:not(.unstackable) .four.fields:not(.unstackable) > .field,
+  .ui.form:not(.unstackable) .five.fields:not(.unstackable) > .fields,
+  .ui.form:not(.unstackable) .five.fields:not(.unstackable) > .field,
+  .ui.form:not(.unstackable) .fields:not(.unstackable) > .two.wide.field,
+  .ui.form:not(.unstackable) .fields:not(.unstackable) > .three.wide.field,
+  .ui.form:not(.unstackable) .fields:not(.unstackable) > .four.wide.field,
+  .ui.form:not(.unstackable) .fields:not(.unstackable) > .five.wide.field,
+  .ui.form:not(.unstackable) .fields:not(.unstackable) > .six.wide.field,
+  .ui.form:not(.unstackable) .fields:not(.unstackable) > .seven.wide.field,
+  .ui.form:not(.unstackable) .fields:not(.unstackable) > .eight.wide.field,
+  .ui.form:not(.unstackable) .fields:not(.unstackable) > .nine.wide.field,
+  .ui.form:not(.unstackable) .fields:not(.unstackable) > .ten.wide.field,
+  .ui.form:not(.unstackable) .fields:not(.unstackable) > .eleven.wide.field,
+  .ui.form:not(.unstackable) .fields:not(.unstackable) > .twelve.wide.field,
+  .ui.form:not(.unstackable) .fields:not(.unstackable) > .thirteen.wide.field,
+  .ui.form:not(.unstackable) .fields:not(.unstackable) > .fourteen.wide.field,
+  .ui.form:not(.unstackable) .fields:not(.unstackable) > .fifteen.wide.field,
+  .ui.form:not(.unstackable) .fields:not(.unstackable) > .sixteen.wide.field {
+    width: 100% !important;
+  }
+  .ui.form .fields {
+    margin-bottom: 0;
+  }
+}
+
+/*--------------------
+     Equal Width
+---------------------*/
+
+.ui[class*="equal width"].form .fields > .field,
+.ui.form [class*="equal width"].fields > .field {
+  width: 100%;
+  -webkit-box-flex: 1;
+      -ms-flex: 1 1 auto;
+          flex: 1 1 auto;
+}
+
+/*--------------------
+    Inline Fields
+---------------------*/
+
+.ui.form .inline.fields {
+  margin: 0 0 1em;
+  -webkit-box-align: center;
+      -ms-flex-align: center;
+          align-items: center;
+}
+.ui.form .inline.fields .field {
+  margin: 0;
+  padding: 0 1em 0 0;
+}
+
+/* Inline Label */
+.ui.form .inline.fields > label,
+.ui.form .inline.fields .field > label,
+.ui.form .inline.fields .field > p,
+.ui.form .inline.field > label,
+.ui.form .inline.field > p {
+  display: inline-block;
+  width: auto;
+  margin-top: 0;
+  margin-bottom: 0;
+  vertical-align: baseline;
+  font-size: 0.92857143em;
+  font-weight: bold;
+  color: #AAAAAA;
+  text-transform: none;
+}
+
+/* Grouped Inline Label */
+.ui.form .inline.fields > label {
+  margin: 0.035714em 1em 0 0;
+}
+
+/* Inline Input */
+.ui.form .inline.fields .field > input,
+.ui.form .inline.fields .field > select,
+.ui.form .inline.field > input,
+.ui.form .inline.field > select {
+  display: inline-block;
+  width: auto;
+  margin-top: 0;
+  margin-bottom: 0;
+  vertical-align: middle;
+  font-size: 1em;
+}
+
+/* Label */
+.ui.form .inline.fields .field > :first-child,
+.ui.form .inline.field > :first-child {
+  margin: 0 0.85714286em 0 0;
+}
+.ui.form .inline.fields .field > :only-child,
+.ui.form .inline.field > :only-child {
+  margin: 0;
+}
+
+/* Wide */
+.ui.form .inline.fields .wide.field {
+  display: -webkit-box;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-box-align: center;
+      -ms-flex-align: center;
+          align-items: center;
+}
+.ui.form .inline.fields .wide.field > input,
+.ui.form .inline.fields .wide.field > select {
+  width: 100%;
+}
+
+/*--------------------
+        Sizes
+---------------------*/
+
+.ui.mini.form {
+  font-size: 0.78571429rem;
+}
+.ui.tiny.form {
+  font-size: 0.85714286rem;
+}
+.ui.small.form {
+  font-size: 0.92857143rem;
+}
+.ui.form {
+  font-size: 1rem;
+}
+.ui.large.form {
+  font-size: 1.14285714rem;
+}
+.ui.big.form {
+  font-size: 1.28571429rem;
+}
+.ui.huge.form {
+  font-size: 1.42857143rem;
+}
+.ui.massive.form {
+  font-size: 1.71428571rem;
+}
+
+/* Dropdown */
+.ui.mini.form .field .dropdown {
+  font-size: 0.78571429rem;
+}
+.ui.tiny.form .field .dropdown {
+  font-size: 0.85714286rem;
+}
+.ui.small.form .field .dropdown {
+  font-size: 0.92857143rem;
+}
+.ui.form .field .dropdown {
+  font-size: 1rem;
+}
+.ui.large.form .field .dropdown {
+  font-size: 1.14285714rem;
+}
+.ui.big.form .field .dropdown {
+  font-size: 1.28571429rem;
+}
+.ui.huge.form .field .dropdown {
+  font-size: 1.42857143rem;
+}
+.ui.massive.form .field .dropdown {
+  font-size: 1.71428571rem;
+}
+
+/* Dropdown Items */
+.ui.mini.form .field .dropdown .menu > .item {
+  font-size: 0.78571429rem;
+}
+.ui.tiny.form .field .dropdown .menu > .item {
+  font-size: 0.85714286rem;
+}
+.ui.small.form .field .dropdown .menu > .item {
+  font-size: 0.92857143rem;
+}
+.ui.form .field .dropdown .menu > .item {
+  font-size: 1rem;
+}
+.ui.large.form .field .dropdown .menu > .item {
+  font-size: 1.14285714rem;
+}
+.ui.big.form .field .dropdown .menu > .item {
+  font-size: 1.28571429rem;
+}
+.ui.huge.form .field .dropdown .menu > .item {
+  font-size: 1.42857143rem;
+}
+.ui.massive.form .field .dropdown .menu > .item {
+  font-size: 1.71428571rem;
+}
+
+
+/*******************************
+         Theme Overrides
+*******************************/
+
+
+
+/*******************************
+         Site Overrides
+*******************************/
+

+ 1966 - 0
website/semantic/dist/components/form.js

@@ -0,0 +1,1966 @@
+/*!
+ * # Fomantic-UI - Form Validation
+ * http://github.com/fomantic/Fomantic-UI/
+ *
+ *
+ * Released under the MIT license
+ * http://opensource.org/licenses/MIT
+ *
+ */
+
+;(function ($, window, document, undefined) {
+
+'use strict';
+
+$.isFunction = $.isFunction || function(obj) {
+  return typeof obj === "function" && typeof obj.nodeType !== "number";
+};
+
+window = (typeof window != 'undefined' && window.Math == Math)
+  ? window
+  : (typeof self != 'undefined' && self.Math == Math)
+    ? self
+    : Function('return this')()
+;
+
+$.fn.form = function(parameters) {
+  var
+    $allModules      = $(this),
+    moduleSelector   = $allModules.selector || '',
+
+    time             = new Date().getTime(),
+    performance      = [],
+
+    query            = arguments[0],
+    legacyParameters = arguments[1],
+    methodInvoked    = (typeof query == 'string'),
+    queryArguments   = [].slice.call(arguments, 1),
+    returnedValue
+  ;
+  $allModules
+    .each(function() {
+      var
+        $module     = $(this),
+        element     = this,
+
+        formErrors  = [],
+        keyHeldDown = false,
+
+        // set at run-time
+        $field,
+        $group,
+        $message,
+        $prompt,
+        $submit,
+        $clear,
+        $reset,
+
+        settings,
+        validation,
+
+        metadata,
+        selector,
+        className,
+        regExp,
+        error,
+
+        namespace,
+        moduleNamespace,
+        eventNamespace,
+
+        submitting = false,
+        dirty = false,
+        history = ['clean', 'clean'],
+
+        instance,
+        module
+      ;
+
+      module      = {
+
+        initialize: function() {
+
+          // settings grabbed at run time
+          module.get.settings();
+          if(methodInvoked) {
+            if(instance === undefined) {
+              module.instantiate();
+            }
+            module.invoke(query);
+          }
+          else {
+            if(instance !== undefined) {
+              instance.invoke('destroy');
+            }
+            module.verbose('Initializing form validation', $module, settings);
+            module.bindEvents();
+            module.set.defaults();
+            module.instantiate();
+          }
+        },
+
+        instantiate: function() {
+          module.verbose('Storing instance of module', module);
+          instance = module;
+          $module
+            .data(moduleNamespace, module)
+          ;
+        },
+
+        destroy: function() {
+          module.verbose('Destroying previous module', instance);
+          module.removeEvents();
+          $module
+            .removeData(moduleNamespace)
+          ;
+        },
+
+        refresh: function() {
+          module.verbose('Refreshing selector cache');
+          $field      = $module.find(selector.field);
+          $group      = $module.find(selector.group);
+          $message    = $module.find(selector.message);
+          $prompt     = $module.find(selector.prompt);
+
+          $submit     = $module.find(selector.submit);
+          $clear      = $module.find(selector.clear);
+          $reset      = $module.find(selector.reset);
+        },
+
+        submit: function() {
+          module.verbose('Submitting form', $module);
+          submitting = true;
+          $module.submit();
+        },
+
+        attachEvents: function(selector, action) {
+          action = action || 'submit';
+          $(selector).on('click' + eventNamespace, function(event) {
+            module[action]();
+            event.preventDefault();
+          });
+        },
+
+        bindEvents: function() {
+          module.verbose('Attaching form events');
+          $module
+            .on('submit' + eventNamespace, module.validate.form)
+            .on('blur'   + eventNamespace, selector.field, module.event.field.blur)
+            .on('click'  + eventNamespace, selector.submit, module.submit)
+            .on('click'  + eventNamespace, selector.reset, module.reset)
+            .on('click'  + eventNamespace, selector.clear, module.clear)
+          ;
+          if(settings.keyboardShortcuts) {
+            $module.on('keydown' + eventNamespace, selector.field, module.event.field.keydown);
+          }
+          $field.each(function(index, el) {
+            var
+              $input     = $(el),
+              type       = $input.prop('type'),
+              inputEvent = module.get.changeEvent(type, $input)
+            ;
+            $input.on(inputEvent + eventNamespace, module.event.field.change);
+          });
+
+          // Dirty events
+          if (settings.preventLeaving) {
+            $(window).on('beforeunload' + eventNamespace, module.event.beforeUnload);
+          }
+
+          $field.on('change click keyup keydown blur', function(e) {
+            $(this).trigger(e.type + ".dirty");
+          });
+
+          $field.on('change.dirty click.dirty keyup.dirty keydown.dirty blur.dirty', module.determine.isDirty);
+
+          $module.on('dirty' + eventNamespace, function(e) {
+            settings.onDirty.call();
+          });
+
+          $module.on('clean' + eventNamespace, function(e) {
+            settings.onClean.call();
+          })
+        },
+
+        clear: function() {
+          $field.each(function (index, el) {
+            var
+              $field       = $(el),
+              $element     = $field.parent(),
+              $fieldGroup  = $field.closest($group),
+              $prompt      = $fieldGroup.find(selector.prompt),
+              $calendar    = $field.closest(selector.uiCalendar),
+              defaultValue = $field.data(metadata.defaultValue) || '',
+              isCheckbox   = $element.is(selector.uiCheckbox),
+              isDropdown   = $element.is(selector.uiDropdown)  && module.can.useElement('dropdown'),
+              isCalendar   = ($calendar.length > 0  && module.can.useElement('calendar')),
+              isErrored    = $fieldGroup.hasClass(className.error)
+            ;
+            if(isErrored) {
+              module.verbose('Resetting error on field', $fieldGroup);
+              $fieldGroup.removeClass(className.error);
+              $prompt.remove();
+            }
+            if(isDropdown) {
+              module.verbose('Resetting dropdown value', $element, defaultValue);
+              $element.dropdown('clear');
+            }
+            else if(isCheckbox) {
+              $field.prop('checked', false);
+            }
+            else if (isCalendar) {
+              $calendar.calendar('clear');
+            }
+            else {
+              module.verbose('Resetting field value', $field, defaultValue);
+              $field.val('');
+            }
+          });
+        },
+
+        reset: function() {
+          $field.each(function (index, el) {
+            var
+              $field       = $(el),
+              $element     = $field.parent(),
+              $fieldGroup  = $field.closest($group),
+              $calendar    = $field.closest(selector.uiCalendar),
+              $prompt      = $fieldGroup.find(selector.prompt),
+              defaultValue = $field.data(metadata.defaultValue),
+              isCheckbox   = $element.is(selector.uiCheckbox),
+              isDropdown   = $element.is(selector.uiDropdown)  && module.can.useElement('dropdown'),
+              isCalendar   = ($calendar.length > 0  && module.can.useElement('calendar')),
+              isErrored    = $fieldGroup.hasClass(className.error)
+            ;
+            if(defaultValue === undefined) {
+              return;
+            }
+            if(isErrored) {
+              module.verbose('Resetting error on field', $fieldGroup);
+              $fieldGroup.removeClass(className.error);
+              $prompt.remove();
+            }
+            if(isDropdown) {
+              module.verbose('Resetting dropdown value', $element, defaultValue);
+              $element.dropdown('restore defaults');
+            }
+            else if(isCheckbox) {
+              module.verbose('Resetting checkbox value', $element, defaultValue);
+              $field.prop('checked', defaultValue);
+            }
+            else if (isCalendar) {
+              $calendar.calendar('set date', defaultValue);
+            }
+            else {
+              module.verbose('Resetting field value', $field, defaultValue);
+              $field.val(defaultValue);
+            }
+          });
+
+          module.determine.isDirty();
+        },
+
+        determine: {
+          isValid: function() {
+            var
+              allValid = true
+            ;
+            $.each(validation, function(fieldName, field) {
+              if( !( module.validate.field(field, fieldName, true) ) ) {
+                allValid = false;
+              }
+            });
+            return allValid;
+          },
+          isDirty: function(e) {
+            var formIsDirty = false;
+
+            $field.each(function(index, el) {
+              var
+                $el = $(el),
+                isCheckbox = ($el.filter(selector.checkbox).length > 0),
+                isDirty
+              ;
+
+              if (isCheckbox) {
+                isDirty = module.is.checkboxDirty($el);
+              } else {
+                isDirty = module.is.fieldDirty($el);
+              }
+
+              $el.data(settings.metadata.isDirty, isDirty);
+
+              formIsDirty |= isDirty;
+            });
+
+            if (formIsDirty) {
+              module.set.dirty();
+            } else {
+              module.set.clean();
+            }
+
+            if (e && e.namespace === 'dirty') {
+              e.stopImmediatePropagation();
+              e.preventDefault();
+            }
+          }
+        },
+
+        is: {
+          bracketedRule: function(rule) {
+            return (rule.type && rule.type.match(settings.regExp.bracket));
+          },
+          shorthandFields: function(fields) {
+            var
+              fieldKeys = Object.keys(fields),
+              firstRule = fields[fieldKeys[0]]
+            ;
+            return module.is.shorthandRules(firstRule);
+          },
+          // duck type rule test
+          shorthandRules: function(rules) {
+            return (typeof rules == 'string' || Array.isArray(rules));
+          },
+          empty: function($field) {
+            if(!$field || $field.length === 0) {
+              return true;
+            }
+            else if($field.is(selector.checkbox)) {
+              return !$field.is(':checked');
+            }
+            else {
+              return module.is.blank($field);
+            }
+          },
+          blank: function($field) {
+            return $.trim($field.val()) === '';
+          },
+          valid: function(field) {
+            var
+              allValid = true
+            ;
+            if(field) {
+              module.verbose('Checking if field is valid', field);
+              return module.validate.field(validation[field], field, false);
+            }
+            else {
+              module.verbose('Checking if form is valid');
+              $.each(validation, function(fieldName, field) {
+                if( !module.is.valid(fieldName) ) {
+                  allValid = false;
+                }
+              });
+              return allValid;
+            }
+          },
+          dirty: function() {
+            return dirty;
+          },
+          clean: function() {
+            return !dirty;
+          },
+          fieldDirty: function($el) {
+            var initialValue = $el.data(metadata.defaultValue);
+            // Explicitly check for null/undefined here as value may be `false`, so ($el.data(dataInitialValue) || '') would not work
+            if (initialValue == null) { initialValue = ''; }
+            var currentValue = $el.val();
+            if (currentValue == null) { currentValue = ''; }
+
+            // Boolean values can be encoded as "true/false" or "True/False" depending on underlying frameworks so we need a case insensitive comparison
+            var boolRegex = /^(true|false)$/i;
+            var isBoolValue = boolRegex.test(initialValue) && boolRegex.test(currentValue);
+            if (isBoolValue) {
+              var regex = new RegExp("^" + initialValue + "$", "i");
+              return !regex.test(currentValue);
+            }
+
+            return currentValue !== initialValue;
+          },
+          checkboxDirty: function($el) {
+            var initialValue = $el.data(metadata.defaultValue);
+            var currentValue = $el.is(":checked");
+
+            return initialValue !== currentValue;
+          },
+          justDirty: function() {
+            return (history[0] === 'dirty');
+          },
+          justClean: function() {
+            return (history[0] === 'clean');
+          }
+        },
+
+        removeEvents: function() {
+          $module.off(eventNamespace);
+          $field.off(eventNamespace);
+          $submit.off(eventNamespace);
+          $field.off(eventNamespace);
+        },
+
+        event: {
+          field: {
+            keydown: function(event) {
+              var
+                $field       = $(this),
+                key          = event.which,
+                isInput      = $field.is(selector.input),
+                isCheckbox   = $field.is(selector.checkbox),
+                isInDropdown = ($field.closest(selector.uiDropdown).length > 0),
+                keyCode      = {
+                  enter  : 13,
+                  escape : 27
+                }
+              ;
+              if( key == keyCode.escape) {
+                module.verbose('Escape key pressed blurring field');
+                $field
+                  .blur()
+                ;
+              }
+              if(!event.ctrlKey && key == keyCode.enter && isInput && !isInDropdown && !isCheckbox) {
+                if(!keyHeldDown) {
+                  $field.one('keyup' + eventNamespace, module.event.field.keyup);
+                  module.submit();
+                  module.debug('Enter pressed on input submitting form');
+                }
+                keyHeldDown = true;
+              }
+            },
+            keyup: function() {
+              keyHeldDown = false;
+            },
+            blur: function(event) {
+              var
+                $field          = $(this),
+                $fieldGroup     = $field.closest($group),
+                validationRules = module.get.validation($field)
+              ;
+              if( $fieldGroup.hasClass(className.error) ) {
+                module.debug('Revalidating field', $field, validationRules);
+                if(validationRules) {
+                  module.validate.field( validationRules );
+                }
+              }
+              else if(settings.on == 'blur') {
+                if(validationRules) {
+                  module.validate.field( validationRules );
+                }
+              }
+            },
+            change: function(event) {
+              var
+                $field      = $(this),
+                $fieldGroup = $field.closest($group),
+                validationRules = module.get.validation($field)
+              ;
+              if(validationRules && (settings.on == 'change' || ( $fieldGroup.hasClass(className.error) && settings.revalidate) )) {
+                clearTimeout(module.timer);
+                module.timer = setTimeout(function() {
+                  module.debug('Revalidating field', $field,  module.get.validation($field));
+                  module.validate.field( validationRules );
+                }, settings.delay);
+              }
+            }
+          },
+          beforeUnload: function(event) {
+            if (module.is.dirty() && !submitting) {
+              var event = event || window.event;
+
+              // For modern browsers
+              if (event) {
+                event.returnValue = settings.text.leavingMessage;
+              }
+
+              // For olders...
+              return settings.text.leavingMessage;
+            }
+          }
+
+        },
+
+        get: {
+          ancillaryValue: function(rule) {
+            if(!rule.type || (!rule.value && !module.is.bracketedRule(rule))) {
+              return false;
+            }
+            return (rule.value !== undefined)
+              ? rule.value
+              : rule.type.match(settings.regExp.bracket)[1] + ''
+            ;
+          },
+          ruleName: function(rule) {
+            if( module.is.bracketedRule(rule) ) {
+              return rule.type.replace(rule.type.match(settings.regExp.bracket)[0], '');
+            }
+            return rule.type;
+          },
+          changeEvent: function(type, $input) {
+            if(type == 'checkbox' || type == 'radio' || type == 'hidden' || $input.is('select')) {
+              return 'change';
+            }
+            else {
+              return module.get.inputEvent();
+            }
+          },
+          inputEvent: function() {
+            return (document.createElement('input').oninput !== undefined)
+              ? 'input'
+              : (document.createElement('input').onpropertychange !== undefined)
+                ? 'propertychange'
+                : 'keyup'
+            ;
+          },
+          fieldsFromShorthand: function(fields) {
+            var
+              fullFields = {}
+            ;
+            $.each(fields, function(name, rules) {
+              if(typeof rules == 'string') {
+                rules = [rules];
+              }
+              fullFields[name] = {
+                rules: []
+              };
+              $.each(rules, function(index, rule) {
+                fullFields[name].rules.push({ type: rule });
+              });
+            });
+            return fullFields;
+          },
+          prompt: function(rule, field) {
+            var
+              ruleName      = module.get.ruleName(rule),
+              ancillary     = module.get.ancillaryValue(rule),
+              $field        = module.get.field(field.identifier),
+              value         = $field.val(),
+              prompt        = $.isFunction(rule.prompt)
+                ? rule.prompt(value)
+                : rule.prompt || settings.prompt[ruleName] || settings.text.unspecifiedRule,
+              requiresValue = (prompt.search('{value}') !== -1),
+              requiresName  = (prompt.search('{name}') !== -1),
+              $label,
+              name
+            ;
+            if(requiresValue) {
+              prompt = prompt.replace('{value}', $field.val());
+            }
+            if(requiresName) {
+              $label = $field.closest(selector.group).find('label').eq(0);
+              name = ($label.length == 1)
+                ? $label.text()
+                : $field.prop('placeholder') || settings.text.unspecifiedField
+              ;
+              prompt = prompt.replace('{name}', name);
+            }
+            prompt = prompt.replace('{identifier}', field.identifier);
+            prompt = prompt.replace('{ruleValue}', ancillary);
+            if(!rule.prompt) {
+              module.verbose('Using default validation prompt for type', prompt, ruleName);
+            }
+            return prompt;
+          },
+          settings: function() {
+            if($.isPlainObject(parameters)) {
+              var
+                keys     = Object.keys(parameters),
+                isLegacySettings = (keys.length > 0)
+                  ? (parameters[keys[0]].identifier !== undefined && parameters[keys[0]].rules !== undefined)
+                  : false
+              ;
+              if(isLegacySettings) {
+                // 1.x (ducktyped)
+                settings   = $.extend(true, {}, $.fn.form.settings, legacyParameters);
+                validation = $.extend({}, $.fn.form.settings.defaults, parameters);
+                module.error(settings.error.oldSyntax, element);
+                module.verbose('Extending settings from legacy parameters', validation, settings);
+              }
+              else {
+                // 2.x
+                if(parameters.fields && module.is.shorthandFields(parameters.fields)) {
+                  parameters.fields = module.get.fieldsFromShorthand(parameters.fields);
+                }
+                settings   = $.extend(true, {}, $.fn.form.settings, parameters);
+                validation = $.extend({}, $.fn.form.settings.defaults, settings.fields);
+                module.verbose('Extending settings', validation, settings);
+              }
+            }
+            else {
+              settings   = $.fn.form.settings;
+              validation = $.fn.form.settings.defaults;
+              module.verbose('Using default form validation', validation, settings);
+            }
+
+            // shorthand
+            namespace       = settings.namespace;
+            metadata        = settings.metadata;
+            selector        = settings.selector;
+            className       = settings.className;
+            regExp          = settings.regExp;
+            error           = settings.error;
+            moduleNamespace = 'module-' + namespace;
+            eventNamespace  = '.' + namespace;
+
+            // grab instance
+            instance = $module.data(moduleNamespace);
+
+            // refresh selector cache
+            module.refresh();
+          },
+          field: function(identifier) {
+            module.verbose('Finding field with identifier', identifier);
+            identifier = module.escape.string(identifier);
+            var t;
+            if((t=$field.filter('#' + identifier)).length > 0 ) {
+              return t;
+            }
+            if((t=$field.filter('[name="' + identifier +'"]')).length > 0 ) {
+              return t;
+            }
+            if((t=$field.filter('[name="' + identifier +'[]"]')).length > 0 ) {
+              return t;
+            }
+            if((t=$field.filter('[data-' + metadata.validate + '="'+ identifier +'"]')).length > 0 ) {
+              return t;
+            }
+            return $('<input/>');
+          },
+          fields: function(fields) {
+            var
+              $fields = $()
+            ;
+            $.each(fields, function(index, name) {
+              $fields = $fields.add( module.get.field(name) );
+            });
+            return $fields;
+          },
+          validation: function($field) {
+            var
+              fieldValidation,
+              identifier
+            ;
+            if(!validation) {
+              return false;
+            }
+            $.each(validation, function(fieldName, field) {
+              identifier = field.identifier || fieldName;
+              $.each(module.get.field(identifier), function(index, groupField) {
+                if(groupField == $field[0]) {
+                  field.identifier = identifier;
+                  fieldValidation = field;
+                  return false;
+                }
+              });
+            });
+            return fieldValidation || false;
+          },
+          value: function (field) {
+            var
+              fields = [],
+              results
+            ;
+            fields.push(field);
+            results = module.get.values.call(element, fields);
+            return results[field];
+          },
+          values: function (fields) {
+            var
+              $fields = Array.isArray(fields)
+                ? module.get.fields(fields)
+                : $field,
+              values = {}
+            ;
+            $fields.each(function(index, field) {
+              var
+                $field       = $(field),
+                $calendar    = $field.closest(selector.uiCalendar),
+                name         = $field.prop('name'),
+                value        = $field.val(),
+                isCheckbox   = $field.is(selector.checkbox),
+                isRadio      = $field.is(selector.radio),
+                isMultiple   = (name.indexOf('[]') !== -1),
+                isCalendar   = ($calendar.length > 0  && module.can.useElement('calendar')),
+                isChecked    = (isCheckbox)
+                  ? $field.is(':checked')
+                  : false
+              ;
+              if(name) {
+                if(isMultiple) {
+                  name = name.replace('[]', '');
+                  if(!values[name]) {
+                    values[name] = [];
+                  }
+                  if(isCheckbox) {
+                    if(isChecked) {
+                      values[name].push(value || true);
+                    }
+                    else {
+                      values[name].push(false);
+                    }
+                  }
+                  else {
+                    values[name].push(value);
+                  }
+                }
+                else {
+                  if(isRadio) {
+                    if(values[name] === undefined || values[name] == false) {
+                      values[name] = (isChecked)
+                        ? value || true
+                        : false
+                      ;
+                    }
+                  }
+                  else if(isCheckbox) {
+                    if(isChecked) {
+                      values[name] = value || true;
+                    }
+                    else {
+                      values[name] = false;
+                    }
+                  }
+                  else if(isCalendar) {
+                    var date = $calendar.calendar('get date');
+
+                    if (date !== null) {
+                      if (settings.dateHandling == 'date') {
+                        values[name] = date;
+                      } else if(settings.dateHandling == 'input') {
+                        values[name] = $calendar.calendar('get input date')
+                      } else if (settings.dateHandling == 'formatter') {
+                        var type = $calendar.calendar('setting', 'type');
+
+                        switch(type) {
+                          case 'date':
+                          values[name] = settings.formatter.date(date);
+                          break;
+
+                          case 'datetime':
+                          values[name] = settings.formatter.datetime(date);
+                          break;
+
+                          case 'time':
+                          values[name] = settings.formatter.time(date);
+                          break;
+
+                          case 'month':
+                          values[name] = settings.formatter.month(date);
+                          break;
+
+                          case 'year':
+                          values[name] = settings.formatter.year(date);
+                          break;
+
+                          default:
+                          module.debug('Wrong calendar mode', $calendar, type);
+                          values[name] = '';
+                        }
+                      }
+                    } else {
+                      values[name] = '';
+                    }
+                  } else {
+                    values[name] = value;
+                  }
+                }
+              }
+            });
+            return values;
+          },
+          dirtyFields: function() {
+            return $field.filter(function(index, e) {
+              return $(e).data(metadata.isDirty);
+            });
+          }
+        },
+
+        has: {
+
+          field: function(identifier) {
+            module.verbose('Checking for existence of a field with identifier', identifier);
+            identifier = module.escape.string(identifier);
+            if(typeof identifier !== 'string') {
+              module.error(error.identifier, identifier);
+            }
+            if($field.filter('#' + identifier).length > 0 ) {
+              return true;
+            }
+            else if( $field.filter('[name="' + identifier +'"]').length > 0 ) {
+              return true;
+            }
+            else if( $field.filter('[data-' + metadata.validate + '="'+ identifier +'"]').length > 0 ) {
+              return true;
+            }
+            return false;
+          }
+
+        },
+
+        can: {
+            useElement: function(element){
+               if ($.fn[element] !== undefined) {
+                   return true;
+               }
+               module.error(error.noElement.replace('{element}',element));
+               return false;
+            }
+        },
+
+        escape: {
+          string: function(text) {
+            text =  String(text);
+            return text.replace(regExp.escape, '\\$&');
+          }
+        },
+
+        add: {
+          // alias
+          rule: function(name, rules) {
+            module.add.field(name, rules);
+          },
+          field: function(name, rules) {
+            var
+              newValidation = {}
+            ;
+            if(module.is.shorthandRules(rules)) {
+              rules = Array.isArray(rules)
+                ? rules
+                : [rules]
+              ;
+              newValidation[name] = {
+                rules: []
+              };
+              $.each(rules, function(index, rule) {
+                newValidation[name].rules.push({ type: rule });
+              });
+            }
+            else {
+              newValidation[name] = rules;
+            }
+            validation = $.extend({}, validation, newValidation);
+            module.debug('Adding rules', newValidation, validation);
+          },
+          fields: function(fields) {
+            var
+              newValidation
+            ;
+            if(fields && module.is.shorthandFields(fields)) {
+              newValidation = module.get.fieldsFromShorthand(fields);
+            }
+            else {
+              newValidation = fields;
+            }
+            validation = $.extend({}, validation, newValidation);
+          },
+          prompt: function(identifier, errors, internal) {
+            var
+              $field       = module.get.field(identifier),
+              $fieldGroup  = $field.closest($group),
+              $prompt      = $fieldGroup.children(selector.prompt),
+              promptExists = ($prompt.length !== 0)
+            ;
+            errors = (typeof errors == 'string')
+              ? [errors]
+              : errors
+            ;
+            module.verbose('Adding field error state', identifier);
+            if(!internal) {
+              $fieldGroup
+                  .addClass(className.error)
+              ;
+            }
+            if(settings.inline) {
+              if(!promptExists) {
+                $prompt = settings.templates.prompt(errors, className.label);
+                $prompt
+                  .appendTo($fieldGroup)
+                ;
+              }
+              $prompt
+                .html(errors[0])
+              ;
+              if(!promptExists) {
+                if(settings.transition && module.can.useElement('transition') && $module.transition('is supported')) {
+                  module.verbose('Displaying error with css transition', settings.transition);
+                  $prompt.transition(settings.transition + ' in', settings.duration);
+                }
+                else {
+                  module.verbose('Displaying error with fallback javascript animation');
+                  $prompt
+                    .fadeIn(settings.duration)
+                  ;
+                }
+              }
+              else {
+                module.verbose('Inline errors are disabled, no inline error added', identifier);
+              }
+            }
+          },
+          errors: function(errors) {
+            module.debug('Adding form error messages', errors);
+            module.set.error();
+            $message
+              .html( settings.templates.error(errors) )
+            ;
+          }
+        },
+
+        remove: {
+          rule: function(field, rule) {
+            var
+              rules = Array.isArray(rule)
+                ? rule
+                : [rule]
+            ;
+            if(validation[field] === undefined || !Array.isArray(validation[field].rules)) {
+              return;
+            }
+            if(rule === undefined) {
+              module.debug('Removed all rules');
+              validation[field].rules = [];
+              return;
+            }
+            $.each(validation[field].rules, function(index, rule) {
+              if(rule && rules.indexOf(rule.type) !== -1) {
+                module.debug('Removed rule', rule.type);
+                validation[field].rules.splice(index, 1);
+              }
+            });
+          },
+          field: function(field) {
+            var
+              fields = Array.isArray(field)
+                ? field
+                : [field]
+            ;
+            $.each(fields, function(index, field) {
+              module.remove.rule(field);
+            });
+          },
+          // alias
+          rules: function(field, rules) {
+            if(Array.isArray(field)) {
+              $.each(field, function(index, field) {
+                module.remove.rule(field, rules);
+              });
+            }
+            else {
+              module.remove.rule(field, rules);
+            }
+          },
+          fields: function(fields) {
+            module.remove.field(fields);
+          },
+          prompt: function(identifier) {
+            var
+              $field      = module.get.field(identifier),
+              $fieldGroup = $field.closest($group),
+              $prompt     = $fieldGroup.children(selector.prompt)
+            ;
+            $fieldGroup
+              .removeClass(className.error)
+            ;
+            if(settings.inline && $prompt.is(':visible')) {
+              module.verbose('Removing prompt for field', identifier);
+              if(settings.transition  && module.can.useElement('transition') && $module.transition('is supported')) {
+                $prompt.transition(settings.transition + ' out', settings.duration, function() {
+                  $prompt.remove();
+                });
+              }
+              else {
+                $prompt
+                  .fadeOut(settings.duration, function(){
+                    $prompt.remove();
+                  })
+                ;
+              }
+            }
+          }
+        },
+
+        set: {
+          success: function() {
+            $module
+              .removeClass(className.error)
+              .addClass(className.success)
+            ;
+          },
+          defaults: function () {
+            $field.each(function (index, el) {
+              var
+                $el        = $(el),
+                $parent    = $el.parent(),
+                isCheckbox = ($el.filter(selector.checkbox).length > 0),
+                isDropdown = $parent.is(selector.uiDropdown) && module.can.useElement('dropdown'),
+                value      = (isCheckbox)
+                  ? $el.is(':checked')
+                  : $el.val()
+              ;
+              if (isDropdown) {
+                $parent.dropdown('save defaults');
+              }
+              $el.data(metadata.defaultValue, value);
+              $el.data(metadata.isDirty, false);
+            });
+          },
+          error: function() {
+            $module
+              .removeClass(className.success)
+              .addClass(className.error)
+            ;
+          },
+          value: function (field, value) {
+            var
+              fields = {}
+            ;
+            fields[field] = value;
+            return module.set.values.call(element, fields);
+          },
+          values: function (fields) {
+            if($.isEmptyObject(fields)) {
+              return;
+            }
+            $.each(fields, function(key, value) {
+              var
+                $field      = module.get.field(key),
+                $element    = $field.parent(),
+                $calendar   = $field.closest(selector.uiCalendar),
+                isMultiple  = Array.isArray(value),
+                isCheckbox  = $element.is(selector.uiCheckbox)  && module.can.useElement('checkbox'),
+                isDropdown  = $element.is(selector.uiDropdown) && module.can.useElement('dropdown'),
+                isRadio     = ($field.is(selector.radio) && isCheckbox),
+                isCalendar  = ($calendar.length > 0  && module.can.useElement('calendar')),
+                fieldExists = ($field.length > 0),
+                $multipleField
+              ;
+              if(fieldExists) {
+                if(isMultiple && isCheckbox) {
+                  module.verbose('Selecting multiple', value, $field);
+                  $element.checkbox('uncheck');
+                  $.each(value, function(index, value) {
+                    $multipleField = $field.filter('[value="' + value + '"]');
+                    $element       = $multipleField.parent();
+                    if($multipleField.length > 0) {
+                      $element.checkbox('check');
+                    }
+                  });
+                }
+                else if(isRadio) {
+                  module.verbose('Selecting radio value', value, $field);
+                  $field.filter('[value="' + value + '"]')
+                    .parent(selector.uiCheckbox)
+                      .checkbox('check')
+                  ;
+                }
+                else if(isCheckbox) {
+                  module.verbose('Setting checkbox value', value, $element);
+                  if(value === true) {
+                    $element.checkbox('check');
+                  }
+                  else {
+                    $element.checkbox('uncheck');
+                  }
+                }
+                else if(isDropdown) {
+                  module.verbose('Setting dropdown value', value, $element);
+                  $element.dropdown('set selected', value);
+                }
+                else if (isCalendar) {
+                  $calendar.calendar('set date',value);
+                }
+                else {
+                  module.verbose('Setting field value', value, $field);
+                  $field.val(value);
+                }
+              }
+            });
+          },
+          dirty: function() {
+            module.verbose('Setting state dirty');
+            dirty = true;
+            history[0] = history[1];
+            history[1] = 'dirty';
+
+            if (module.is.justClean()) {
+              $module.trigger('dirty');
+            }
+          },
+          clean: function() {
+            module.verbose('Setting state clean');
+            dirty = false;
+            history[0] = history[1];
+            history[1] = 'clean';
+
+            if (module.is.justDirty()) {
+              $module.trigger('clean');
+            }
+          },
+          asClean: function() {
+            module.set.defaults();
+            module.set.clean();
+          },
+          asDirty: function() {
+            module.set.defaults();
+            module.set.dirty();
+          }
+        },
+
+        validate: {
+
+          form: function(event, ignoreCallbacks) {
+            var values = module.get.values();
+
+            // input keydown event will fire submit repeatedly by browser default
+            if(keyHeldDown) {
+              return false;
+            }
+
+            // reset errors
+            formErrors = [];
+            if( module.determine.isValid() ) {
+              module.debug('Form has no validation errors, submitting');
+              module.set.success();
+              if(ignoreCallbacks !== true) {
+                return settings.onSuccess.call(element, event, values);
+              }
+            }
+            else {
+              module.debug('Form has errors');
+              module.set.error();
+              if(!settings.inline) {
+                module.add.errors(formErrors);
+              }
+              // prevent ajax submit
+              if(event && $module.data('moduleApi') !== undefined) {
+                event.stopImmediatePropagation();
+              }
+              if(ignoreCallbacks !== true) {
+                return settings.onFailure.call(element, formErrors, values);
+              }
+            }
+          },
+
+          // takes a validation object and returns whether field passes validation
+          field: function(field, fieldName, showErrors) {
+            showErrors = (showErrors !== undefined)
+              ? showErrors
+              : true
+            ;
+            if(typeof field == 'string') {
+              module.verbose('Validating field', field);
+              fieldName = field;
+              field     = validation[field];
+            }
+            var
+              identifier    = field.identifier || fieldName,
+              $field        = module.get.field(identifier),
+              $dependsField = (field.depends)
+                ? module.get.field(field.depends)
+                : false,
+              fieldValid  = true,
+              fieldErrors = []
+            ;
+            if(!field.identifier) {
+              module.debug('Using field name as identifier', identifier);
+              field.identifier = identifier;
+            }
+            var isDisabled = true;
+            $.each($field, function(){
+                if(!$(this).prop('disabled')) {
+                  isDisabled = false;
+                  return false;
+                }
+            });
+            if(isDisabled) {
+              module.debug('Field is disabled. Skipping', identifier);
+            }
+            else if(field.optional && module.is.blank($field)){
+              module.debug('Field is optional and blank. Skipping', identifier);
+            }
+            else if(field.depends && module.is.empty($dependsField)) {
+              module.debug('Field depends on another value that is not present or empty. Skipping', $dependsField);
+            }
+            else if(field.rules !== undefined) {
+              $field.closest($group).removeClass(className.error);
+              $.each(field.rules, function(index, rule) {
+                if( module.has.field(identifier)) {
+                  var invalidFields = module.validate.rule(field, rule,true) || [];
+                  if (invalidFields.length>0){
+                    module.debug('Field is invalid', identifier, rule.type);
+                    fieldErrors.push(module.get.prompt(rule, field));
+                    fieldValid = false;
+                    if(showErrors){
+                      $(invalidFields).closest($group).addClass(className.error);
+                    }
+                  }
+                }
+              });
+            }
+            if(fieldValid) {
+              if(showErrors) {
+                module.remove.prompt(identifier, fieldErrors);
+                settings.onValid.call($field);
+              }
+            }
+            else {
+              if(showErrors) {
+                formErrors = formErrors.concat(fieldErrors);
+                module.add.prompt(identifier, fieldErrors, true);
+                settings.onInvalid.call($field, fieldErrors);
+              }
+              return false;
+            }
+            return true;
+          },
+
+          // takes validation rule and returns whether field passes rule
+          rule: function(field, rule, internal) {
+            var
+              $field       = module.get.field(field.identifier),
+              ancillary    = module.get.ancillaryValue(rule),
+              ruleName     = module.get.ruleName(rule),
+              ruleFunction = settings.rules[ruleName],
+              invalidFields = [],
+              isCheckbox = $field.is(selector.checkbox),
+              isValid = function(field){
+                var value = (isCheckbox ? $(field).filter(':checked').val() : $(field).val());
+                // cast to string avoiding encoding special values
+                value = (value === undefined || value === '' || value === null)
+                    ? ''
+                    : (settings.shouldTrim) ? $.trim(value + '') : String(value + '')
+                ;
+                return ruleFunction.call(field, value, ancillary, $module);
+              }
+            ;
+            if( !$.isFunction(ruleFunction) ) {
+              module.error(error.noRule, ruleName);
+              return;
+            }
+            if(isCheckbox) {
+              if (!isValid($field)) {
+                invalidFields = $field;
+              }
+            } else {
+              $.each($field, function (index, field) {
+                if (!isValid(field)) {
+                  invalidFields.push(field);
+                }
+              });
+            }
+            return internal ? invalidFields : !(invalidFields.length>0);
+          }
+        },
+
+        setting: function(name, value) {
+          if( $.isPlainObject(name) ) {
+            $.extend(true, settings, name);
+          }
+          else if(value !== undefined) {
+            settings[name] = value;
+          }
+          else {
+            return settings[name];
+          }
+        },
+        internal: function(name, value) {
+          if( $.isPlainObject(name) ) {
+            $.extend(true, module, name);
+          }
+          else if(value !== undefined) {
+            module[name] = value;
+          }
+          else {
+            return module[name];
+          }
+        },
+        debug: function() {
+          if(!settings.silent && settings.debug) {
+            if(settings.performance) {
+              module.performance.log(arguments);
+            }
+            else {
+              module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
+              module.debug.apply(console, arguments);
+            }
+          }
+        },
+        verbose: function() {
+          if(!settings.silent && settings.verbose && settings.debug) {
+            if(settings.performance) {
+              module.performance.log(arguments);
+            }
+            else {
+              module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
+              module.verbose.apply(console, arguments);
+            }
+          }
+        },
+        error: function() {
+          if(!settings.silent) {
+            module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
+            module.error.apply(console, arguments);
+          }
+        },
+        performance: {
+          log: function(message) {
+            var
+              currentTime,
+              executionTime,
+              previousTime
+            ;
+            if(settings.performance) {
+              currentTime   = new Date().getTime();
+              previousTime  = time || currentTime;
+              executionTime = currentTime - previousTime;
+              time          = currentTime;
+              performance.push({
+                'Name'           : message[0],
+                'Arguments'      : [].slice.call(message, 1) || '',
+                'Element'        : element,
+                'Execution Time' : executionTime
+              });
+            }
+            clearTimeout(module.performance.timer);
+            module.performance.timer = setTimeout(module.performance.display, 500);
+          },
+          display: function() {
+            var
+              title = settings.name + ':',
+              totalTime = 0
+            ;
+            time = false;
+            clearTimeout(module.performance.timer);
+            $.each(performance, function(index, data) {
+              totalTime += data['Execution Time'];
+            });
+            title += ' ' + totalTime + 'ms';
+            if(moduleSelector) {
+              title += ' \'' + moduleSelector + '\'';
+            }
+            if($allModules.length > 1) {
+              title += ' ' + '(' + $allModules.length + ')';
+            }
+            if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
+              console.groupCollapsed(title);
+              if(console.table) {
+                console.table(performance);
+              }
+              else {
+                $.each(performance, function(index, data) {
+                  console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
+                });
+              }
+              console.groupEnd();
+            }
+            performance = [];
+          }
+        },
+        invoke: function(query, passedArguments, context) {
+          var
+            object = instance,
+            maxDepth,
+            found,
+            response
+          ;
+          passedArguments = passedArguments || queryArguments;
+          context         = element         || context;
+          if(typeof query == 'string' && object !== undefined) {
+            query    = query.split(/[\. ]/);
+            maxDepth = query.length - 1;
+            $.each(query, function(depth, value) {
+              var camelCaseValue = (depth != maxDepth)
+                ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
+                : query
+              ;
+              if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
+                object = object[camelCaseValue];
+              }
+              else if( object[camelCaseValue] !== undefined ) {
+                found = object[camelCaseValue];
+                return false;
+              }
+              else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
+                object = object[value];
+              }
+              else if( object[value] !== undefined ) {
+                found = object[value];
+                return false;
+              }
+              else {
+                return false;
+              }
+            });
+          }
+          if( $.isFunction( found ) ) {
+            response = found.apply(context, passedArguments);
+          }
+          else if(found !== undefined) {
+            response = found;
+          }
+          if(Array.isArray(returnedValue)) {
+            returnedValue.push(response);
+          }
+          else if(returnedValue !== undefined) {
+            returnedValue = [returnedValue, response];
+          }
+          else if(response !== undefined) {
+            returnedValue = response;
+          }
+          return found;
+        }
+      };
+      module.initialize();
+    })
+  ;
+
+  return (returnedValue !== undefined)
+    ? returnedValue
+    : this
+  ;
+};
+
+$.fn.form.settings = {
+
+  name              : 'Form',
+  namespace         : 'form',
+
+  debug             : false,
+  verbose           : false,
+  performance       : true,
+
+  fields            : false,
+
+  keyboardShortcuts : true,
+  on                : 'submit',
+  inline            : false,
+
+  delay             : 200,
+  revalidate        : true,
+  shouldTrim        : true,
+
+  transition        : 'scale',
+  duration          : 200,
+
+  preventLeaving    : false,
+  dateHandling      : 'date', // 'date', 'input', 'formatter'
+
+  onValid           : function() {},
+  onInvalid         : function() {},
+  onSuccess         : function() { return true; },
+  onFailure         : function() { return false; },
+  onDirty           : function() {},
+  onClean           : function() {},
+
+  metadata : {
+    defaultValue : 'default',
+    validate     : 'validate',
+    isDirty      : 'isDirty'
+  },
+
+  regExp: {
+    htmlID  : /^[a-zA-Z][\w:.-]*$/g,
+    bracket : /\[(.*)\]/i,
+    decimal : /^\d+\.?\d*$/,
+    email   : /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i,
+    escape  : /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|:,=@]/g,
+    flags   : /^\/(.*)\/(.*)?/,
+    integer : /^\-?\d+$/,
+    number  : /^\-?\d*(\.\d+)?$/,
+    url     : /(https?:\/\/(?:www\.|(?!www))[^\s\.]+\.[^\s]{2,}|www\.[^\s]+\.[^\s]{2,})/i
+  },
+
+  text: {
+    unspecifiedRule  : 'Please enter a valid value',
+    unspecifiedField : 'This field',
+    leavingMessage   : 'There are unsaved changes on this page which will be discarded if you continue.'
+  },
+
+  prompt: {
+    empty                : '{name} must have a value',
+    checked              : '{name} must be checked',
+    email                : '{name} must be a valid e-mail',
+    url                  : '{name} must be a valid url',
+    regExp               : '{name} is not formatted correctly',
+    integer              : '{name} must be an integer',
+    decimal              : '{name} must be a decimal number',
+    number               : '{name} must be set to a number',
+    is                   : '{name} must be "{ruleValue}"',
+    isExactly            : '{name} must be exactly "{ruleValue}"',
+    not                  : '{name} cannot be set to "{ruleValue}"',
+    notExactly           : '{name} cannot be set to exactly "{ruleValue}"',
+    contain              : '{name} must contain "{ruleValue}"',
+    containExactly       : '{name} must contain exactly "{ruleValue}"',
+    doesntContain        : '{name} cannot contain  "{ruleValue}"',
+    doesntContainExactly : '{name} cannot contain exactly "{ruleValue}"',
+    minLength            : '{name} must be at least {ruleValue} characters',
+    length               : '{name} must be at least {ruleValue} characters',
+    exactLength          : '{name} must be exactly {ruleValue} characters',
+    maxLength            : '{name} cannot be longer than {ruleValue} characters',
+    match                : '{name} must match {ruleValue} field',
+    different            : '{name} must have a different value than {ruleValue} field',
+    creditCard           : '{name} must be a valid credit card number',
+    minCount             : '{name} must have at least {ruleValue} choices',
+    exactCount           : '{name} must have exactly {ruleValue} choices',
+    maxCount             : '{name} must have {ruleValue} or less choices'
+  },
+
+  selector : {
+    checkbox   : 'input[type="checkbox"], input[type="radio"]',
+    clear      : '.clear',
+    field      : 'input, textarea, select',
+    group      : '.field',
+    input      : 'input',
+    message    : '.error.message',
+    prompt     : '.prompt.label',
+    radio      : 'input[type="radio"]',
+    reset      : '.reset:not([type="reset"])',
+    submit     : '.submit:not([type="submit"])',
+    uiCheckbox : '.ui.checkbox',
+    uiDropdown : '.ui.dropdown',
+    uiCalendar : '.ui.calendar'
+  },
+
+  className : {
+    error   : 'error',
+    label   : 'ui basic red pointing prompt label',
+    pressed : 'down',
+    success : 'success'
+  },
+
+  error: {
+    identifier : 'You must specify a string identifier for each field',
+    method     : 'The method you called is not defined.',
+    noRule     : 'There is no rule matching the one you specified',
+    oldSyntax  : 'Starting in 2.0 forms now only take a single settings object. Validation settings converted to new syntax automatically.',
+    noElement  : 'This module requires ui {element}'
+  },
+
+  templates: {
+
+    // template that produces error message
+    error: function(errors) {
+      var
+        html = '<ul class="list">'
+      ;
+      $.each(errors, function(index, value) {
+        html += '<li>' + value + '</li>';
+      });
+      html += '</ul>';
+      return $(html);
+    },
+
+    // template that produces label
+    prompt: function(errors, labelClasses) {
+      return $('<div/>')
+        .addClass(labelClasses)
+        .html(errors[0])
+      ;
+    }
+  },
+
+  formatter: {
+    date: function(date) {
+      return Intl.DateTimeFormat('en-GB').format(date);
+    },
+    datetime: function(date) {
+      return Intl.DateTimeFormat('en-GB', {
+        year: "numeric",
+        month: "2-digit",
+        day: "2-digit",
+        hour: '2-digit',
+        minute: '2-digit',
+        second: '2-digit'
+      }).format(date);
+    },
+    time: function(date) {
+      return Intl.DateTimeFormat('en-GB', {
+        hour: '2-digit',
+        minute: '2-digit',
+        second: '2-digit'
+      }).format(date);
+    },
+    month: function(date) {
+      return Intl.DateTimeFormat('en-GB', {
+        month: '2-digit',
+        year: 'numeric'
+      }).format(date);
+    },
+    year: function(date) {
+      return Intl.DateTimeFormat('en-GB', {
+        year: 'numeric'
+      }).format(date);
+    }
+  },
+
+  rules: {
+
+    // is not empty or blank string
+    empty: function(value) {
+      return !(value === undefined || '' === value || Array.isArray(value) && value.length === 0);
+    },
+
+    // checkbox checked
+    checked: function() {
+      return ($(this).filter(':checked').length > 0);
+    },
+
+    // is most likely an email
+    email: function(value){
+      return $.fn.form.settings.regExp.email.test(value);
+    },
+
+    // value is most likely url
+    url: function(value) {
+      return $.fn.form.settings.regExp.url.test(value);
+    },
+
+    // matches specified regExp
+    regExp: function(value, regExp) {
+      if(regExp instanceof RegExp) {
+        return value.match(regExp);
+      }
+      var
+        regExpParts = regExp.match($.fn.form.settings.regExp.flags),
+        flags
+      ;
+      // regular expression specified as /baz/gi (flags)
+      if(regExpParts) {
+        regExp = (regExpParts.length >= 2)
+          ? regExpParts[1]
+          : regExp
+        ;
+        flags = (regExpParts.length >= 3)
+          ? regExpParts[2]
+          : ''
+        ;
+      }
+      return value.match( new RegExp(regExp, flags) );
+    },
+
+    // is valid integer or matches range
+    integer: function(value, range) {
+      var
+        intRegExp = $.fn.form.settings.regExp.integer,
+        min,
+        max,
+        parts
+      ;
+      if( !range || ['', '..'].indexOf(range) !== -1) {
+        // do nothing
+      }
+      else if(range.indexOf('..') == -1) {
+        if(intRegExp.test(range)) {
+          min = max = range - 0;
+        }
+      }
+      else {
+        parts = range.split('..', 2);
+        if(intRegExp.test(parts[0])) {
+          min = parts[0] - 0;
+        }
+        if(intRegExp.test(parts[1])) {
+          max = parts[1] - 0;
+        }
+      }
+      return (
+        intRegExp.test(value) &&
+        (min === undefined || value >= min) &&
+        (max === undefined || value <= max)
+      );
+    },
+
+    // is valid number (with decimal)
+    decimal: function(value) {
+      return $.fn.form.settings.regExp.decimal.test(value);
+    },
+
+    // is valid number
+    number: function(value) {
+      return $.fn.form.settings.regExp.number.test(value);
+    },
+
+    // is value (case insensitive)
+    is: function(value, text) {
+      text = (typeof text == 'string')
+        ? text.toLowerCase()
+        : text
+      ;
+      value = (typeof value == 'string')
+        ? value.toLowerCase()
+        : value
+      ;
+      return (value == text);
+    },
+
+    // is value
+    isExactly: function(value, text) {
+      return (value == text);
+    },
+
+    // value is not another value (case insensitive)
+    not: function(value, notValue) {
+      value = (typeof value == 'string')
+        ? value.toLowerCase()
+        : value
+      ;
+      notValue = (typeof notValue == 'string')
+        ? notValue.toLowerCase()
+        : notValue
+      ;
+      return (value != notValue);
+    },
+
+    // value is not another value (case sensitive)
+    notExactly: function(value, notValue) {
+      return (value != notValue);
+    },
+
+    // value contains text (insensitive)
+    contains: function(value, text) {
+      // escape regex characters
+      text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
+      return (value.search( new RegExp(text, 'i') ) !== -1);
+    },
+
+    // value contains text (case sensitive)
+    containsExactly: function(value, text) {
+      // escape regex characters
+      text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
+      return (value.search( new RegExp(text) ) !== -1);
+    },
+
+    // value contains text (insensitive)
+    doesntContain: function(value, text) {
+      // escape regex characters
+      text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
+      return (value.search( new RegExp(text, 'i') ) === -1);
+    },
+
+    // value contains text (case sensitive)
+    doesntContainExactly: function(value, text) {
+      // escape regex characters
+      text = text.replace($.fn.form.settings.regExp.escape, "\\$&");
+      return (value.search( new RegExp(text) ) === -1);
+    },
+
+    // is at least string length
+    minLength: function(value, requiredLength) {
+      return (value !== undefined)
+        ? (value.length >= requiredLength)
+        : false
+      ;
+    },
+
+    // see rls notes for 2.0.6 (this is a duplicate of minLength)
+    length: function(value, requiredLength) {
+      return (value !== undefined)
+        ? (value.length >= requiredLength)
+        : false
+      ;
+    },
+
+    // is exactly length
+    exactLength: function(value, requiredLength) {
+      return (value !== undefined)
+        ? (value.length == requiredLength)
+        : false
+      ;
+    },
+
+    // is less than length
+    maxLength: function(value, maxLength) {
+      return (value !== undefined)
+        ? (value.length <= maxLength)
+        : false
+      ;
+    },
+
+    // matches another field
+    match: function(value, identifier, $module) {
+      var
+        matchingValue,
+        matchingElement
+      ;
+      if((matchingElement = $module.find('[data-validate="'+ identifier +'"]')).length > 0 ) {
+        matchingValue = matchingElement.val();
+      }
+      else if((matchingElement = $module.find('#' + identifier)).length > 0) {
+        matchingValue = matchingElement.val();
+      }
+      else if((matchingElement = $module.find('[name="' + identifier +'"]')).length > 0) {
+        matchingValue = matchingElement.val();
+      }
+      else if((matchingElement = $module.find('[name="' + identifier +'[]"]')).length > 0 ) {
+        matchingValue = matchingElement;
+      }
+      return (matchingValue !== undefined)
+        ? ( value.toString() == matchingValue.toString() )
+        : false
+      ;
+    },
+
+    // different than another field
+    different: function(value, identifier, $module) {
+      // use either id or name of field
+      var
+        matchingValue,
+        matchingElement
+      ;
+      if((matchingElement = $module.find('[data-validate="'+ identifier +'"]')).length > 0 ) {
+        matchingValue = matchingElement.val();
+      }
+      else if((matchingElement = $module.find('#' + identifier)).length > 0) {
+        matchingValue = matchingElement.val();
+      }
+      else if((matchingElement = $module.find('[name="' + identifier +'"]')).length > 0) {
+        matchingValue = matchingElement.val();
+      }
+      else if((matchingElement = $module.find('[name="' + identifier +'[]"]')).length > 0 ) {
+        matchingValue = matchingElement;
+      }
+      return (matchingValue !== undefined)
+        ? ( value.toString() !== matchingValue.toString() )
+        : false
+      ;
+    },
+
+    creditCard: function(cardNumber, cardTypes) {
+      var
+        cards = {
+          visa: {
+            pattern : /^4/,
+            length  : [16]
+          },
+          amex: {
+            pattern : /^3[47]/,
+            length  : [15]
+          },
+          mastercard: {
+            pattern : /^5[1-5]/,
+            length  : [16]
+          },
+          discover: {
+            pattern : /^(6011|622(12[6-9]|1[3-9][0-9]|[2-8][0-9]{2}|9[0-1][0-9]|92[0-5]|64[4-9])|65)/,
+            length  : [16]
+          },
+          unionPay: {
+            pattern : /^(62|88)/,
+            length  : [16, 17, 18, 19]
+          },
+          jcb: {
+            pattern : /^35(2[89]|[3-8][0-9])/,
+            length  : [16]
+          },
+          maestro: {
+            pattern : /^(5018|5020|5038|6304|6759|676[1-3])/,
+            length  : [12, 13, 14, 15, 16, 17, 18, 19]
+          },
+          dinersClub: {
+            pattern : /^(30[0-5]|^36)/,
+            length  : [14]
+          },
+          laser: {
+            pattern : /^(6304|670[69]|6771)/,
+            length  : [16, 17, 18, 19]
+          },
+          visaElectron: {
+            pattern : /^(4026|417500|4508|4844|491(3|7))/,
+            length  : [16]
+          }
+        },
+        valid         = {},
+        validCard     = false,
+        requiredTypes = (typeof cardTypes == 'string')
+          ? cardTypes.split(',')
+          : false,
+        unionPay,
+        validation
+      ;
+
+      if(typeof cardNumber !== 'string' || cardNumber.length === 0) {
+        return;
+      }
+
+      // allow dashes in card
+      cardNumber = cardNumber.replace(/[\-]/g, '');
+
+      // verify card types
+      if(requiredTypes) {
+        $.each(requiredTypes, function(index, type){
+          // verify each card type
+          validation = cards[type];
+          if(validation) {
+            valid = {
+              length  : ($.inArray(cardNumber.length, validation.length) !== -1),
+              pattern : (cardNumber.search(validation.pattern) !== -1)
+            };
+            if(valid.length && valid.pattern) {
+              validCard = true;
+            }
+          }
+        });
+
+        if(!validCard) {
+          return false;
+        }
+      }
+
+      // skip luhn for UnionPay
+      unionPay = {
+        number  : ($.inArray(cardNumber.length, cards.unionPay.length) !== -1),
+        pattern : (cardNumber.search(cards.unionPay.pattern) !== -1)
+      };
+      if(unionPay.number && unionPay.pattern) {
+        return true;
+      }
+
+      // verify luhn, adapted from  <https://gist.github.com/2134376>
+      var
+        length        = cardNumber.length,
+        multiple      = 0,
+        producedValue = [
+          [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
+          [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]
+        ],
+        sum           = 0
+      ;
+      while (length--) {
+        sum += producedValue[multiple][parseInt(cardNumber.charAt(length), 10)];
+        multiple ^= 1;
+      }
+      return (sum % 10 === 0 && sum > 0);
+    },
+
+    minCount: function(value, minCount) {
+      if(minCount == 0) {
+        return true;
+      }
+      if(minCount == 1) {
+        return (value !== '');
+      }
+      return (value.split(',').length >= minCount);
+    },
+
+    exactCount: function(value, exactCount) {
+      if(exactCount == 0) {
+        return (value === '');
+      }
+      if(exactCount == 1) {
+        return (value !== '' && value.search(',') === -1);
+      }
+      return (value.split(',').length == exactCount);
+    },
+
+    maxCount: function(value, maxCount) {
+      if(maxCount == 0) {
+        return false;
+      }
+      if(maxCount == 1) {
+        return (value.search(',') === -1);
+      }
+      return (value.split(',').length <= maxCount);
+    }
+  }
+
+};
+
+})( jQuery, window, document );

Разлика између датотеке није приказан због своје велике величине
+ 8 - 0
website/semantic/dist/components/form.min.css


Неке датотеке нису приказане због велике количине промена