mirror of
https://github.com/Radarr/Radarr.git
synced 2026-04-17 21:26:22 -04:00
Moved source code under src folder - massive change
This commit is contained in:
1
src/UI/.idea/.name
generated
Normal file
1
src/UI/.idea/.name
generated
Normal file
@@ -0,0 +1 @@
|
||||
NzbDrone.UI
|
||||
11
src/UI/.idea/NzbDrone.UI.iml
generated
Normal file
11
src/UI/.idea/NzbDrone.UI.iml
generated
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="jQuery-1.9.1" level="application" />
|
||||
<orderEntry type="library" name="backbone.backgrid.filter.js" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
|
||||
50
src/UI/.idea/codeStyleSettings.xml
generated
Normal file
50
src/UI/.idea/codeStyleSettings.xml
generated
Normal file
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectCodeStyleSettingsManager">
|
||||
<option name="PER_PROJECT_SETTINGS">
|
||||
<value>
|
||||
<option name="LINE_SEPARATOR" value=" " />
|
||||
<option name="RIGHT_MARGIN" value="190" />
|
||||
<option name="HTML_ATTRIBUTE_WRAP" value="0" />
|
||||
<option name="HTML_KEEP_LINE_BREAKS" value="false" />
|
||||
<option name="HTML_KEEP_BLANK_LINES" value="1" />
|
||||
<option name="HTML_ALIGN_ATTRIBUTES" value="false" />
|
||||
<option name="HTML_INLINE_ELEMENTS" value="" />
|
||||
<option name="HTML_DONT_ADD_BREAKS_IF_INLINE_CONTENT" value="" />
|
||||
<CssCodeStyleSettings>
|
||||
<option name="HEX_COLOR_LOWER_CASE" value="true" />
|
||||
<option name="HEX_COLOR_LONG_FORMAT" value="true" />
|
||||
<option name="VALUE_ALIGNMENT" value="1" />
|
||||
</CssCodeStyleSettings>
|
||||
<JSCodeStyleSettings>
|
||||
<option name="ALIGN_OBJECT_PROPERTIES" value="2" />
|
||||
</JSCodeStyleSettings>
|
||||
<XML>
|
||||
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
|
||||
</XML>
|
||||
<codeStyleSettings language="CSS">
|
||||
<indentOptions>
|
||||
<option name="SMART_TABS" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JavaScript">
|
||||
<option name="KEEP_LINE_BREAKS" value="false" />
|
||||
<option name="KEEP_FIRST_COLUMN_COMMENT" value="false" />
|
||||
<option name="ELSE_ON_NEW_LINE" value="true" />
|
||||
<option name="CATCH_ON_NEW_LINE" value="true" />
|
||||
<option name="FINALLY_ON_NEW_LINE" value="true" />
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
|
||||
<option name="SPACE_AFTER_COLON" value="false" />
|
||||
<option name="METHOD_PARAMETERS_WRAP" value="5" />
|
||||
<option name="ARRAY_INITIALIZER_WRAP" value="2" />
|
||||
<option name="IF_BRACE_FORCE" value="3" />
|
||||
<option name="DOWHILE_BRACE_FORCE" value="3" />
|
||||
<option name="WHILE_BRACE_FORCE" value="3" />
|
||||
<option name="FOR_BRACE_FORCE" value="3" />
|
||||
</codeStyleSettings>
|
||||
</value>
|
||||
</option>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</component>
|
||||
</project>
|
||||
|
||||
19
src/UI/.idea/dictionaries/Keivan.xml
generated
Normal file
19
src/UI/.idea/dictionaries/Keivan.xml
generated
Normal file
@@ -0,0 +1,19 @@
|
||||
<component name="ProjectDictionaryState">
|
||||
<dictionary name="Keivan">
|
||||
<words>
|
||||
<w>deps</w>
|
||||
<w>mixins</w>
|
||||
<w>nzbdrone</w>
|
||||
<w>rootdir</w>
|
||||
<w>rootfolder</w>
|
||||
<w>rootfolders</w>
|
||||
<w>signalr</w>
|
||||
<w>templated</w>
|
||||
<w>thetvdb</w>
|
||||
<w>trakt</w>
|
||||
<w>tvdb</w>
|
||||
<w>xlarge</w>
|
||||
<w>yyyy</w>
|
||||
</words>
|
||||
</dictionary>
|
||||
</component>
|
||||
13
src/UI/.idea/dictionaries/Keivan_Beigi.xml
generated
Normal file
13
src/UI/.idea/dictionaries/Keivan_Beigi.xml
generated
Normal file
@@ -0,0 +1,13 @@
|
||||
<component name="ProjectDictionaryState">
|
||||
<dictionary name="Keivan.Beigi">
|
||||
<words>
|
||||
<w>backgrid</w>
|
||||
<w>bnzbd</w>
|
||||
<w>clickable</w>
|
||||
<w>couldn</w>
|
||||
<w>mouseenter</w>
|
||||
<w>mouseleave</w>
|
||||
<w>navbar</w>
|
||||
</words>
|
||||
</dictionary>
|
||||
</component>
|
||||
3
src/UI/.idea/dictionaries/Mark.xml
generated
Normal file
3
src/UI/.idea/dictionaries/Mark.xml
generated
Normal file
@@ -0,0 +1,3 @@
|
||||
<component name="ProjectDictionaryState">
|
||||
<dictionary name="Mark" />
|
||||
</component>
|
||||
5
src/UI/.idea/encodings.xml
generated
Normal file
5
src/UI/.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
|
||||
</project>
|
||||
|
||||
122
src/UI/.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
122
src/UI/.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,122 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0" is_locked="false">
|
||||
<option name="myName" value="Project Default" />
|
||||
<option name="myLocal" value="false" />
|
||||
<inspection_tool class="AssignmentResultUsedJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="AssignmentToForLoopParameterJS" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="AssignmentToFunctionParameterJS" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="BadExpressionStatementJS" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="BreakStatementJS" enabled="true" level="SERVER PROBLEM" enabled_by_default="true" />
|
||||
<inspection_tool class="BreakStatementWithLabelJS" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="ChainedEqualityJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="CheckEmptyScriptTag" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="ConditionalExpressionWithIdenticalBranchesJS" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="ConstantIfStatementJS" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="ConstantOnLHSOfComparisonJS" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="ContinueStatementWithLabelJS" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="CssMissingSemicolonInspection" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="CyclomaticComplexityJS" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="m_limit" value="10" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="DefaultNotLastCaseInSwitchJS" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="DuplicateCaseLabelJS" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="DuplicateConditionJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="DynamicallyGeneratedCodeJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="EmptyTryBlockJS" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="ExceptionCaughtLocallyJS" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="ForLoopReplaceableByWhileJS" enabled="true" level="INFO" enabled_by_default="true">
|
||||
<option name="m_ignoreLoopsWithoutConditions" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="FunctionNamingConventionJS" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="m_regex" value="[a-z][A-Za-z]*" />
|
||||
<option name="m_minLength" value="4" />
|
||||
<option name="m_maxLength" value="32" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="FunctionWithInconsistentReturnsJS" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="HtmlFormInputWithoutLabel" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="HtmlPresentationalElement" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="HtmlUnknownAttribute" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="myValues">
|
||||
<value>
|
||||
<list size="2">
|
||||
<item index="0" class="java.lang.String" itemvalue="name" />
|
||||
<item index="1" class="java.lang.String" itemvalue="validation-name" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
<option name="myCustomValuesEnabled" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="HtmlUnknownTag" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="myValues">
|
||||
<value>
|
||||
<list size="8">
|
||||
<item index="0" class="java.lang.String" itemvalue="nobr" />
|
||||
<item index="1" class="java.lang.String" itemvalue="noembed" />
|
||||
<item index="2" class="java.lang.String" itemvalue="comment" />
|
||||
<item index="3" class="java.lang.String" itemvalue="noscript" />
|
||||
<item index="4" class="java.lang.String" itemvalue="embed" />
|
||||
<item index="5" class="java.lang.String" itemvalue="script" />
|
||||
<item index="6" class="java.lang.String" itemvalue="icon" />
|
||||
<item index="7" class="java.lang.String" itemvalue="p" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
<option name="myCustomValuesEnabled" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="IfStatementWithIdenticalBranchesJS" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="IfStatementWithTooManyBranchesJS" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="m_limit" value="3" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="JSDuplicatedDeclaration" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="JSHint" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="JSLastCommaInArrayLiteral" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="JSPotentiallyInvalidUsageOfThis" enabled="true" level="SERVER PROBLEM" enabled_by_default="true" />
|
||||
<inspection_tool class="JSUndeclaredVariable" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="JSUnnecessarySemicolon" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="JSUnresolvedFunction" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="JSUnresolvedVariable" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="JSUnusedGlobalSymbols" enabled="false" level="INFO" enabled_by_default="false" />
|
||||
<inspection_tool class="LabeledStatementJS" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="LocalVariableNamingConventionJS" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="m_regex" value="[a-z][A-Za-z]*" />
|
||||
<option name="m_minLength" value="1" />
|
||||
<option name="m_maxLength" value="32" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="LossyEncoding" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="NegatedIfStatementJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="NestedAssignmentJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="NestedFunctionCallJS" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="NestedSwitchStatementJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="NestingDepthJS" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="m_limit" value="5" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="NonBlockStatementBodyJS" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="ParameterNamingConventionJS" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="m_regex" value="[a-z][A-Za-z]*" />
|
||||
<option name="m_minLength" value="1" />
|
||||
<option name="m_maxLength" value="32" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ParametersPerFunctionJS" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="m_limit" value="4" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ReservedWordUsedAsNameJS" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="ReuseOfLocalVariableJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="StatementsPerFunctionJS" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="m_limit" value="30" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="SwitchStatementWithNoDefaultBranchJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="TailRecursionJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="TaskInspection" enabled="true" level="INFO" enabled_by_default="true">
|
||||
<option name="suppressedTasks">
|
||||
<set>
|
||||
<option value="LESS" />
|
||||
</set>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ThisExpressionReferencesGlobalObjectJS" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="ThreeNegationsPerFunctionJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="UnterminatedStatementJS" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="ignoreSemicolonAtEndOfBlock" value="true" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
7
src/UI/.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
7
src/UI/.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="PROJECT_PROFILE" value="Project Default" />
|
||||
<option name="USE_PROJECT_PROFILE" value="true" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
||||
3
src/UI/.idea/jsLibraryMappings.xml
generated
Normal file
3
src/UI/.idea/jsLibraryMappings.xml
generated
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4" />
|
||||
|
||||
69
src/UI/.idea/jsLinters/jshint.xml
generated
Normal file
69
src/UI/.idea/jsLinters/jshint.xml
generated
Normal file
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JSHintConfiguration" version="2.1.10" use-config-file="false">
|
||||
<option bitwise="true" />
|
||||
<option camelcase="true" />
|
||||
<option curly="true" />
|
||||
<option eqeqeq="true" />
|
||||
<option forin="true" />
|
||||
<option immed="true" />
|
||||
<option latedef="true" />
|
||||
<option newcap="true" />
|
||||
<option noarg="true" />
|
||||
<option noempty="false" />
|
||||
<option nonew="true" />
|
||||
<option plusplus="false" />
|
||||
<option regexp="false" />
|
||||
<option undef="true" />
|
||||
<option unused="false" />
|
||||
<option strict="true" />
|
||||
<option trailing="false" />
|
||||
<option quotmark="single" />
|
||||
<option maxdepth="3" />
|
||||
<option asi="false" />
|
||||
<option boss="false" />
|
||||
<option debug="false" />
|
||||
<option eqnull="false" />
|
||||
<option es5="false" />
|
||||
<option esnext="false" />
|
||||
<option evil="false" />
|
||||
<option expr="false" />
|
||||
<option funcscope="false" />
|
||||
<option globalstrict="true" />
|
||||
<option iterator="false" />
|
||||
<option lastsemic="false" />
|
||||
<option laxbreak="false" />
|
||||
<option laxcomma="false" />
|
||||
<option loopfunc="false" />
|
||||
<option multistr="false" />
|
||||
<option proto="false" />
|
||||
<option onecase="false" />
|
||||
<option regexdash="false" />
|
||||
<option scripturl="false" />
|
||||
<option smarttabs="false" />
|
||||
<option shadow="false" />
|
||||
<option sub="false" />
|
||||
<option supernew="false" />
|
||||
<option validthis="false" />
|
||||
<option browser="true" />
|
||||
<option couch="false" />
|
||||
<option devel="true" />
|
||||
<option dojo="false" />
|
||||
<option jquery="true" />
|
||||
<option mootools="false" />
|
||||
<option node="false" />
|
||||
<option nonstandard="false" />
|
||||
<option prototypejs="false" />
|
||||
<option rhino="false" />
|
||||
<option worker="false" />
|
||||
<option wsh="false" />
|
||||
<option yui="false" />
|
||||
<option nomen="false" />
|
||||
<option onevar="false" />
|
||||
<option passfail="false" />
|
||||
<option white="false" />
|
||||
<option maxerr="50" />
|
||||
<option predef="window, define, require ,$" />
|
||||
</component>
|
||||
</project>
|
||||
|
||||
13
src/UI/.idea/jsLinters/jslint.xml
generated
Normal file
13
src/UI/.idea/jsLinters/jslint.xml
generated
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JSLintConfiguration" html="true" json="true">
|
||||
<option browser="true" />
|
||||
<option devel="true" />
|
||||
<option indent="4" />
|
||||
<option maxerr="50" />
|
||||
<option plusplus="true" />
|
||||
<option todo="true" />
|
||||
<option white="true" />
|
||||
</component>
|
||||
</project>
|
||||
|
||||
8
src/UI/.idea/misc.xml
generated
Normal file
8
src/UI/.idea/misc.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectKey">
|
||||
<option name="state" value="git@github.com:NzbDrone/NzbDrone.git" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" />
|
||||
</project>
|
||||
|
||||
9
src/UI/.idea/modules.xml
generated
Normal file
9
src/UI/.idea/modules.xml
generated
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/NzbDrone.UI.iml" filepath="$PROJECT_DIR$/.idea/NzbDrone.UI.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
|
||||
23
src/UI/.idea/runConfigurations/Debug___Chrome.xml
generated
Normal file
23
src/UI/.idea/runConfigurations/Debug___Chrome.xml
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Debug - Chrome" type="JavascriptDebugType" factoryName="JavaScript Debug" singleton="true" uri="http://localhost:8989">
|
||||
<mapping url="http://localhost:8989/Calendar" local-file="$PROJECT_DIR$/Calendar" />
|
||||
<mapping url="http://localhost:8989/MainMenuView.js" local-file="$PROJECT_DIR$/MainMenuView.js" />
|
||||
<mapping url="http://localhost:8989/Settings" local-file="$PROJECT_DIR$/Settings" />
|
||||
<mapping url="http://localhost:8989/Upcoming" local-file="$PROJECT_DIR$/Upcoming" />
|
||||
<mapping url="http://localhost:8989/app.js" local-file="$PROJECT_DIR$/app.js" />
|
||||
<mapping url="http://localhost:8989/Mixins" local-file="$PROJECT_DIR$/Mixins" />
|
||||
<mapping url="http://localhost:8989/Missing" local-file="$PROJECT_DIR$/Missing" />
|
||||
<mapping url="http://localhost:8989/Quality" local-file="$PROJECT_DIR$/Quality" />
|
||||
<mapping url="http://localhost:8989/Config.js" local-file="$PROJECT_DIR$/Config.js" />
|
||||
<mapping url="http://localhost:8989/Shared" local-file="$PROJECT_DIR$/Shared" />
|
||||
<mapping url="http://localhost:8989/AddSeries" local-file="$PROJECT_DIR$/AddSeries" />
|
||||
<mapping url="http://localhost:8989/HeaderView.js" local-file="$PROJECT_DIR$/HeaderView.js" />
|
||||
<mapping url="http://localhost:8989" local-file="$PROJECT_DIR$" />
|
||||
<mapping url="http://localhost:8989/Routing.js" local-file="$PROJECT_DIR$/Routing.js" />
|
||||
<mapping url="http://localhost:8989/Controller.js" local-file="$PROJECT_DIR$/Controller.js" />
|
||||
<mapping url="http://localhost:8989/Series" local-file="$PROJECT_DIR$/Series" />
|
||||
<RunnerSettings RunnerId="JavascriptDebugRunner" />
|
||||
<ConfigurationWrapper RunnerId="JavascriptDebugRunner" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
||||
23
src/UI/.idea/runConfigurations/Debug___Firefox.xml
generated
Normal file
23
src/UI/.idea/runConfigurations/Debug___Firefox.xml
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Debug - Firefox" type="JavascriptDebugType" factoryName="JavaScript Debug" singleton="true" engineId="firefox" uri="http://localhost:8989">
|
||||
<mapping url="http://localhost:8989/Calendar" local-file="$PROJECT_DIR$/Calendar" />
|
||||
<mapping url="http://localhost:8989/MainMenuView.js" local-file="$PROJECT_DIR$/MainMenuView.js" />
|
||||
<mapping url="http://localhost:8989/Settings" local-file="$PROJECT_DIR$/Settings" />
|
||||
<mapping url="http://localhost:8989/Upcoming" local-file="$PROJECT_DIR$/Upcoming" />
|
||||
<mapping url="http://localhost:8989/app.js" local-file="$PROJECT_DIR$/app.js" />
|
||||
<mapping url="http://localhost:8989/Mixins" local-file="$PROJECT_DIR$/Mixins" />
|
||||
<mapping url="http://localhost:8989/Missing" local-file="$PROJECT_DIR$/Missing" />
|
||||
<mapping url="http://localhost:8989/Config.js" local-file="$PROJECT_DIR$/Config.js" />
|
||||
<mapping url="http://localhost:8989/Quality" local-file="$PROJECT_DIR$/Quality" />
|
||||
<mapping url="http://localhost:8989/AddSeries" local-file="$PROJECT_DIR$/AddSeries" />
|
||||
<mapping url="http://localhost:8989/Shared" local-file="$PROJECT_DIR$/Shared" />
|
||||
<mapping url="http://localhost:8989/HeaderView.js" local-file="$PROJECT_DIR$/HeaderView.js" />
|
||||
<mapping url="http://localhost:8989" local-file="$PROJECT_DIR$" />
|
||||
<mapping url="http://localhost:8989/Routing.js" local-file="$PROJECT_DIR$/Routing.js" />
|
||||
<mapping url="http://localhost:8989/Controller.js" local-file="$PROJECT_DIR$/Controller.js" />
|
||||
<mapping url="http://localhost:8989/Series" local-file="$PROJECT_DIR$/Series" />
|
||||
<RunnerSettings RunnerId="JavascriptDebugRunner" />
|
||||
<ConfigurationWrapper RunnerId="JavascriptDebugRunner" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
||||
3
src/UI/.idea/scopes/NzbDrone.xml
generated
Normal file
3
src/UI/.idea/scopes/NzbDrone.xml
generated
Normal file
@@ -0,0 +1,3 @@
|
||||
<component name="DependencyValidationManager">
|
||||
<scope name="NzbDrone" pattern="!file:JsLibraries//*" />
|
||||
</component>
|
||||
5
src/UI/.idea/scopes/scope_settings.xml
generated
Normal file
5
src/UI/.idea/scopes/scope_settings.xml
generated
Normal file
@@ -0,0 +1,5 @@
|
||||
<component name="DependencyValidationManager">
|
||||
<state>
|
||||
<option name="SKIP_IMPORT_STATEMENTS" value="false" />
|
||||
</state>
|
||||
</component>
|
||||
7
src/UI/.idea/vcs.xml
generated
Normal file
7
src/UI/.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
|
||||
26
src/UI/AddSeries/AddSeriesCollection.js
Normal file
26
src/UI/AddSeries/AddSeriesCollection.js
Normal file
@@ -0,0 +1,26 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'backbone',
|
||||
'Series/SeriesModel'
|
||||
], function (Backbone, SeriesModel) {
|
||||
return Backbone.Collection.extend({
|
||||
url : window.NzbDrone.ApiRoot + '/series/lookup',
|
||||
model: SeriesModel,
|
||||
|
||||
parse: function (response) {
|
||||
|
||||
var self = this;
|
||||
|
||||
_.each(response, function (model) {
|
||||
model.id = undefined;
|
||||
|
||||
if (self.unmappedFolderModel) {
|
||||
model.path = self.unmappedFolderModel.get('folder').path;
|
||||
}
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
});
|
||||
});
|
||||
62
src/UI/AddSeries/AddSeriesLayout.js
Normal file
62
src/UI/AddSeries/AddSeriesLayout.js
Normal file
@@ -0,0 +1,62 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'app',
|
||||
'marionette',
|
||||
'AddSeries/RootFolders/Layout',
|
||||
'AddSeries/Existing/AddExistingSeriesCollectionView',
|
||||
'AddSeries/AddSeriesView',
|
||||
'Quality/QualityProfileCollection',
|
||||
'AddSeries/RootFolders/Collection',
|
||||
'Series/SeriesCollection'
|
||||
], function (App,
|
||||
Marionette,
|
||||
RootFolderLayout,
|
||||
ExistingSeriesCollectionView,
|
||||
AddSeriesView,
|
||||
QualityProfileCollection,
|
||||
RootFolderCollection) {
|
||||
|
||||
return Marionette.Layout.extend({
|
||||
template: 'AddSeries/AddSeriesLayoutTemplate',
|
||||
|
||||
regions: {
|
||||
workspace: '#add-series-workspace'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click .x-import': '_importSeries',
|
||||
'click .x-add-new': '_addSeries'
|
||||
},
|
||||
|
||||
attributes: {
|
||||
id: 'add-series-screen'
|
||||
},
|
||||
|
||||
initialize: function () {
|
||||
QualityProfileCollection.fetch();
|
||||
RootFolderCollection.promise = RootFolderCollection.fetch();
|
||||
},
|
||||
|
||||
onShow: function () {
|
||||
this.workspace.show(new AddSeriesView());
|
||||
},
|
||||
|
||||
_folderSelected: function (options) {
|
||||
App.vent.trigger(App.Commands.CloseModalCommand);
|
||||
|
||||
this.workspace.show(new ExistingSeriesCollectionView({model: options.model}));
|
||||
},
|
||||
|
||||
_importSeries: function () {
|
||||
this.rootFolderLayout = new RootFolderLayout();
|
||||
this.rootFolderLayout.on('folderSelected', this._folderSelected, this);
|
||||
App.modalRegion.show(this.rootFolderLayout);
|
||||
},
|
||||
|
||||
_addSeries: function () {
|
||||
this.workspace.show(new AddSeriesView());
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
22
src/UI/AddSeries/AddSeriesLayoutTemplate.html
Normal file
22
src/UI/AddSeries/AddSeriesLayoutTemplate.html
Normal file
@@ -0,0 +1,22 @@
|
||||
<div class="row operations-row">
|
||||
<div class="btn-group btn-block">
|
||||
<div class="btn btn-large add-series-import-btn x-import">
|
||||
<i class="icon-hdd"/>
|
||||
Import existing series on disk
|
||||
</div>
|
||||
<button class="btn btn-large btn-icon-only dropdown-toggle" data-toggle="dropdown">
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li class="add-new x-add-new">
|
||||
Add new series
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!--<div class="btn btn-block btn-large add-series-import-btn x-import">-->
|
||||
<!--<i class="icon-hdd"/>-->
|
||||
<!--Import existing series on disk-->
|
||||
<!--</div>-->
|
||||
</div>
|
||||
<div id="add-series-workspace"/>
|
||||
139
src/UI/AddSeries/AddSeriesView.js
Normal file
139
src/UI/AddSeries/AddSeriesView.js
Normal file
@@ -0,0 +1,139 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'app',
|
||||
'marionette',
|
||||
'AddSeries/AddSeriesCollection',
|
||||
'AddSeries/SearchResultCollectionView',
|
||||
'AddSeries/NotFoundView',
|
||||
'Shared/LoadingView',
|
||||
'underscore'
|
||||
], function (App, Marionette, AddSeriesCollection, SearchResultCollectionView, NotFoundView, LoadingView, _) {
|
||||
return Marionette.Layout.extend({
|
||||
template: 'AddSeries/AddSeriesViewTemplate',
|
||||
|
||||
regions: {
|
||||
searchResult: '#search-result'
|
||||
},
|
||||
|
||||
ui: {
|
||||
seriesSearch: '.x-series-search',
|
||||
searchBar : '.x-search-bar',
|
||||
loadMore : '.x-load-more'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click .x-load-more': '_onLoadMore'
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
this.isExisting = options.isExisting;
|
||||
this.collection = new AddSeriesCollection();
|
||||
|
||||
if (this.isExisting) {
|
||||
this.collection.unmappedFolderModel = this.model;
|
||||
}
|
||||
|
||||
if (this.isExisting) {
|
||||
this.className = 'existing-series';
|
||||
}
|
||||
else {
|
||||
this.className = 'new-series';
|
||||
}
|
||||
|
||||
this.listenTo(App.vent, App.Events.SeriesAdded, this._onSeriesAdded);
|
||||
this.listenTo(this.collection, 'sync', this._showResults);
|
||||
|
||||
this.resultCollectionView = new SearchResultCollectionView({
|
||||
collection: this.collection,
|
||||
isExisting: this.isExisting
|
||||
});
|
||||
|
||||
this.throttledSearch = _.debounce(this.search, 1000, {trailing: true}).bind(this);
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
var self = this;
|
||||
|
||||
this.$el.addClass(this.className);
|
||||
|
||||
this.ui.seriesSearch.keyup(function () {
|
||||
self.searchResult.close();
|
||||
self._abortExistingSearch();
|
||||
self.throttledSearch({
|
||||
term: self.ui.seriesSearch.val()
|
||||
});
|
||||
});
|
||||
|
||||
if (this.isExisting) {
|
||||
this.ui.searchBar.hide();
|
||||
}
|
||||
},
|
||||
|
||||
onShow: function () {
|
||||
this.searchResult.show(this.resultCollectionView);
|
||||
this.ui.seriesSearch.focus();
|
||||
},
|
||||
|
||||
search: function (options) {
|
||||
|
||||
this.collection.reset();
|
||||
|
||||
if (!options.term || options.term === this.collection.term) {
|
||||
return $.Deferred().resolve();
|
||||
}
|
||||
|
||||
this.searchResult.show(new LoadingView());
|
||||
this.collection.term = options.term;
|
||||
this.currentSearchPromise = this.collection.fetch({
|
||||
data: { term: options.term }
|
||||
});
|
||||
|
||||
return this.currentSearchPromise;
|
||||
},
|
||||
|
||||
_onSeriesAdded: function (options) {
|
||||
if (this.isExisting && options.series.get('path') === this.model.get('folder').path) {
|
||||
this.close();
|
||||
}
|
||||
|
||||
else if (!this.isExisting) {
|
||||
this.collection.reset();
|
||||
this.searchResult.close();
|
||||
this.ui.seriesSearch.val('');
|
||||
this.ui.seriesSearch.focus();
|
||||
}
|
||||
},
|
||||
|
||||
_onLoadMore: function () {
|
||||
var showingAll = this.resultCollectionView.showMore();
|
||||
this.ui.searchBar.show();
|
||||
|
||||
if (showingAll) {
|
||||
this.ui.loadMore.hide();
|
||||
}
|
||||
},
|
||||
|
||||
_showResults: function () {
|
||||
if (!this.isClosed) {
|
||||
|
||||
if (this.collection.length === 0) {
|
||||
this.searchResult.show(new NotFoundView({term: this.collection.term}));
|
||||
}
|
||||
else {
|
||||
this.searchResult.show(this.resultCollectionView);
|
||||
if (!this.showingAll && this.isExisting) {
|
||||
this.ui.loadMore.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_abortExistingSearch: function () {
|
||||
if (this.currentSearchPromise && this.currentSearchPromise.readyState > 0 && this.currentSearchPromise.readyState < 4) {
|
||||
console.log('aborting previous pending search request.');
|
||||
this.currentSearchPromise.abort();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
23
src/UI/AddSeries/AddSeriesViewTemplate.html
Normal file
23
src/UI/AddSeries/AddSeriesViewTemplate.html
Normal file
@@ -0,0 +1,23 @@
|
||||
{{#if folder.path}}
|
||||
<div class="row unmapped-folder-path">
|
||||
<div class="span11">
|
||||
{{folder.path}}
|
||||
</div>
|
||||
</div>{{/if}}
|
||||
<div class="row x-search-bar">
|
||||
<div class="input-prepend nz-input-large add-series-search span11">
|
||||
<i class="add-on icon-search"/>
|
||||
{{#if folder}}
|
||||
<input type="text" class="input-block-level x-series-search" value="{{folder.name}}">
|
||||
{{else}}
|
||||
<input type="text" class="input-block-level x-series-search" placeholder="Start typing the name of series you want to add ...">
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div id="search-result" class="result-list span12"/>
|
||||
</div>
|
||||
<div class="btn btn-block text-center new-series-loadmore x-load-more" style="display: none;">
|
||||
<i class="icon-angle-down"/>
|
||||
more
|
||||
</div>{{debug}}
|
||||
45
src/UI/AddSeries/Existing/AddExistingSeriesCollectionView.js
Normal file
45
src/UI/AddSeries/Existing/AddExistingSeriesCollectionView.js
Normal file
@@ -0,0 +1,45 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'marionette',
|
||||
'AddSeries/AddSeriesView',
|
||||
'AddSeries/Existing/UnmappedFolderCollection'
|
||||
], function (Marionette, AddSeriesView, UnmappedFolderCollection) {
|
||||
|
||||
return Marionette.CollectionView.extend({
|
||||
|
||||
itemView: AddSeriesView,
|
||||
|
||||
initialize: function () {
|
||||
this.collection = new UnmappedFolderCollection();
|
||||
this.collection.importItems(this.model);
|
||||
},
|
||||
|
||||
showCollection: function () {
|
||||
this._showAndSearch(0);
|
||||
},
|
||||
|
||||
_showAndSearch: function (index) {
|
||||
var self = this;
|
||||
var model = this.collection.at(index);
|
||||
|
||||
if (model) {
|
||||
var currentIndex = index;
|
||||
var folderName = model.get('folder').name;
|
||||
this.addItemView(model, this.getItemView(), index);
|
||||
this.children.findByModel(model)
|
||||
.search({term: folderName})
|
||||
.always(function () {
|
||||
if (!self.isClosed) {
|
||||
self._showAndSearch(currentIndex + 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
itemViewOptions: {
|
||||
isExisting: true
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
23
src/UI/AddSeries/Existing/UnmappedFolderCollection.js
Normal file
23
src/UI/AddSeries/Existing/UnmappedFolderCollection.js
Normal file
@@ -0,0 +1,23 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'backbone',
|
||||
'AddSeries/Existing/UnmappedFolderModel'
|
||||
], function (Backbone, UnmappedFolderModel) {
|
||||
return Backbone.Collection.extend({
|
||||
model: UnmappedFolderModel,
|
||||
|
||||
importItems: function (rootFolderModel) {
|
||||
|
||||
this.reset();
|
||||
var rootFolder = rootFolderModel;
|
||||
|
||||
_.each(rootFolderModel.get('unmappedFolders'), function (folder) {
|
||||
this.push(new UnmappedFolderModel({
|
||||
rootFolder: rootFolder,
|
||||
folder : folder
|
||||
}));
|
||||
}, this);
|
||||
}
|
||||
});
|
||||
});
|
||||
10
src/UI/AddSeries/Existing/UnmappedFolderModel.js
Normal file
10
src/UI/AddSeries/Existing/UnmappedFolderModel.js
Normal file
@@ -0,0 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
define(
|
||||
[
|
||||
'backbone'
|
||||
], function (Backbone) {
|
||||
return Backbone.Model.extend({
|
||||
|
||||
});
|
||||
});
|
||||
8
src/UI/AddSeries/NotFoundTemplate.html
Normal file
8
src/UI/AddSeries/NotFoundTemplate.html
Normal file
@@ -0,0 +1,8 @@
|
||||
<div class="text-center span12">
|
||||
<h3>
|
||||
Sorry. We couldn't find any series matching '{{term}}'
|
||||
</h3>
|
||||
<a href="https://github.com/NzbDrone/NzbDrone/wiki/FAQ#why-cant-i-add-a-new-show-to-nzbdrone-its-on-thetvdb">Why can't I find my show?</a>
|
||||
|
||||
</div>
|
||||
{{debug}}
|
||||
20
src/UI/AddSeries/NotFoundView.js
Normal file
20
src/UI/AddSeries/NotFoundView.js
Normal file
@@ -0,0 +1,20 @@
|
||||
'use strict';
|
||||
|
||||
define(
|
||||
[
|
||||
'marionette'
|
||||
], function (Marionette) {
|
||||
|
||||
return Marionette.CompositeView.extend({
|
||||
template: 'AddSeries/NotFoundTemplate',
|
||||
|
||||
initialize: function (options) {
|
||||
this.options = options;
|
||||
},
|
||||
|
||||
templateHelpers: function () {
|
||||
return this.options;
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
17
src/UI/AddSeries/RootFolders/Collection.js
Normal file
17
src/UI/AddSeries/RootFolders/Collection.js
Normal file
@@ -0,0 +1,17 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'backbone',
|
||||
'AddSeries/RootFolders/Model',
|
||||
'Mixins/backbone.signalr.mixin'
|
||||
], function (Backbone, RootFolderModel) {
|
||||
|
||||
var RootFolderCollection = Backbone.Collection.extend({
|
||||
url : window.NzbDrone.ApiRoot + '/rootfolder',
|
||||
model: RootFolderModel
|
||||
});
|
||||
|
||||
//var collection = new RootFolderCollection().bindSignalR();
|
||||
|
||||
return new RootFolderCollection();
|
||||
});
|
||||
16
src/UI/AddSeries/RootFolders/CollectionView.js
Normal file
16
src/UI/AddSeries/RootFolders/CollectionView.js
Normal file
@@ -0,0 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
define(
|
||||
[
|
||||
'marionette',
|
||||
'AddSeries/RootFolders/ItemView'
|
||||
], function (Marionette, RootFolderItemView) {
|
||||
|
||||
|
||||
return Marionette.CollectionView.extend({
|
||||
itemView: RootFolderItemView,
|
||||
|
||||
tagName : 'table',
|
||||
className: 'table table-hover'
|
||||
});
|
||||
});
|
||||
37
src/UI/AddSeries/RootFolders/ItemView.js
Normal file
37
src/UI/AddSeries/RootFolders/ItemView.js
Normal file
@@ -0,0 +1,37 @@
|
||||
'use strict';
|
||||
|
||||
define(
|
||||
[
|
||||
'marionette'
|
||||
], function (Marionette) {
|
||||
|
||||
return Marionette.ItemView.extend({
|
||||
|
||||
template: 'AddSeries/RootFolders/ItemViewTemplate',
|
||||
tagName : 'tr',
|
||||
|
||||
initialize: function () {
|
||||
this.listenTo(this.model, 'change', this.render);
|
||||
},
|
||||
|
||||
|
||||
events: {
|
||||
'click .x-delete': 'removeFolder',
|
||||
'click .x-folder': 'folderSelected'
|
||||
},
|
||||
|
||||
removeFolder: function () {
|
||||
|
||||
var self = this;
|
||||
|
||||
this.model.destroy()
|
||||
.success(function(){
|
||||
self.close();
|
||||
});
|
||||
},
|
||||
|
||||
folderSelected: function () {
|
||||
this.trigger('folderSelected', this.model);
|
||||
}
|
||||
});
|
||||
});
|
||||
10
src/UI/AddSeries/RootFolders/ItemViewTemplate.html
Normal file
10
src/UI/AddSeries/RootFolders/ItemViewTemplate.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<td class="span10 x-folder folder-path">
|
||||
{{path}}
|
||||
</td>
|
||||
<td class="span3 x-folder folder-free-space">
|
||||
<span>{{Bytes freeSpace}}</span>
|
||||
</td>
|
||||
<td class="span1 nz-row-action">
|
||||
<div class="btn btn-small btn-icon-only icon-nd-delete x-delete">
|
||||
</div>
|
||||
</td>
|
||||
71
src/UI/AddSeries/RootFolders/Layout.js
Normal file
71
src/UI/AddSeries/RootFolders/Layout.js
Normal file
@@ -0,0 +1,71 @@
|
||||
'use strict';
|
||||
|
||||
define(
|
||||
[
|
||||
'marionette',
|
||||
'AddSeries/RootFolders/CollectionView',
|
||||
'AddSeries/RootFolders/Collection',
|
||||
'AddSeries/RootFolders/Model',
|
||||
'Shared/LoadingView',
|
||||
'Mixins/AsValidatedView',
|
||||
'Mixins/AutoComplete'
|
||||
], function (Marionette, RootFolderCollectionView, RootFolderCollection, RootFolderModel, LoadingView, AsValidatedView) {
|
||||
|
||||
var layout = Marionette.Layout.extend({
|
||||
template: 'AddSeries/RootFolders/LayoutTemplate',
|
||||
|
||||
ui: {
|
||||
pathInput: '.x-path input'
|
||||
},
|
||||
|
||||
regions: {
|
||||
currentDirs: '#current-dirs'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click .x-add': '_addFolder'
|
||||
},
|
||||
|
||||
initialize: function () {
|
||||
this.collection = RootFolderCollection;
|
||||
this.rootfolderListView = new RootFolderCollectionView({ collection: RootFolderCollection });
|
||||
|
||||
this.listenTo(this.rootfolderListView, 'itemview:folderSelected', this._onFolderSelected);
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
var self = this;
|
||||
this.currentDirs.show(new LoadingView());
|
||||
|
||||
RootFolderCollection.promise.done(function () {
|
||||
self.currentDirs.show(self.rootfolderListView);
|
||||
});
|
||||
|
||||
this.ui.pathInput.autoComplete('/directories');
|
||||
},
|
||||
|
||||
_onFolderSelected: function (options) {
|
||||
this.trigger('folderSelected', options);
|
||||
},
|
||||
|
||||
_addFolder: function () {
|
||||
|
||||
var self = this;
|
||||
|
||||
var newDir = new RootFolderModel({
|
||||
Path: this.ui.pathInput.val()
|
||||
});
|
||||
|
||||
this.bindToModelValidation(newDir);
|
||||
|
||||
newDir.save().done(function () {
|
||||
RootFolderCollection.add(newDir);
|
||||
self.trigger('folderSelected', {model: newDir});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return AsValidatedView.apply(layout);
|
||||
|
||||
});
|
||||
21
src/UI/AddSeries/RootFolders/LayoutTemplate.html
Normal file
21
src/UI/AddSeries/RootFolders/LayoutTemplate.html
Normal file
@@ -0,0 +1,21 @@
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>Select Folder</h3>
|
||||
</div>
|
||||
<div class="modal-body root-folders-modal">
|
||||
<div class="validation-errors"></div>
|
||||
<div class="input-prepend input-append x-path control-group">
|
||||
<span class="add-on"> <i class="icon-folder-open"></i></span>
|
||||
<input class="span9" type="text" validation-name="path" placeholder="Enter path to folder that contains your shows">
|
||||
<button class="btn btn-success x-add">
|
||||
<i class="icon-ok"/>
|
||||
</button>
|
||||
</div>
|
||||
{{#if items}}
|
||||
<h4>Recent Folders</h4>
|
||||
{{/if}}
|
||||
<div id="current-dirs" class="root-folders-list"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn" data-dismiss="modal">close</button>
|
||||
</div>
|
||||
12
src/UI/AddSeries/RootFolders/Model.js
Normal file
12
src/UI/AddSeries/RootFolders/Model.js
Normal file
@@ -0,0 +1,12 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'backbone'
|
||||
], function (Backbone) {
|
||||
return Backbone.Model.extend({
|
||||
urlRoot : window.NzbDrone.ApiRoot + '/rootfolder',
|
||||
defaults: {
|
||||
freeSpace: 0
|
||||
}
|
||||
});
|
||||
});
|
||||
12
src/UI/AddSeries/RootFolders/RootFolderSelectionPartial.html
Normal file
12
src/UI/AddSeries/RootFolders/RootFolderSelectionPartial.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<div>
|
||||
<select class="span4 x-root-folder" validation-name="RootFolderPath">
|
||||
{{#if this}}
|
||||
{{#each this}}
|
||||
<option value="{{id}}">{{path}}</option>
|
||||
{{/each}}
|
||||
{{else}}
|
||||
<option value="">Select Path</option>
|
||||
{{/if}}
|
||||
<option value="addNew">Add a different path</option>
|
||||
</select>
|
||||
</div>
|
||||
@@ -0,0 +1,9 @@
|
||||
<select class="span2 x-starting-season">
|
||||
{{#each this}}
|
||||
{{#if_eq seasonNumber compare="0"}}
|
||||
<option value="{{seasonNumber}}">Specials</option>
|
||||
{{else}}
|
||||
<option value="{{seasonNumber}}">Season {{seasonNumber}}</option>
|
||||
{{/if_eq}}
|
||||
{{/each}}
|
||||
</select>
|
||||
35
src/UI/AddSeries/SearchResultCollectionView.js
Normal file
35
src/UI/AddSeries/SearchResultCollectionView.js
Normal file
@@ -0,0 +1,35 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'marionette',
|
||||
'AddSeries/SearchResultView'
|
||||
], function (Marionette, SearchResultView) {
|
||||
|
||||
return Marionette.CollectionView.extend({
|
||||
|
||||
itemView: SearchResultView,
|
||||
|
||||
initialize: function (options) {
|
||||
this.isExisting = options.isExisting;
|
||||
this.showing = 1;
|
||||
},
|
||||
|
||||
showAll: function () {
|
||||
this.showingAll = true;
|
||||
this.render();
|
||||
},
|
||||
|
||||
showMore: function () {
|
||||
this.showing += 5;
|
||||
this.render();
|
||||
|
||||
return this.showing >= this.collection.length;
|
||||
},
|
||||
|
||||
appendHtml: function (collectionView, itemView, index) {
|
||||
if (!this.isExisting || index < this.showing || index === 0) {
|
||||
collectionView.$el.append(itemView.el);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
161
src/UI/AddSeries/SearchResultView.js
Normal file
161
src/UI/AddSeries/SearchResultView.js
Normal file
@@ -0,0 +1,161 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'app',
|
||||
'underscore',
|
||||
'marionette',
|
||||
'Quality/QualityProfileCollection',
|
||||
'AddSeries/RootFolders/Collection',
|
||||
'AddSeries/RootFolders/Layout',
|
||||
'Series/SeriesCollection',
|
||||
'Config',
|
||||
'Shared/Messenger',
|
||||
'Mixins/AsValidatedView',
|
||||
'jquery.dotdotdot'
|
||||
], function (App, _, Marionette, QualityProfiles, RootFolders, RootFolderLayout, SeriesCollection, Config, Messenger, AsValidatedView) {
|
||||
|
||||
var view = Marionette.ItemView.extend({
|
||||
|
||||
template: 'AddSeries/SearchResultViewTemplate',
|
||||
|
||||
ui: {
|
||||
qualityProfile: '.x-quality-profile',
|
||||
rootFolder : '.x-root-folder',
|
||||
addButton : '.x-add',
|
||||
overview : '.x-overview',
|
||||
startingSeason: '.x-starting-season'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click .x-add' : '_addSeries',
|
||||
'change .x-quality-profile': '_qualityProfileChanged',
|
||||
'change .x-root-folder' : '_rootFolderChanged'
|
||||
},
|
||||
|
||||
initialize: function () {
|
||||
|
||||
if (!this.model) {
|
||||
throw 'model is required';
|
||||
}
|
||||
|
||||
this.templateHelpers = {};
|
||||
this._configureTemplateHelpers();
|
||||
|
||||
this.listenTo(App.vent, Config.Events.ConfigUpdatedEvent, this._onConfigUpdated);
|
||||
this.listenTo(this.model, 'change', this.render);
|
||||
this.listenTo(RootFolders, 'all', this.render);
|
||||
|
||||
this.rootFolderLayout = new RootFolderLayout();
|
||||
this.listenTo(this.rootFolderLayout, 'folderSelected', this._setRootFolder);
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
|
||||
var defaultQuality = Config.getValue(Config.Keys.DefaultQualityProfileId);
|
||||
var defaultRoot = Config.getValue(Config.Keys.DefaultRootFolderId);
|
||||
|
||||
if (QualityProfiles.get(defaultQuality)) {
|
||||
this.ui.qualityProfile.val(defaultQuality);
|
||||
}
|
||||
|
||||
if (RootFolders.get(defaultRoot)) {
|
||||
this.ui.rootFolder.val(defaultRoot);
|
||||
}
|
||||
|
||||
var minSeasonNotZero = _.min(_.reject(this.model.get('seasons'), { seasonNumber: 0 }), 'seasonNumber');
|
||||
|
||||
if (minSeasonNotZero) {
|
||||
this.ui.startingSeason.val(minSeasonNotZero.seasonNumber);
|
||||
}
|
||||
|
||||
//TODO: make this work via onRender, FM?
|
||||
//works with onShow, but stops working after the first render
|
||||
this.ui.overview.dotdotdot({
|
||||
height: 120
|
||||
});
|
||||
},
|
||||
|
||||
_configureTemplateHelpers: function () {
|
||||
var existingSeries = SeriesCollection.where({tvdbId: this.model.get('tvdbId')});
|
||||
|
||||
if (existingSeries.length > 0) {
|
||||
this.templateHelpers.existing = existingSeries[0].toJSON();
|
||||
}
|
||||
|
||||
this.templateHelpers.qualityProfiles = QualityProfiles.toJSON();
|
||||
|
||||
if (!this.model.get('isExisting')) {
|
||||
this.templateHelpers.rootFolders = RootFolders.toJSON();
|
||||
}
|
||||
},
|
||||
|
||||
_onConfigUpdated: function (options) {
|
||||
if (options.key === Config.Keys.DefaultQualityProfileId) {
|
||||
this.ui.qualityProfile.val(options.value);
|
||||
}
|
||||
else if (options.key === Config.Keys.DefaultRootFolderId) {
|
||||
this.ui.rootFolder.val(options.value);
|
||||
}
|
||||
},
|
||||
|
||||
_qualityProfileChanged: function () {
|
||||
Config.setValue(Config.Keys.DefaultQualityProfileId, this.ui.qualityProfile.val());
|
||||
},
|
||||
|
||||
_rootFolderChanged: function () {
|
||||
var rootFolderValue = this.ui.rootFolder.val();
|
||||
if (rootFolderValue === 'addNew') {
|
||||
App.modalRegion.show(this.rootFolderLayout);
|
||||
}
|
||||
else {
|
||||
Config.setValue(Config.Keys.DefaultRootFolderId, rootFolderValue);
|
||||
}
|
||||
},
|
||||
|
||||
_setRootFolder: function (options) {
|
||||
App.vent.trigger(App.Commands.CloseModalCommand);
|
||||
this.ui.rootFolder.val(options.model.id);
|
||||
this._rootFolderChanged();
|
||||
},
|
||||
|
||||
_addSeries: function () {
|
||||
var icon = this.ui.addButton.find('icon');
|
||||
icon.removeClass('icon-plus').addClass('icon-spin icon-spinner disabled');
|
||||
|
||||
var quality = this.ui.qualityProfile.val();
|
||||
var rootFolderPath = this.ui.rootFolder.children(':selected').text();
|
||||
var startingSeason = this.ui.startingSeason.val();
|
||||
|
||||
this.model.set('qualityProfileId', quality);
|
||||
this.model.set('rootFolderPath', rootFolderPath);
|
||||
this.model.setSeasonPass(startingSeason);
|
||||
|
||||
var self = this;
|
||||
|
||||
SeriesCollection.add(this.model);
|
||||
|
||||
|
||||
var promise = this.model.save();
|
||||
|
||||
promise.done(function () {
|
||||
self.close();
|
||||
icon.removeClass('icon-spin icon-spinner disabled').addClass('icon-search');
|
||||
|
||||
Messenger.show({
|
||||
message: 'Added: ' + self.model.get('title')
|
||||
});
|
||||
|
||||
App.vent.trigger(App.Events.SeriesAdded, { series: self.model });
|
||||
});
|
||||
|
||||
promise.fail(function () {
|
||||
icon.removeClass('icon-spin icon-spinner disabled').addClass('icon-search');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
AsValidatedView.apply(view);
|
||||
|
||||
return view;
|
||||
});
|
||||
38
src/UI/AddSeries/SearchResultViewTemplate.html
Normal file
38
src/UI/AddSeries/SearchResultViewTemplate.html
Normal file
@@ -0,0 +1,38 @@
|
||||
<div class="search-item {{#unless isExisting}}search-item-new{{/unless}}">
|
||||
<div class="row">
|
||||
<div class="span2">
|
||||
<a href="{{traktUrl}}" target="_blank">
|
||||
<img class="new-series-poster" src="{{remotePoster}}"
|
||||
{{defaultImg}} >
|
||||
</a>
|
||||
</div>
|
||||
<div class="span9">
|
||||
|
||||
<div class="row">
|
||||
<h2>{{titleWithYear}}</h2>
|
||||
</div>
|
||||
<div class="row new-series-overview x-overview">
|
||||
{{overview}}
|
||||
</div>
|
||||
<div class="row">
|
||||
<form class="form-inline">
|
||||
{{#if existing}}
|
||||
<div class="btn add-series disabled pull-right">
|
||||
Already Exists
|
||||
</div>
|
||||
{{else}}
|
||||
{{#unless path}}
|
||||
{{> RootFolderSelectionPartial rootFolders}}
|
||||
{{/unless}}
|
||||
|
||||
{{> StartingSeasonSelectionPartial seasons}}
|
||||
{{> QualityProfileSelectionPartial qualityProfiles}}
|
||||
<span class="btn btn-success x-add add-series pull-right"> Add
|
||||
<i class="icon-plus"></i>
|
||||
</span>
|
||||
{{/if}}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
125
src/UI/AddSeries/addSeries.less
Normal file
125
src/UI/AddSeries/addSeries.less
Normal file
@@ -0,0 +1,125 @@
|
||||
@import "../Shared/Styles/card.less";
|
||||
@import "../Shared/Styles/clickable.less";
|
||||
|
||||
#add-series-screen {
|
||||
|
||||
.operations-row {
|
||||
margin-left : 0px;
|
||||
}
|
||||
|
||||
.existing-series {
|
||||
|
||||
.card();
|
||||
margin : 30px 0px;
|
||||
|
||||
.add-series-search {
|
||||
width : 970px;
|
||||
}
|
||||
|
||||
.unmapped-folder-path {
|
||||
padding: 20px;
|
||||
margin-left : 0px;
|
||||
font-weight : 100;
|
||||
font-size : 25px;
|
||||
text-align : center;
|
||||
}
|
||||
|
||||
.new-series-loadmore {
|
||||
font-size : 30px;
|
||||
font-weight : 300;
|
||||
padding-top : 10px;
|
||||
padding-bottom : 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.new-series {
|
||||
.search-item {
|
||||
.card();
|
||||
margin : 40px 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.add-series-search {
|
||||
margin-top : 20px;
|
||||
margin-bottom : 20px;
|
||||
padding-left : 20px;
|
||||
|
||||
*[class*='icon-'] {
|
||||
font-size : 28px;
|
||||
height : 30px;
|
||||
width : 40px;
|
||||
padding-top : 14px;
|
||||
}
|
||||
input {
|
||||
height : 50px;
|
||||
}
|
||||
}
|
||||
|
||||
.search-item {
|
||||
|
||||
.new-series-overview {
|
||||
overflow : hidden;
|
||||
height : 120px;
|
||||
}
|
||||
|
||||
.new-series-poster {
|
||||
min-width : 138px;
|
||||
min-height : 203px;
|
||||
max-width : 138px;
|
||||
max-height : 203px;
|
||||
margin : 10px;
|
||||
}
|
||||
|
||||
padding-bottom : 20px;
|
||||
a {
|
||||
color : #343434;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration : none;
|
||||
}
|
||||
select {
|
||||
font-size : 16px;
|
||||
}
|
||||
|
||||
.add-series {
|
||||
margin-left : 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
li.add-new {
|
||||
.clickable;
|
||||
|
||||
display: block;
|
||||
padding: 3px 20px;
|
||||
clear: both;
|
||||
font-weight: normal;
|
||||
line-height: 20px;
|
||||
color: rgb(51, 51, 51);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
li.add-new:hover {
|
||||
text-decoration: none;
|
||||
color: rgb(255, 255, 255);
|
||||
background-color: rgb(0, 129, 194);
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
.dropdown-menu {
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
.add-series-import-btn {
|
||||
width: 93.3%;
|
||||
}
|
||||
}
|
||||
|
||||
.root-folders-modal {
|
||||
overflow: visible;
|
||||
|
||||
.root-folders-list {
|
||||
overflow: auto;
|
||||
max-height: 300px;
|
||||
}
|
||||
}
|
||||
37
src/UI/Calendar/CalendarLayout.js
Normal file
37
src/UI/Calendar/CalendarLayout.js
Normal file
@@ -0,0 +1,37 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'marionette',
|
||||
'Calendar/UpcomingCollection',
|
||||
'Calendar/UpcomingCollectionView',
|
||||
'Calendar/CalendarView',
|
||||
], function (Marionette, UpcomingCollection, UpcomingCollectionView, CalendarView) {
|
||||
return Marionette.Layout.extend({
|
||||
template: 'Calendar/CalendarLayoutTemplate',
|
||||
|
||||
regions: {
|
||||
upcoming: '#x-upcoming',
|
||||
calendar: '#x-calendar'
|
||||
},
|
||||
|
||||
initialize: function () {
|
||||
this.upcomingCollection = new UpcomingCollection();
|
||||
this.upcomingCollection.fetch();
|
||||
},
|
||||
|
||||
onShow: function () {
|
||||
this._showUpcoming();
|
||||
this._showCalendar();
|
||||
},
|
||||
|
||||
_showUpcoming: function () {
|
||||
this.upcoming.show(new UpcomingCollectionView({
|
||||
collection: this.upcomingCollection
|
||||
}));
|
||||
},
|
||||
|
||||
_showCalendar: function () {
|
||||
this.calendar.show(new CalendarView());
|
||||
}
|
||||
});
|
||||
});
|
||||
17
src/UI/Calendar/CalendarLayoutTemplate.html
Normal file
17
src/UI/Calendar/CalendarLayoutTemplate.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<div class="row">
|
||||
<div class="span3">
|
||||
<h4>Upcoming</h4>
|
||||
<div id="x-upcoming"/>
|
||||
</div>
|
||||
<div class=span9>
|
||||
<div id="x-calendar" class="calendar"/>
|
||||
<div class="legend calendar">
|
||||
<ul class='legend-labels'>
|
||||
<li><span class="primary" title="Episode hasn't aired yet"></span>Unaired</li>
|
||||
<li><span class="warning" title="Episode is currently airing"></span>On Air</li>
|
||||
<li><span class="danger" title="Episode file has not been found"></span>Missing</li>
|
||||
<li><span class="success" title="Episode was downloaded and sorted"></span>Downloaded</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
117
src/UI/Calendar/CalendarView.js
Normal file
117
src/UI/Calendar/CalendarView.js
Normal file
@@ -0,0 +1,117 @@
|
||||
'use strict';
|
||||
|
||||
define(
|
||||
[
|
||||
'app',
|
||||
'marionette',
|
||||
'moment',
|
||||
'Calendar/Collection',
|
||||
'fullcalendar'
|
||||
], function (App, Marionette, Moment, CalendarCollection) {
|
||||
|
||||
var _instance;
|
||||
|
||||
return Marionette.ItemView.extend({
|
||||
initialize: function () {
|
||||
this.collection = new CalendarCollection();
|
||||
},
|
||||
render : function () {
|
||||
$(this.$el).empty().fullCalendar({
|
||||
defaultView : 'basicWeek',
|
||||
allDayDefault : false,
|
||||
ignoreTimezone: false,
|
||||
weekMode : 'variable',
|
||||
firstDay : window.NzbDrone.ServerStatus.startOfWeek,
|
||||
timeFormat : 'h(:mm)tt',
|
||||
header : {
|
||||
left : 'prev,next today',
|
||||
center: 'title',
|
||||
right : 'month,basicWeek,basicDay'
|
||||
},
|
||||
buttonText : {
|
||||
prev: '<i class="icon-arrow-left"></i>',
|
||||
next: '<i class="icon-arrow-right"></i>'
|
||||
},
|
||||
events : this.getEvents,
|
||||
eventRender : function (event, element) {
|
||||
$(element).addClass(event.statusLevel);
|
||||
$(element).children('.fc-event-inner').addClass(event.statusLevel);
|
||||
},
|
||||
eventClick : function (event) {
|
||||
App.vent.trigger(App.Commands.ShowEpisodeDetails, {episode: event.model});
|
||||
}
|
||||
});
|
||||
|
||||
_instance = this;
|
||||
},
|
||||
|
||||
onShow: function () {
|
||||
this.$('.fc-button-today').click();
|
||||
},
|
||||
|
||||
getEvents: function (start, end, callback) {
|
||||
var startDate = Moment(start).toISOString();
|
||||
var endDate = Moment(end).toISOString();
|
||||
|
||||
_instance.collection.fetch({
|
||||
data : { start: startDate, end: endDate },
|
||||
success: function (calendarCollection) {
|
||||
calendarCollection.each(function (element) {
|
||||
var episodeTitle = element.get('title');
|
||||
var seriesTitle = element.get('series').title;
|
||||
var start = element.get('airDateUtc');
|
||||
var runtime = element.get('series').runtime;
|
||||
var end = Moment(start).add('minutes', runtime).toISOString();
|
||||
|
||||
|
||||
element.set({
|
||||
title : seriesTitle,
|
||||
episodeTitle: episodeTitle,
|
||||
start : start,
|
||||
end : end,
|
||||
allDay : false
|
||||
});
|
||||
|
||||
element.set('statusLevel', _instance.getStatusLevel(element));
|
||||
element.set('model', element);
|
||||
});
|
||||
|
||||
callback(calendarCollection.toJSON());
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
getStatusLevel: function (element) {
|
||||
var hasFile = element.get('hasFile');
|
||||
var currentTime = Moment();
|
||||
var start = Moment(element.get('airDateUtc'));
|
||||
var end = Moment(element.get('end'));
|
||||
|
||||
var statusLevel = 'primary';
|
||||
|
||||
if (hasFile) {
|
||||
statusLevel = 'success';
|
||||
}
|
||||
|
||||
else if (currentTime.isAfter(start) && currentTime.isBefore(end)) {
|
||||
var s = start.toISOString();
|
||||
var e = end.toISOString();
|
||||
var c = currentTime.toISOString();
|
||||
|
||||
statusLevel = 'warning';
|
||||
}
|
||||
|
||||
else if (start.isBefore(currentTime) && !hasFile) {
|
||||
statusLevel = 'danger';
|
||||
}
|
||||
|
||||
var test = currentTime.startOf('day').format('LLLL');
|
||||
|
||||
if (end.isBefore(currentTime.startOf('day'))) {
|
||||
statusLevel += ' past'
|
||||
}
|
||||
|
||||
return statusLevel;
|
||||
}
|
||||
});
|
||||
});
|
||||
17
src/UI/Calendar/Collection.js
Normal file
17
src/UI/Calendar/Collection.js
Normal file
@@ -0,0 +1,17 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'backbone',
|
||||
'Series/EpisodeModel'
|
||||
], function (Backbone, EpisodeModel) {
|
||||
return Backbone.Collection.extend({
|
||||
url : window.NzbDrone.ApiRoot + '/calendar',
|
||||
model: EpisodeModel,
|
||||
|
||||
comparator: function (model) {
|
||||
var date = new Date(model.get('airDateUtc'));
|
||||
var time = date.getTime();
|
||||
return time;
|
||||
}
|
||||
});
|
||||
});
|
||||
32
src/UI/Calendar/UpcomingCollection.js
Normal file
32
src/UI/Calendar/UpcomingCollection.js
Normal file
@@ -0,0 +1,32 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'backbone',
|
||||
'moment',
|
||||
'Series/EpisodeModel'
|
||||
], function (Backbone, Moment, EpisodeModel) {
|
||||
return Backbone.Collection.extend({
|
||||
url : window.NzbDrone.ApiRoot + '/calendar',
|
||||
model: EpisodeModel,
|
||||
|
||||
comparator: function (model1, model2) {
|
||||
var airDate1 = model1.get('airDateUtc');
|
||||
var date1 = Moment(airDate1);
|
||||
var time1 = date1.unix();
|
||||
|
||||
var airDate2 = model2.get('airDateUtc');
|
||||
var date2 = Moment(airDate2);
|
||||
var time2 = date2.unix();
|
||||
|
||||
if (time1 < time2){
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (time1 > time2){
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
});
|
||||
11
src/UI/Calendar/UpcomingCollectionView.js
Normal file
11
src/UI/Calendar/UpcomingCollectionView.js
Normal file
@@ -0,0 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
define(
|
||||
[
|
||||
'marionette',
|
||||
'Calendar/UpcomingItemView'
|
||||
], function (Marionette, UpcomingItemView) {
|
||||
return Marionette.CollectionView.extend({
|
||||
itemView: UpcomingItemView
|
||||
});
|
||||
});
|
||||
31
src/UI/Calendar/UpcomingItemView.js
Normal file
31
src/UI/Calendar/UpcomingItemView.js
Normal file
@@ -0,0 +1,31 @@
|
||||
'use strict';
|
||||
|
||||
define(
|
||||
[
|
||||
'app',
|
||||
'marionette',
|
||||
'moment'
|
||||
], function (App, Marionette, Moment) {
|
||||
return Marionette.ItemView.extend({
|
||||
template: 'Calendar/UpcomingItemViewTemplate',
|
||||
tagName : 'div',
|
||||
|
||||
events: {
|
||||
'click .x-episode-title': '_showEpisodeDetails'
|
||||
},
|
||||
|
||||
initialize: function () {
|
||||
var start = this.model.get('airDateUtc');
|
||||
var runtime = this.model.get('series').runtime;
|
||||
var end = Moment(start).add('minutes', runtime);
|
||||
|
||||
this.model.set({
|
||||
end: end.toISOString()
|
||||
})
|
||||
},
|
||||
|
||||
_showEpisodeDetails: function () {
|
||||
App.vent.trigger(App.Commands.ShowEpisodeDetails, {episode: this.model});
|
||||
}
|
||||
});
|
||||
});
|
||||
13
src/UI/Calendar/UpcomingItemViewTemplate.html
Normal file
13
src/UI/Calendar/UpcomingItemViewTemplate.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<div class="event">
|
||||
<div class="date {{StatusLevel}}">
|
||||
<h1>{{Day airDateUtc}}</h1>
|
||||
<h4>{{Month airDateUtc}}</h4>
|
||||
</div>
|
||||
{{#with series}}
|
||||
<a href="{{route}}">
|
||||
<h4>{{title}}</h4>
|
||||
</a>
|
||||
{{/with}}
|
||||
<p>{{StartTime airDateUtc}} {{ShortDate airDateUtc}}<span class="pull-right">{{seasonNumber}}x{{Pad2 episodeNumber}}</span><br>{{episodeTitle}}</p>
|
||||
<p class="episode-title x-episode-title">{{title}}</p>
|
||||
</div>
|
||||
152
src/UI/Calendar/calendar.less
Normal file
152
src/UI/Calendar/calendar.less
Normal file
@@ -0,0 +1,152 @@
|
||||
@import "../Content/Bootstrap/mixins";
|
||||
@import "../Content/Bootstrap/variables";
|
||||
@import "../Content/Bootstrap/buttons";
|
||||
@import "../Shared/Styles/clickable";
|
||||
|
||||
.calendar {
|
||||
th, td {
|
||||
border-color : #eeeeee;
|
||||
}
|
||||
|
||||
.fc-event-skin {
|
||||
background-color : #007ccd;
|
||||
border : 1px solid #007ccd;
|
||||
border-radius : 4px;
|
||||
text-align : center;
|
||||
}
|
||||
|
||||
.fc-event {
|
||||
.clickable;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color : #eeeeee;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size : 17.5px;
|
||||
}
|
||||
|
||||
.fc-state-highlight {
|
||||
background : #f1f1f1;
|
||||
}
|
||||
|
||||
.past {
|
||||
opacity : 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.event {
|
||||
display : inline-block;
|
||||
width : 100%;
|
||||
margin-bottom : 10px;
|
||||
border-top : 1px solid #eeeeee;
|
||||
padding-top : 10px;
|
||||
|
||||
h4 {
|
||||
font-weight : 500;
|
||||
color : #008dcd;
|
||||
margin : 5px 0px;
|
||||
}
|
||||
|
||||
p {
|
||||
color : #999999;
|
||||
margin : 0px;
|
||||
}
|
||||
|
||||
.date {
|
||||
text-align : center;
|
||||
display : inline-block;
|
||||
border-left : 4px solid #eeeeee;
|
||||
padding-left : 16px;
|
||||
float : left;
|
||||
margin-right : 20px;
|
||||
|
||||
h4 {
|
||||
line-height : 1em;
|
||||
color : #555555;
|
||||
font-weight : 300;
|
||||
text-transform : uppercase;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight : 500;
|
||||
line-height : 0.8em;
|
||||
}
|
||||
}
|
||||
|
||||
.primary {
|
||||
border-color : @btnPrimaryBackground;
|
||||
}
|
||||
|
||||
.info {
|
||||
border-color : @btnInfoBackground;
|
||||
}
|
||||
|
||||
.inverse {
|
||||
border-color : @btnInverseBackground;
|
||||
}
|
||||
|
||||
.warning {
|
||||
border-color : @btnWarningBackground;
|
||||
}
|
||||
|
||||
.danger {
|
||||
border-color : @btnDangerBackground;
|
||||
}
|
||||
|
||||
.success {
|
||||
border-color : @btnSuccessBackground;;
|
||||
}
|
||||
|
||||
.purple {
|
||||
border-color : #7932ea;
|
||||
}
|
||||
|
||||
.episode-title {
|
||||
.btn-link;
|
||||
color : @linkColor;
|
||||
margin-top : 1px;
|
||||
.text-overflow;
|
||||
}
|
||||
}
|
||||
|
||||
.calendar {
|
||||
|
||||
background-position : -160px -128px;
|
||||
|
||||
.primary {
|
||||
border-color : @btnPrimaryBackground;
|
||||
background-color : @btnPrimaryBackground;
|
||||
}
|
||||
|
||||
.info {
|
||||
border-color : @btnInfoBackground;
|
||||
background-color : @btnInfoBackground;
|
||||
}
|
||||
|
||||
.inverse {
|
||||
border-color : @btnInverseBackground;
|
||||
background-color : @btnInverseBackground;
|
||||
}
|
||||
|
||||
.warning {
|
||||
border-color : @btnWarningBackground;
|
||||
background-color : @btnWarningBackground;
|
||||
}
|
||||
|
||||
.danger {
|
||||
border-color : @btnDangerBackground;
|
||||
background-color : @btnDangerBackground;
|
||||
}
|
||||
|
||||
.success {
|
||||
border-color : @btnSuccessBackground;
|
||||
background-color : @btnSuccessBackground;
|
||||
}
|
||||
|
||||
.purple {
|
||||
border-color : #7932ea;
|
||||
background-color : #7932ea;
|
||||
}
|
||||
}
|
||||
40
src/UI/Cells/ApprovalStatusCell.js
Normal file
40
src/UI/Cells/ApprovalStatusCell.js
Normal file
@@ -0,0 +1,40 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'backgrid',
|
||||
'marionette',
|
||||
'bootstrap'
|
||||
], function (Backgrid, Marionette) {
|
||||
|
||||
return Backgrid.Cell.extend({
|
||||
|
||||
className: 'approval-status-cell',
|
||||
template : 'Cells/ApprovalStatusCellTemplate',
|
||||
|
||||
|
||||
render: function () {
|
||||
|
||||
var rejections = this.model.get(this.column.get('name'));
|
||||
|
||||
if (rejections.length === 0) {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.templateFunction = Marionette.TemplateCache.get(this.template);
|
||||
|
||||
var html = this.templateFunction(rejections);
|
||||
this.$el.html('<i class="icon-exclamation-sign"/>');
|
||||
|
||||
this.$el.popover({
|
||||
content : html,
|
||||
html : true,
|
||||
trigger : 'hover',
|
||||
title : 'Release Rejected',
|
||||
placement: 'left',
|
||||
container: this.$el
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
});
|
||||
});
|
||||
5
src/UI/Cells/ApprovalStatusCellTemplate.html
Normal file
5
src/UI/Cells/ApprovalStatusCellTemplate.html
Normal file
@@ -0,0 +1,5 @@
|
||||
<ul>
|
||||
{{#each this}}
|
||||
<li>{{this}}</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
70
src/UI/Cells/Edit/QualityCellEditor.js
Normal file
70
src/UI/Cells/Edit/QualityCellEditor.js
Normal file
@@ -0,0 +1,70 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'backgrid',
|
||||
'Settings/Quality/Profile/QualityProfileSchemaCollection',
|
||||
'Series/EpisodeFileModel'
|
||||
], function (Backgrid, QualityProfileSchemaCollection, EpisodeFileModel) {
|
||||
return Backgrid.CellEditor.extend({
|
||||
|
||||
className: 'quality-cell-editor',
|
||||
template : 'Cells/Edit/QualityCellEditorTemplate',
|
||||
tagName : 'select',
|
||||
|
||||
events: {
|
||||
'change': 'save',
|
||||
'blur': 'close',
|
||||
'keydown': 'close'
|
||||
},
|
||||
|
||||
render: function () {
|
||||
var self = this;
|
||||
|
||||
var qualityProfileSchemaCollection = new QualityProfileSchemaCollection();
|
||||
var promise = qualityProfileSchemaCollection.fetch();
|
||||
|
||||
promise.done(function () {
|
||||
var templateName = self.template;
|
||||
self.schema = qualityProfileSchemaCollection.first();
|
||||
|
||||
var selected = _.find(self.schema.get('available'), { 'id': self.model.get(self.column.get("name")).quality.id });
|
||||
|
||||
if (selected) {
|
||||
selected.selected = true;
|
||||
}
|
||||
|
||||
self.templateFunction = Marionette.TemplateCache.get(templateName);
|
||||
var data = self.schema.toJSON();
|
||||
var html = self.templateFunction(data);
|
||||
self.$el.html(html);
|
||||
});
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
save: function (e) {
|
||||
var model = this.model;
|
||||
var column = this.column;
|
||||
var selected = parseInt(this.$el.val());
|
||||
|
||||
var quality = _.find(this.schema.get('available'), { 'id': selected });
|
||||
|
||||
var newQuality = {
|
||||
proper: false,
|
||||
quality: quality
|
||||
};
|
||||
|
||||
model.set(column.get("name"), newQuality);
|
||||
model.save();
|
||||
model.trigger("backgrid:edited", model, column, new Backgrid.Command(e));
|
||||
},
|
||||
|
||||
close: function (e) {
|
||||
var model = this.model;
|
||||
var column = this.column;
|
||||
var command = new Backgrid.Command(e);
|
||||
|
||||
model.trigger("backgrid:edited", model, column, command);
|
||||
}
|
||||
});
|
||||
});
|
||||
7
src/UI/Cells/Edit/QualityCellEditorTemplate.html
Normal file
7
src/UI/Cells/Edit/QualityCellEditorTemplate.html
Normal file
@@ -0,0 +1,7 @@
|
||||
{{#each available}}
|
||||
{{#if selected}}
|
||||
<option value="{{id}}" selected="selected">{{name}}</option>
|
||||
{{else}}
|
||||
<option value="{{id}}">{{name}}</option>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
54
src/UI/Cells/EpisodeActionsCell.js
Normal file
54
src/UI/Cells/EpisodeActionsCell.js
Normal file
@@ -0,0 +1,54 @@
|
||||
'use strict';
|
||||
|
||||
define(
|
||||
[
|
||||
'app',
|
||||
'Cells/NzbDroneCell',
|
||||
'Commands/CommandController'
|
||||
], function (App, NzbDroneCell, CommandController) {
|
||||
return NzbDroneCell.extend({
|
||||
|
||||
className: 'episode-actions-cell',
|
||||
template : 'Cells/EpisodeActionsCellTemplate',
|
||||
|
||||
ui: {
|
||||
automaticSearch: '.x-automatic-search-icon'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click .x-automatic-search': '_automaticSearch',
|
||||
'click .x-manual-search' : '_manualSearch'
|
||||
},
|
||||
|
||||
render: function () {
|
||||
var templateName = this.column.get('template') || this.template;
|
||||
|
||||
this.templateFunction = Marionette.TemplateCache.get(templateName);
|
||||
var data = this.cellValue.toJSON();
|
||||
var html = this.templateFunction(data);
|
||||
this.$el.html(html);
|
||||
|
||||
CommandController.bindToCommand({
|
||||
element: this.$(this.ui.automaticSearch),
|
||||
command: {
|
||||
name : 'episodeSearch',
|
||||
episodeId: this.model.get('id')
|
||||
}
|
||||
});
|
||||
|
||||
this.delegateEvents();
|
||||
return this;
|
||||
},
|
||||
|
||||
_automaticSearch: function () {
|
||||
CommandController.Execute('episodeSearch', {
|
||||
name : 'episodeSearch',
|
||||
episodeId: this.model.get('id')
|
||||
});
|
||||
},
|
||||
|
||||
_manualSearch: function () {
|
||||
App.vent.trigger(App.Commands.ShowEpisodeDetails, { episode: this.cellValue, hideSeriesLink: true, openingTab: 'search' });
|
||||
}
|
||||
});
|
||||
});
|
||||
10
src/UI/Cells/EpisodeActionsCellTemplate.html
Normal file
10
src/UI/Cells/EpisodeActionsCellTemplate.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-mini x-automatic-search x-automatic-search-icon" title="Automatic Search" data-container="body"><i class="icon-search"></i></button>
|
||||
<button class="btn btn-mini dropdown-toggle" data-toggle="dropdown">
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li class="x-automatic-search">Automatic Search</li>
|
||||
<li class="x-manual-search">Manual Search</li>
|
||||
</ul>
|
||||
</div>
|
||||
66
src/UI/Cells/EpisodeNumberCell.js
Normal file
66
src/UI/Cells/EpisodeNumberCell.js
Normal file
@@ -0,0 +1,66 @@
|
||||
'use strict';
|
||||
|
||||
define(
|
||||
[
|
||||
'Cells/NzbDroneCell',
|
||||
'Shared/FormatHelpers'
|
||||
], function (NzbDroneCell, FormatHelpers) {
|
||||
return NzbDroneCell.extend({
|
||||
|
||||
className: 'episode-number-cell',
|
||||
|
||||
render: function () {
|
||||
|
||||
this.$el.empty();
|
||||
|
||||
var airDateField = this.column.get('airDateUtc') || 'airDateUtc';
|
||||
var seasonField = this.column.get('seasonNumber') || 'seasonNumber';
|
||||
var episodeField = this.column.get('episodes') || 'episodeNumber';
|
||||
|
||||
if (this.model) {
|
||||
var result = 'Unknown';
|
||||
|
||||
var airDate = this.model.get(airDateField);
|
||||
var seasonNumber = this.model.get(seasonField);
|
||||
var episodes = this.model.get(episodeField);
|
||||
|
||||
if (this.cellValue) {
|
||||
if (!seasonNumber) {
|
||||
seasonNumber = this.cellValue.get(seasonField);
|
||||
}
|
||||
|
||||
if (!episodes) {
|
||||
episodes = this.cellValue.get(episodeField);
|
||||
}
|
||||
|
||||
if (!airDate) {
|
||||
this.model.get(airDateField);
|
||||
}
|
||||
}
|
||||
|
||||
if (episodes) {
|
||||
|
||||
var paddedEpisodes;
|
||||
|
||||
if (episodes.constructor === Array) {
|
||||
paddedEpisodes = _.map(episodes,function (episodeNumber) {
|
||||
return FormatHelpers.pad(episodeNumber, 2);
|
||||
}).join();
|
||||
}
|
||||
else {
|
||||
paddedEpisodes = FormatHelpers.pad(episodes, 2);
|
||||
}
|
||||
|
||||
result = '{0}x{1}'.format(seasonNumber, paddedEpisodes);
|
||||
}
|
||||
else if (airDate) {
|
||||
result = new Date(airDate).toLocaleDateString();
|
||||
}
|
||||
|
||||
this.$el.html(result);
|
||||
}
|
||||
this.delegateEvents();
|
||||
return this;
|
||||
}
|
||||
});
|
||||
});
|
||||
33
src/UI/Cells/EpisodeProgressCell.js
Normal file
33
src/UI/Cells/EpisodeProgressCell.js
Normal file
@@ -0,0 +1,33 @@
|
||||
'use strict';
|
||||
|
||||
define(
|
||||
[
|
||||
'marionette',
|
||||
'Cells/NzbDroneCell'
|
||||
], function (Marionette, NzbDroneCell) {
|
||||
return NzbDroneCell.extend({
|
||||
className: 'episode-progress-cell',
|
||||
template : 'Cells/EpisodeProgressCellTemplate',
|
||||
|
||||
render: function () {
|
||||
|
||||
var episodeCount = this.model.get('episodeCount');
|
||||
var episodeFileCount = this.model.get('episodeFileCount');
|
||||
|
||||
var percent = 100;
|
||||
|
||||
if (episodeCount > 0) {
|
||||
percent = episodeFileCount / episodeCount * 100;
|
||||
}
|
||||
|
||||
this.model.set('percentOfEpisodes', percent);
|
||||
|
||||
this.templateFunction = Marionette.TemplateCache.get(this.template);
|
||||
var data = this.model.toJSON();
|
||||
var html = this.templateFunction(data);
|
||||
this.$el.html(html);
|
||||
|
||||
return this;
|
||||
}
|
||||
});
|
||||
});
|
||||
1
src/UI/Cells/EpisodeProgressCellTemplate.html
Normal file
1
src/UI/Cells/EpisodeProgressCellTemplate.html
Normal file
@@ -0,0 +1 @@
|
||||
{{> EpisodeProgressPartial }}
|
||||
88
src/UI/Cells/EpisodeStatusCell.js
Normal file
88
src/UI/Cells/EpisodeStatusCell.js
Normal file
@@ -0,0 +1,88 @@
|
||||
'use strict';
|
||||
|
||||
define(
|
||||
[
|
||||
'app',
|
||||
'underscore',
|
||||
'Cells/NzbDroneCell',
|
||||
'History/Queue/QueueCollection',
|
||||
'moment',
|
||||
'Shared/FormatHelpers'
|
||||
], function (App, _, NzbDroneCell, QueueCollection, Moment, FormatHelpers) {
|
||||
return NzbDroneCell.extend({
|
||||
|
||||
className: 'episode-status-cell',
|
||||
|
||||
render: function () {
|
||||
this.listenTo(QueueCollection, 'sync', this._renderCell);
|
||||
|
||||
this._renderCell();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_renderCell: function () {
|
||||
this.$el.empty();
|
||||
|
||||
if (this.model) {
|
||||
|
||||
var icon;
|
||||
var tooltip;
|
||||
|
||||
var hasAired = Moment(this.model.get('airDateUtc')).isBefore(Moment());
|
||||
var hasFile = this.model.get('hasFile');
|
||||
|
||||
if (hasFile) {
|
||||
var episodeFile = App.request(App.Reqres.GetEpisodeFileById, this.model.get('episodeFileId'));
|
||||
|
||||
this.listenTo(episodeFile, 'change', this._refresh);
|
||||
|
||||
var quality = episodeFile.get('quality');
|
||||
var size = FormatHelpers.bytes(episodeFile.get('size'));
|
||||
var title = 'Episode downloaded';
|
||||
|
||||
if (quality.proper) {
|
||||
title += ' [PROPER] - {0}'.format(size);
|
||||
this.$el.html('<span class="badge badge-info" title="{0}">{1}</span>'.format(title, quality.quality.name));
|
||||
}
|
||||
|
||||
else {
|
||||
title += ' - {0}'.format(size);
|
||||
this.$el.html('<span class="badge badge-inverse" title="{0}">{1}</span>'.format(title, quality.quality.name));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
else {
|
||||
var model = this.model;
|
||||
|
||||
var downloading = _.find(QueueCollection.models, function (queueModel) {
|
||||
return queueModel.get('episode').id === model.get('id');
|
||||
});
|
||||
|
||||
if (downloading || this.model.get('downloading')) {
|
||||
icon = 'icon-nd-downloading';
|
||||
tooltip = 'Episode is downloading';
|
||||
}
|
||||
|
||||
else if (!this.model.get('airDateUtc')) {
|
||||
icon = 'icon-nd-tba';
|
||||
tooltip = 'TBA';
|
||||
}
|
||||
|
||||
else if (hasAired) {
|
||||
icon = 'icon-nd-missing';
|
||||
tooltip = 'Episode missing from disk';
|
||||
}
|
||||
else {
|
||||
icon = 'icon-nd-not-aired';
|
||||
tooltip = 'Episode has not aired';
|
||||
}
|
||||
}
|
||||
|
||||
this.$el.html('<i class="{0}" title="{1}"/>'.format(icon, tooltip));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
33
src/UI/Cells/EpisodeTitleCell.js
Normal file
33
src/UI/Cells/EpisodeTitleCell.js
Normal file
@@ -0,0 +1,33 @@
|
||||
'use strict';
|
||||
|
||||
define(
|
||||
[
|
||||
'app',
|
||||
'Cells/NzbDroneCell'
|
||||
], function (App, NzbDroneCell) {
|
||||
return NzbDroneCell.extend({
|
||||
|
||||
className: 'episode-title-cell',
|
||||
|
||||
events: {
|
||||
'click': '_showDetails'
|
||||
},
|
||||
|
||||
render: function () {
|
||||
var title = this.cellValue.get('title');
|
||||
|
||||
if (!title || title === '') {
|
||||
title = 'TBA';
|
||||
}
|
||||
|
||||
this.$el.html(title);
|
||||
return this;
|
||||
},
|
||||
|
||||
_showDetails: function () {
|
||||
var hideSeriesLink = this.column.get('hideSeriesLink');
|
||||
|
||||
App.vent.trigger(App.Commands.ShowEpisodeDetails, { episode: this.cellValue, hideSeriesLink: hideSeriesLink });
|
||||
}
|
||||
});
|
||||
});
|
||||
19
src/UI/Cells/FileSizeCell.js
Normal file
19
src/UI/Cells/FileSizeCell.js
Normal file
@@ -0,0 +1,19 @@
|
||||
'use strict';
|
||||
|
||||
define(
|
||||
[
|
||||
'backgrid',
|
||||
'Shared/FormatHelpers'
|
||||
], function (Backgrid, FormatHelpers) {
|
||||
return Backgrid.Cell.extend({
|
||||
|
||||
className: 'file-size-cell',
|
||||
|
||||
render: function () {
|
||||
var size = this.model.get(this.column.get('name'));
|
||||
this.$el.html(FormatHelpers.bytes(size));
|
||||
this.delegateEvents();
|
||||
return this;
|
||||
}
|
||||
});
|
||||
});
|
||||
69
src/UI/Cells/Header/QualityHeaderCell.js
Normal file
69
src/UI/Cells/Header/QualityHeaderCell.js
Normal file
@@ -0,0 +1,69 @@
|
||||
'use strict';
|
||||
|
||||
define(
|
||||
[
|
||||
'backgrid',
|
||||
'Shared/Grid/HeaderCell'
|
||||
], function (Backgrid, NzbDroneHeaderCell) {
|
||||
|
||||
Backgrid.QualityHeaderCell = NzbDroneHeaderCell.extend({
|
||||
events: {
|
||||
'click': 'onClick'
|
||||
},
|
||||
|
||||
onClick: function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
var self = this;
|
||||
var columnName = this.column.get('name');
|
||||
|
||||
if (this.column.get('sortable')) {
|
||||
if (this.direction() === 'ascending') {
|
||||
this.sort(columnName, 'descending', function (left, right) {
|
||||
var leftVal = left.get(columnName);
|
||||
var rightVal = right.get(columnName);
|
||||
|
||||
return self._comparator(leftVal, rightVal);
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.sort(columnName, 'ascending', function (left, right) {
|
||||
var leftVal = left.get(columnName);
|
||||
var rightVal = right.get(columnName);
|
||||
|
||||
return self._comparator(rightVal, leftVal);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_comparator: function (leftVal, rightVal) {
|
||||
var leftWeight = leftVal.quality.weight;
|
||||
var rightWeight = rightVal.quality.weight;
|
||||
|
||||
if (!leftWeight && !rightWeight) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!leftWeight) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!rightWeight) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (leftWeight === rightWeight) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (leftWeight > rightWeight) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
|
||||
return Backgrid.QualityHeaderCell;
|
||||
});
|
||||
16
src/UI/Cells/IndexerCell.js
Normal file
16
src/UI/Cells/IndexerCell.js
Normal file
@@ -0,0 +1,16 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'backgrid'
|
||||
], function (Backgrid) {
|
||||
return Backgrid.Cell.extend({
|
||||
|
||||
class : 'indexer-cell',
|
||||
|
||||
render: function () {
|
||||
var indexer = this.model.get(this.column.get('name'));
|
||||
this.$el.html(indexer);
|
||||
return this;
|
||||
}
|
||||
});
|
||||
});
|
||||
62
src/UI/Cells/NzbDroneCell.js
Normal file
62
src/UI/Cells/NzbDroneCell.js
Normal file
@@ -0,0 +1,62 @@
|
||||
'use strict';
|
||||
|
||||
define(
|
||||
[
|
||||
'backgrid'
|
||||
], function (Backgrid) {
|
||||
return Backgrid.Cell.extend({
|
||||
|
||||
_originalInit: Backgrid.Cell.prototype.initialize,
|
||||
|
||||
initialize: function () {
|
||||
this._originalInit.apply(this, arguments);
|
||||
this.cellValue = this._getValue();
|
||||
|
||||
this.listenTo(this.model, 'change', this._refresh);
|
||||
|
||||
if (this._onEdit) {
|
||||
this.listenTo(this.model, "backgrid:edit", function (model, column, cell, editor) {
|
||||
if (column.get("name") == this.column.get("name")) {
|
||||
this._onEdit(model, column, cell, editor);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_refresh: function () {
|
||||
this.cellValue = this._getValue();
|
||||
this.render();
|
||||
},
|
||||
|
||||
_getValue: function () {
|
||||
|
||||
var cellValue = this.column.get('cellValue');
|
||||
|
||||
if (cellValue) {
|
||||
if (cellValue === 'this') {
|
||||
return this.model;
|
||||
}
|
||||
}
|
||||
|
||||
var name = this.column.get('name');
|
||||
|
||||
if (name === 'this') {
|
||||
return this.model;
|
||||
}
|
||||
|
||||
var value = this.model.get(name);
|
||||
|
||||
if (!value) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
//if not a model
|
||||
if (!value.get && typeof value === 'object') {
|
||||
value = new Backbone.Model(value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
13
src/UI/Cells/QualityCell.js
Normal file
13
src/UI/Cells/QualityCell.js
Normal file
@@ -0,0 +1,13 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'Cells/TemplatedCell',
|
||||
'Cells/Edit/QualityCellEditor'
|
||||
], function (TemplatedCell, QualityCellEditor) {
|
||||
return TemplatedCell.extend({
|
||||
|
||||
className: 'quality-cell',
|
||||
template : 'Cells/QualityCellTemplate',
|
||||
editor : QualityCellEditor
|
||||
});
|
||||
});
|
||||
5
src/UI/Cells/QualityCellTemplate.html
Normal file
5
src/UI/Cells/QualityCellTemplate.html
Normal file
@@ -0,0 +1,5 @@
|
||||
{{#if proper}}
|
||||
<span class="badge badge-info" title="PROPER">{{quality.name}}</span>
|
||||
{{else}}
|
||||
<span class="badge badge-inverse">{{quality.name}}</span>
|
||||
{{/if}}
|
||||
24
src/UI/Cells/QualityProfileCell.js
Normal file
24
src/UI/Cells/QualityProfileCell.js
Normal file
@@ -0,0 +1,24 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'backgrid',
|
||||
'Quality/QualityProfileCollection'
|
||||
], function (Backgrid, QualityProfileCollection) {
|
||||
return Backgrid.Cell.extend({
|
||||
className: 'quality-profile-cell',
|
||||
|
||||
render: function () {
|
||||
|
||||
this.$el.empty();
|
||||
var qualityProfileId = this.model.get(this.column.get('name'));
|
||||
|
||||
var profile = _.findWhere(QualityProfileCollection.models, { id: qualityProfileId });
|
||||
|
||||
if (profile) {
|
||||
this.$el.html(profile.get('name'));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
});
|
||||
});
|
||||
23
src/UI/Cells/RelativeDateCell.js
Normal file
23
src/UI/Cells/RelativeDateCell.js
Normal file
@@ -0,0 +1,23 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'Cells/NzbDroneCell',
|
||||
'moment',
|
||||
'Shared/FormatHelpers'
|
||||
], function (NzbDroneCell, Moment, FormatHelpers) {
|
||||
return NzbDroneCell.extend({
|
||||
|
||||
className: 'relative-date-cell',
|
||||
|
||||
render: function () {
|
||||
|
||||
var date = this.model.get(this.column.get('name'));
|
||||
|
||||
if (date) {
|
||||
this.$el.html("<span title='" + Moment(date).format('LLLL') + "' >" + FormatHelpers.dateHelper(date) + "</span");
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
});
|
||||
});
|
||||
37
src/UI/Cells/SeriesActionsCell.js
Normal file
37
src/UI/Cells/SeriesActionsCell.js
Normal file
@@ -0,0 +1,37 @@
|
||||
'use strict';
|
||||
|
||||
define(
|
||||
[
|
||||
'app',
|
||||
'Cells/NzbDroneCell'
|
||||
], function (App, NzbDroneCell) {
|
||||
return NzbDroneCell.extend({
|
||||
|
||||
className: 'series-actions-cell',
|
||||
|
||||
events: {
|
||||
'click .x-edit-series' : '_editSeries',
|
||||
'click .x-remove-series': '_removeSeries'
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.$el.empty();
|
||||
|
||||
this.$el.html(
|
||||
'<i class="icon-cog x-edit-series" title="" data-original-title="Edit Series"></i> ' +
|
||||
'<i class="icon-remove x-remove-series" title="" data-original-title="Delete Series"></i>'
|
||||
);
|
||||
|
||||
this.delegateEvents();
|
||||
return this;
|
||||
},
|
||||
|
||||
_editSeries: function () {
|
||||
App.vent.trigger(App.Commands.EditSeriesCommand, {series:this.model});
|
||||
},
|
||||
|
||||
_removeSeries: function () {
|
||||
App.vent.trigger(App.Commands.DeleteSeriesCommand, {series:this.model});
|
||||
}
|
||||
});
|
||||
});
|
||||
32
src/UI/Cells/SeriesStatusCell.js
Normal file
32
src/UI/Cells/SeriesStatusCell.js
Normal file
@@ -0,0 +1,32 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'backgrid'
|
||||
], function (Backgrid) {
|
||||
return Backgrid.Cell.extend({
|
||||
className: 'series-status-cell',
|
||||
|
||||
render: function () {
|
||||
this.$el.empty();
|
||||
var monitored = this.model.get('monitored');
|
||||
var status = this.model.get('status');
|
||||
|
||||
if (status === 'ended') {
|
||||
this.$el.html('<i class="icon-stop grid-icon" title="Ended"></i>');
|
||||
this.model.set('statusWeight', 3);
|
||||
}
|
||||
|
||||
else if (!monitored) {
|
||||
this.$el.html('<i class="icon-pause grid-icon" title="Not Monitored"></i>');
|
||||
this.model.set('statusWeight', 2);
|
||||
}
|
||||
|
||||
else {
|
||||
this.$el.html('<i class="icon-play grid-icon" title="Continuing"></i>');
|
||||
this.model.set('statusWeight', 1);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
});
|
||||
});
|
||||
12
src/UI/Cells/SeriesTitleCell.js
Normal file
12
src/UI/Cells/SeriesTitleCell.js
Normal file
@@ -0,0 +1,12 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'Cells/TemplatedCell'
|
||||
], function (TemplatedCell) {
|
||||
return TemplatedCell.extend({
|
||||
|
||||
className: 'series-title',
|
||||
template : 'Cells/SeriesTitleTemplate'
|
||||
|
||||
});
|
||||
});
|
||||
1
src/UI/Cells/SeriesTitleTemplate.html
Normal file
1
src/UI/Cells/SeriesTitleTemplate.html
Normal file
@@ -0,0 +1 @@
|
||||
<a href="{{route}}">{{title}}</a>
|
||||
23
src/UI/Cells/TemplatedCell.js
Normal file
23
src/UI/Cells/TemplatedCell.js
Normal file
@@ -0,0 +1,23 @@
|
||||
'use strict';
|
||||
|
||||
define(
|
||||
[
|
||||
'marionette',
|
||||
'Cells/NzbDroneCell'
|
||||
], function (Marionette, NzbDroneCell) {
|
||||
return NzbDroneCell.extend({
|
||||
|
||||
render: function () {
|
||||
|
||||
var templateName = this.column.get('template') || this.template;
|
||||
|
||||
this.templateFunction = Marionette.TemplateCache.get(templateName);
|
||||
var data = this.cellValue.toJSON();
|
||||
var html = this.templateFunction(data);
|
||||
this.$el.html(html);
|
||||
|
||||
this.delegateEvents();
|
||||
return this;
|
||||
}
|
||||
});
|
||||
});
|
||||
53
src/UI/Cells/ToggleCell.js
Normal file
53
src/UI/Cells/ToggleCell.js
Normal file
@@ -0,0 +1,53 @@
|
||||
'use strict';
|
||||
|
||||
define(
|
||||
[
|
||||
'backgrid'
|
||||
], function (Backgrid) {
|
||||
return Backgrid.Cell.extend({
|
||||
|
||||
className: 'toggle-cell',
|
||||
|
||||
events: {
|
||||
'click': '_onClick'
|
||||
},
|
||||
|
||||
_onClick: function () {
|
||||
|
||||
var self = this;
|
||||
|
||||
this.$el.tooltip('hide');
|
||||
|
||||
var name = this.column.get('name');
|
||||
this.model.set(name, !this.model.get(name));
|
||||
|
||||
this.$('i').addClass('icon-spinner icon-spin');
|
||||
|
||||
this.model.save().always(function () {
|
||||
self.render();
|
||||
});
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.$el.empty();
|
||||
this.$el.html('<i />');
|
||||
|
||||
var name = this.column.get('name');
|
||||
|
||||
if (this.model.get(name)) {
|
||||
this.$('i').addClass(this.column.get('trueClass'));
|
||||
}
|
||||
else {
|
||||
this.$('i').addClass(this.column.get('falseClass'));
|
||||
}
|
||||
|
||||
var tooltip = this.column.get('tooltip');
|
||||
|
||||
if (tooltip) {
|
||||
this.$('i').attr('title', tooltip);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
});
|
||||
});
|
||||
99
src/UI/Cells/cells.less
Normal file
99
src/UI/Cells/cells.less
Normal file
@@ -0,0 +1,99 @@
|
||||
@import "../Content/Bootstrap/mixins";
|
||||
@import "../Content/Bootstrap/variables";
|
||||
@import "../Content/Bootstrap/buttons";
|
||||
@import "../Shared/Styles/clickable";
|
||||
|
||||
.episode-title-cell {
|
||||
.btn-link;
|
||||
}
|
||||
|
||||
.air-date-cell {
|
||||
width : 120px;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.relative-date-cell {
|
||||
width : 150px;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.history-event-type-cell {
|
||||
width : 10px;
|
||||
}
|
||||
|
||||
.download-report-cell {
|
||||
.clickable();
|
||||
|
||||
i {
|
||||
.clickable();
|
||||
}
|
||||
}
|
||||
|
||||
.toggle-cell{
|
||||
.clickable();
|
||||
}
|
||||
|
||||
.approval-status-cell {
|
||||
widows : 10px;
|
||||
.popover {
|
||||
max-width : 400px;
|
||||
}
|
||||
|
||||
i {
|
||||
color : @red;
|
||||
}
|
||||
}
|
||||
|
||||
td.episode-status-cell, td.quality-cell {
|
||||
text-align: center;
|
||||
width: 80px;
|
||||
|
||||
.badge {
|
||||
font-size: 9px;
|
||||
}
|
||||
}
|
||||
|
||||
.history-details-cell {
|
||||
.clickable();
|
||||
width: 10px;
|
||||
|
||||
i {
|
||||
.clickable();
|
||||
}
|
||||
}
|
||||
|
||||
.nzb-title-cell {
|
||||
max-width: 600px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.episode-actions-cell {
|
||||
width: 50px;
|
||||
|
||||
li {
|
||||
.clickable();
|
||||
|
||||
display: block;
|
||||
padding: 3px 20px;
|
||||
clear: both;
|
||||
font-weight: normal;
|
||||
line-height: 20px;
|
||||
color: rgb(51, 51, 51);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
li:hover {
|
||||
text-decoration: none;
|
||||
color: rgb(255, 255, 255);
|
||||
background-color: rgb(0, 129, 194);
|
||||
}
|
||||
}
|
||||
|
||||
.series-actions-cell {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.timeleft-cell {
|
||||
cursor: default;
|
||||
width: 80px;
|
||||
}
|
||||
26
src/UI/Commands/CommandCollection.js
Normal file
26
src/UI/Commands/CommandCollection.js
Normal file
@@ -0,0 +1,26 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'backbone',
|
||||
'Commands/CommandModel',
|
||||
'Mixins/backbone.signalr.mixin'
|
||||
], function (Backbone, CommandModel) {
|
||||
|
||||
var CommandCollection = Backbone.Collection.extend({
|
||||
url : window.NzbDrone.ApiRoot + '/command',
|
||||
model: CommandModel,
|
||||
|
||||
findCommand: function (command) {
|
||||
return this.find(function (model) {
|
||||
return model.isSameCommand(command);
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
var collection = new CommandCollection().bindSignalR();
|
||||
|
||||
collection.fetch();
|
||||
|
||||
return collection;
|
||||
});
|
||||
66
src/UI/Commands/CommandController.js
Normal file
66
src/UI/Commands/CommandController.js
Normal file
@@ -0,0 +1,66 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'app',
|
||||
'Commands/CommandModel',
|
||||
'Commands/CommandCollection',
|
||||
'underscore',
|
||||
'jQuery/jquery.spin'
|
||||
], function (App, CommandModel, CommandCollection, _) {
|
||||
|
||||
var singleton = function () {
|
||||
|
||||
return {
|
||||
|
||||
Execute: function (name, properties) {
|
||||
|
||||
var attr = _.extend({name: name.toLocaleLowerCase()}, properties);
|
||||
|
||||
var commandModel = new CommandModel(attr);
|
||||
|
||||
return commandModel.save().success(function () {
|
||||
CommandCollection.add(commandModel);
|
||||
});
|
||||
},
|
||||
|
||||
bindToCommand: function (options) {
|
||||
|
||||
var self = this;
|
||||
|
||||
var existingCommand = CommandCollection.findCommand(options.command);
|
||||
|
||||
if (existingCommand) {
|
||||
this._bindToCommandModel.call(this, existingCommand, options);
|
||||
}
|
||||
|
||||
CommandCollection.bind('add sync', function (model) {
|
||||
if (model.isSameCommand(options.command)) {
|
||||
self._bindToCommandModel.call(self, model, options);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_bindToCommandModel: function bindToCommand(model, options) {
|
||||
|
||||
if (!model.isActive()) {
|
||||
options.element.stopSpin();
|
||||
return;
|
||||
}
|
||||
|
||||
model.bind('change:state', function (model) {
|
||||
if (!model.isActive()) {
|
||||
options.element.stopSpin();
|
||||
|
||||
if (model.isComplete()) {
|
||||
App.vent.trigger(App.Events.CommandComplete, { command: model, model: options.model });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
options.element.startSpin();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
return singleton();
|
||||
});
|
||||
15
src/UI/Commands/CommandMessengerCollectionView.js
Normal file
15
src/UI/Commands/CommandMessengerCollectionView.js
Normal file
@@ -0,0 +1,15 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'app',
|
||||
'marionette',
|
||||
'Commands/CommandCollection',
|
||||
'Commands/CommandMessengerItemView'
|
||||
], function (App, Marionette, commandCollection, CommandMessengerItemView) {
|
||||
|
||||
var CollectionView = Marionette.CollectionView.extend({
|
||||
itemView : CommandMessengerItemView
|
||||
});
|
||||
|
||||
new CollectionView({collection: commandCollection});
|
||||
});
|
||||
48
src/UI/Commands/CommandMessengerItemView.js
Normal file
48
src/UI/Commands/CommandMessengerItemView.js
Normal file
@@ -0,0 +1,48 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'app',
|
||||
'marionette',
|
||||
'Shared/Messenger'
|
||||
], function (App, Marionette, Messenger) {
|
||||
|
||||
return Marionette.ItemView.extend({
|
||||
|
||||
|
||||
initialize: function () {
|
||||
this.listenTo(this.model, 'change', this.render);
|
||||
},
|
||||
|
||||
|
||||
render: function () {
|
||||
if (!this.model.get('message') || !this.model.get('sendUpdatesToClient')) {
|
||||
return;
|
||||
}
|
||||
|
||||
var message = {
|
||||
type : 'info',
|
||||
message : '[{0}] {1}'.format(this.model.get('name'), this.model.get('message')),
|
||||
id : this.model.id,
|
||||
hideAfter: 0
|
||||
};
|
||||
|
||||
switch (this.model.get('state')) {
|
||||
case 'completed':
|
||||
message.hideAfter = 4;
|
||||
break;
|
||||
case 'failed':
|
||||
message.hideAfter = 4;
|
||||
message.type = 'error';
|
||||
break;
|
||||
default :
|
||||
message.hideAfter = 0;
|
||||
}
|
||||
|
||||
Messenger.show(message);
|
||||
|
||||
console.log(message.message);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
37
src/UI/Commands/CommandModel.js
Normal file
37
src/UI/Commands/CommandModel.js
Normal file
@@ -0,0 +1,37 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'backbone'
|
||||
], function (Backbone) {
|
||||
return Backbone.Model.extend({
|
||||
url: window.NzbDrone.ApiRoot + '/command',
|
||||
|
||||
parse: function (response) {
|
||||
response.name = response.name.toLocaleLowerCase();
|
||||
return response;
|
||||
},
|
||||
|
||||
isSameCommand: function (command) {
|
||||
|
||||
if (command.name.toLocaleLowerCase() !== this.get('name').toLocaleLowerCase()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var key in command) {
|
||||
if (key !== 'name' && command[key] !== this.get(key)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
isActive: function () {
|
||||
return this.get('state') !== 'completed' && this.get('state') !== 'failed';
|
||||
},
|
||||
|
||||
isComplete: function () {
|
||||
return this.get('state') === 'completed';
|
||||
}
|
||||
});
|
||||
});
|
||||
43
src/UI/Config.js
Normal file
43
src/UI/Config.js
Normal file
@@ -0,0 +1,43 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'app'
|
||||
], function (App) {
|
||||
return {
|
||||
Events: {
|
||||
ConfigUpdatedEvent: 'ConfigUpdatedEvent'
|
||||
},
|
||||
Keys : {
|
||||
DefaultQualityProfileId: 'DefaultQualityProfileId',
|
||||
DefaultRootFolderId: 'DefaultRootFolderId'
|
||||
},
|
||||
|
||||
getValueBoolean: function (key, defaultValue) {
|
||||
return this.getValue(key, defaultValue) === 'true';
|
||||
},
|
||||
|
||||
getValue: function (key, defaultValue) {
|
||||
|
||||
var storeValue = localStorage.getItem(key);
|
||||
|
||||
if (!storeValue) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return storeValue.toString();
|
||||
},
|
||||
|
||||
setValue: function (key, value) {
|
||||
|
||||
console.log('Config: [{0}] => [{1}] '.format(key, value));
|
||||
|
||||
if (this.getValue(key) === value.toString()) {
|
||||
return;
|
||||
}
|
||||
|
||||
localStorage.setItem(key, value);
|
||||
App.vent.trigger(this.Events.ConfigUpdatedEvent, {key: key, value: value});
|
||||
|
||||
}
|
||||
};
|
||||
});
|
||||
2
src/UI/Content/Backgrid/backgrid.less
Normal file
2
src/UI/Content/Backgrid/backgrid.less
Normal file
@@ -0,0 +1,2 @@
|
||||
@import "filter";
|
||||
@import "paginator";
|
||||
11
src/UI/Content/Backgrid/filter.less
Normal file
11
src/UI/Content/Backgrid/filter.less
Normal file
@@ -0,0 +1,11 @@
|
||||
.backgrid-filter .close {
|
||||
display : inline-block;
|
||||
float : none;
|
||||
width : 20px;
|
||||
height : 20px;
|
||||
margin-top : -4px;
|
||||
font-size : 20px;
|
||||
line-height : 20px;
|
||||
text-align : center;
|
||||
vertical-align : text-top;
|
||||
}
|
||||
58
src/UI/Content/Backgrid/paginator.less
Normal file
58
src/UI/Content/Backgrid/paginator.less
Normal file
@@ -0,0 +1,58 @@
|
||||
@import "../prefixer";
|
||||
@import "../../Shared/Styles/clickable.less";
|
||||
|
||||
.backgrid-paginator {
|
||||
text-align : center;
|
||||
box-sizing : border-box;
|
||||
border-top : none;
|
||||
.box-sizing(border-box);
|
||||
.border-radius(0 0 4px 4px);
|
||||
position: relative;
|
||||
|
||||
.total-records {
|
||||
display : inline-block;
|
||||
height : 30px;
|
||||
padding : 0;
|
||||
line-height: 30px;
|
||||
font-size : 13px;
|
||||
position : absolute;
|
||||
right : 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
display : inline-block;
|
||||
|
||||
li {
|
||||
display : inline;
|
||||
|
||||
i, span {
|
||||
float : left;
|
||||
width : 30px;
|
||||
height : 30px;
|
||||
padding : 0;
|
||||
line-height : 30px;
|
||||
text-decoration : none;
|
||||
}
|
||||
|
||||
.pager-btn {
|
||||
.clickable;
|
||||
}
|
||||
}
|
||||
.active {
|
||||
span {
|
||||
background-color : #f5f5f5;
|
||||
color : #999999;
|
||||
cursor : default;
|
||||
width : inherit;
|
||||
padding : 0px 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.disabled {
|
||||
i, span {
|
||||
color : #999999;
|
||||
cursor : default;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
34
src/UI/Content/Bootstrap/accordion.less
Normal file
34
src/UI/Content/Bootstrap/accordion.less
Normal file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// Accordion
|
||||
// --------------------------------------------------
|
||||
|
||||
|
||||
// Parent container
|
||||
.accordion {
|
||||
margin-bottom: @baseLineHeight;
|
||||
}
|
||||
|
||||
// Group == heading + body
|
||||
.accordion-group {
|
||||
margin-bottom: 2px;
|
||||
border: 1px solid #e5e5e5;
|
||||
.border-radius(@baseBorderRadius);
|
||||
}
|
||||
.accordion-heading {
|
||||
border-bottom: 0;
|
||||
}
|
||||
.accordion-heading .accordion-toggle {
|
||||
display: block;
|
||||
padding: 8px 15px;
|
||||
}
|
||||
|
||||
// General toggle styles
|
||||
.accordion-toggle {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
// Inner needs the styles because you can't animate properly with any styles on the element
|
||||
.accordion-inner {
|
||||
padding: 9px 15px;
|
||||
border-top: 1px solid #e5e5e5;
|
||||
}
|
||||
79
src/UI/Content/Bootstrap/alerts.less
Normal file
79
src/UI/Content/Bootstrap/alerts.less
Normal file
@@ -0,0 +1,79 @@
|
||||
//
|
||||
// Alerts
|
||||
// --------------------------------------------------
|
||||
|
||||
|
||||
// Base styles
|
||||
// -------------------------
|
||||
|
||||
.alert {
|
||||
padding: 8px 35px 8px 14px;
|
||||
margin-bottom: @baseLineHeight;
|
||||
text-shadow: 0 1px 0 rgba(255,255,255,.5);
|
||||
background-color: @warningBackground;
|
||||
border: 1px solid @warningBorder;
|
||||
.border-radius(@baseBorderRadius);
|
||||
}
|
||||
.alert,
|
||||
.alert h4 {
|
||||
// Specified for the h4 to prevent conflicts of changing @headingsColor
|
||||
color: @warningText;
|
||||
}
|
||||
.alert h4 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
// Adjust close link position
|
||||
.alert .close {
|
||||
position: relative;
|
||||
top: -2px;
|
||||
right: -21px;
|
||||
line-height: @baseLineHeight;
|
||||
}
|
||||
|
||||
|
||||
// Alternate styles
|
||||
// -------------------------
|
||||
|
||||
.alert-success {
|
||||
background-color: @successBackground;
|
||||
border-color: @successBorder;
|
||||
color: @successText;
|
||||
}
|
||||
.alert-success h4 {
|
||||
color: @successText;
|
||||
}
|
||||
.alert-danger,
|
||||
.alert-error {
|
||||
background-color: @errorBackground;
|
||||
border-color: @errorBorder;
|
||||
color: @errorText;
|
||||
}
|
||||
.alert-danger h4,
|
||||
.alert-error h4 {
|
||||
color: @errorText;
|
||||
}
|
||||
.alert-info {
|
||||
background-color: @infoBackground;
|
||||
border-color: @infoBorder;
|
||||
color: @infoText;
|
||||
}
|
||||
.alert-info h4 {
|
||||
color: @infoText;
|
||||
}
|
||||
|
||||
|
||||
// Block alerts
|
||||
// -------------------------
|
||||
|
||||
.alert-block {
|
||||
padding-top: 14px;
|
||||
padding-bottom: 14px;
|
||||
}
|
||||
.alert-block > p,
|
||||
.alert-block > ul {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.alert-block p + p {
|
||||
margin-top: 5px;
|
||||
}
|
||||
63
src/UI/Content/Bootstrap/bootstrap.less
vendored
Normal file
63
src/UI/Content/Bootstrap/bootstrap.less
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
/*!
|
||||
* Bootstrap v2.3.2
|
||||
*
|
||||
* Copyright 2012 Twitter, Inc
|
||||
* Licensed under the Apache License v2.0
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Designed and built with all the love in the world @twitter by @mdo and @fat.
|
||||
*/
|
||||
|
||||
// Core variables and mixins
|
||||
@import "variables.less"; // Modify this for custom colors, font-sizes, etc
|
||||
@import "mixins.less";
|
||||
|
||||
// CSS Reset
|
||||
@import "reset.less";
|
||||
|
||||
// Grid system and page structure
|
||||
@import "scaffolding.less";
|
||||
@import "grid.less";
|
||||
@import "layouts.less";
|
||||
|
||||
// Base CSS
|
||||
@import "type.less";
|
||||
@import "code.less";
|
||||
@import "forms.less";
|
||||
@import "tables.less";
|
||||
|
||||
// Components: common
|
||||
@import "../FontAwesome/font-awesome.less";
|
||||
@import "dropdowns.less";
|
||||
@import "wells.less";
|
||||
@import "component-animations.less";
|
||||
@import "close.less";
|
||||
|
||||
// Components: Buttons & Alerts
|
||||
@import "buttons.less";
|
||||
@import "button-groups.less";
|
||||
@import "alerts.less"; // Note: alerts share common CSS with buttons and thus have styles in buttons.less
|
||||
|
||||
// Components: Nav
|
||||
@import "navs.less";
|
||||
@import "navbar.less";
|
||||
@import "breadcrumbs.less";
|
||||
@import "pagination.less";
|
||||
@import "pager.less";
|
||||
|
||||
// Components: Popovers
|
||||
@import "modals.less";
|
||||
@import "tooltip.less";
|
||||
@import "popovers.less";
|
||||
|
||||
// Components: Misc
|
||||
@import "thumbnails.less";
|
||||
@import "media.less";
|
||||
@import "labels-badges.less";
|
||||
@import "progress-bars.less";
|
||||
@import "accordion.less";
|
||||
@import "carousel.less";
|
||||
@import "hero-unit.less";
|
||||
|
||||
// Utility classes
|
||||
@import "utilities.less"; // Has to be last to override when necessary
|
||||
24
src/UI/Content/Bootstrap/breadcrumbs.less
Normal file
24
src/UI/Content/Bootstrap/breadcrumbs.less
Normal file
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// Breadcrumbs
|
||||
// --------------------------------------------------
|
||||
|
||||
|
||||
.breadcrumb {
|
||||
padding: 8px 15px;
|
||||
margin: 0 0 @baseLineHeight;
|
||||
list-style: none;
|
||||
background-color: #f5f5f5;
|
||||
.border-radius(@baseBorderRadius);
|
||||
> li {
|
||||
display: inline-block;
|
||||
.ie7-inline-block();
|
||||
text-shadow: 0 1px 0 @white;
|
||||
> .divider {
|
||||
padding: 0 5px;
|
||||
color: #ccc;
|
||||
}
|
||||
}
|
||||
> .active {
|
||||
color: @grayLight;
|
||||
}
|
||||
}
|
||||
229
src/UI/Content/Bootstrap/button-groups.less
Normal file
229
src/UI/Content/Bootstrap/button-groups.less
Normal file
@@ -0,0 +1,229 @@
|
||||
//
|
||||
// Button groups
|
||||
// --------------------------------------------------
|
||||
|
||||
|
||||
// Make the div behave like a button
|
||||
.btn-group {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
.ie7-inline-block();
|
||||
font-size: 0; // remove as part 1 of font-size inline-block hack
|
||||
vertical-align: middle; // match .btn alignment given font-size hack above
|
||||
white-space: nowrap; // prevent buttons from wrapping when in tight spaces (e.g., the table on the tests page)
|
||||
.ie7-restore-left-whitespace();
|
||||
}
|
||||
|
||||
// Space out series of button groups
|
||||
.btn-group + .btn-group {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
// Optional: Group multiple button groups together for a toolbar
|
||||
.btn-toolbar {
|
||||
font-size: 0; // Hack to remove whitespace that results from using inline-block
|
||||
margin-top: @baseLineHeight / 2;
|
||||
margin-bottom: @baseLineHeight / 2;
|
||||
> .btn + .btn,
|
||||
> .btn-group + .btn,
|
||||
> .btn + .btn-group {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
// Float them, remove border radius, then re-add to first and last elements
|
||||
.btn-group > .btn {
|
||||
position: relative;
|
||||
.border-radius(0);
|
||||
}
|
||||
.btn-group > .btn + .btn {
|
||||
margin-left: -1px;
|
||||
}
|
||||
.btn-group > .btn,
|
||||
.btn-group > .dropdown-menu,
|
||||
.btn-group > .popover {
|
||||
font-size: @baseFontSize; // redeclare as part 2 of font-size inline-block hack
|
||||
}
|
||||
|
||||
// Reset fonts for other sizes
|
||||
.btn-group > .btn-mini {
|
||||
font-size: @fontSizeMini;
|
||||
}
|
||||
.btn-group > .btn-small {
|
||||
font-size: @fontSizeSmall;
|
||||
}
|
||||
.btn-group > .btn-large {
|
||||
font-size: @fontSizeLarge;
|
||||
}
|
||||
|
||||
// Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match
|
||||
.btn-group > .btn:first-child {
|
||||
margin-left: 0;
|
||||
.border-top-left-radius(@baseBorderRadius);
|
||||
.border-bottom-left-radius(@baseBorderRadius);
|
||||
}
|
||||
// Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it
|
||||
.btn-group > .btn:last-child,
|
||||
.btn-group > .dropdown-toggle {
|
||||
.border-top-right-radius(@baseBorderRadius);
|
||||
.border-bottom-right-radius(@baseBorderRadius);
|
||||
}
|
||||
// Reset corners for large buttons
|
||||
.btn-group > .btn.large:first-child {
|
||||
margin-left: 0;
|
||||
.border-top-left-radius(@borderRadiusLarge);
|
||||
.border-bottom-left-radius(@borderRadiusLarge);
|
||||
}
|
||||
.btn-group > .btn.large:last-child,
|
||||
.btn-group > .large.dropdown-toggle {
|
||||
.border-top-right-radius(@borderRadiusLarge);
|
||||
.border-bottom-right-radius(@borderRadiusLarge);
|
||||
}
|
||||
|
||||
// On hover/focus/active, bring the proper btn to front
|
||||
.btn-group > .btn:hover,
|
||||
.btn-group > .btn:focus,
|
||||
.btn-group > .btn:active,
|
||||
.btn-group > .btn.active {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
// On active and open, don't show outline
|
||||
.btn-group .dropdown-toggle:active,
|
||||
.btn-group.open .dropdown-toggle {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Split button dropdowns
|
||||
// ----------------------
|
||||
|
||||
// Give the line between buttons some depth
|
||||
.btn-group > .btn + .dropdown-toggle {
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
.box-shadow(~"inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05)");
|
||||
*padding-top: 5px;
|
||||
*padding-bottom: 5px;
|
||||
}
|
||||
.btn-group > .btn-mini + .dropdown-toggle {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
*padding-top: 2px;
|
||||
*padding-bottom: 2px;
|
||||
}
|
||||
.btn-group > .btn-small + .dropdown-toggle {
|
||||
*padding-top: 5px;
|
||||
*padding-bottom: 4px;
|
||||
}
|
||||
.btn-group > .btn-large + .dropdown-toggle {
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
*padding-top: 7px;
|
||||
*padding-bottom: 7px;
|
||||
}
|
||||
|
||||
.btn-group.open {
|
||||
|
||||
// The clickable button for toggling the menu
|
||||
// Remove the gradient and set the same inset shadow as the :active state
|
||||
.dropdown-toggle {
|
||||
background-image: none;
|
||||
.box-shadow(~"inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05)");
|
||||
}
|
||||
|
||||
// Keep the hover's background when dropdown is open
|
||||
.btn.dropdown-toggle {
|
||||
background-color: @btnBackgroundHighlight;
|
||||
}
|
||||
.btn-primary.dropdown-toggle {
|
||||
background-color: @btnPrimaryBackgroundHighlight;
|
||||
}
|
||||
.btn-warning.dropdown-toggle {
|
||||
background-color: @btnWarningBackgroundHighlight;
|
||||
}
|
||||
.btn-danger.dropdown-toggle {
|
||||
background-color: @btnDangerBackgroundHighlight;
|
||||
}
|
||||
.btn-success.dropdown-toggle {
|
||||
background-color: @btnSuccessBackgroundHighlight;
|
||||
}
|
||||
.btn-info.dropdown-toggle {
|
||||
background-color: @btnInfoBackgroundHighlight;
|
||||
}
|
||||
.btn-inverse.dropdown-toggle {
|
||||
background-color: @btnInverseBackgroundHighlight;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Reposition the caret
|
||||
.btn .caret {
|
||||
margin-top: 8px;
|
||||
margin-left: 0;
|
||||
}
|
||||
// Carets in other button sizes
|
||||
.btn-large .caret {
|
||||
margin-top: 6px;
|
||||
}
|
||||
.btn-large .caret {
|
||||
border-left-width: 5px;
|
||||
border-right-width: 5px;
|
||||
border-top-width: 5px;
|
||||
}
|
||||
.btn-mini .caret,
|
||||
.btn-small .caret {
|
||||
margin-top: 8px;
|
||||
}
|
||||
// Upside down carets for .dropup
|
||||
.dropup .btn-large .caret {
|
||||
border-bottom-width: 5px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Account for other colors
|
||||
.btn-primary,
|
||||
.btn-warning,
|
||||
.btn-danger,
|
||||
.btn-info,
|
||||
.btn-success,
|
||||
.btn-inverse {
|
||||
.caret {
|
||||
border-top-color: @white;
|
||||
border-bottom-color: @white;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Vertical button groups
|
||||
// ----------------------
|
||||
|
||||
.btn-group-vertical {
|
||||
display: inline-block; // makes buttons only take up the width they need
|
||||
.ie7-inline-block();
|
||||
}
|
||||
.btn-group-vertical > .btn {
|
||||
display: block;
|
||||
float: none;
|
||||
max-width: 100%;
|
||||
.border-radius(0);
|
||||
}
|
||||
.btn-group-vertical > .btn + .btn {
|
||||
margin-left: 0;
|
||||
margin-top: -1px;
|
||||
}
|
||||
.btn-group-vertical > .btn:first-child {
|
||||
.border-radius(@baseBorderRadius @baseBorderRadius 0 0);
|
||||
}
|
||||
.btn-group-vertical > .btn:last-child {
|
||||
.border-radius(0 0 @baseBorderRadius @baseBorderRadius);
|
||||
}
|
||||
.btn-group-vertical > .btn-large:first-child {
|
||||
.border-radius(@borderRadiusLarge @borderRadiusLarge 0 0);
|
||||
}
|
||||
.btn-group-vertical > .btn-large:last-child {
|
||||
.border-radius(0 0 @borderRadiusLarge @borderRadiusLarge);
|
||||
}
|
||||
228
src/UI/Content/Bootstrap/buttons.less
Normal file
228
src/UI/Content/Bootstrap/buttons.less
Normal file
@@ -0,0 +1,228 @@
|
||||
//
|
||||
// Buttons
|
||||
// --------------------------------------------------
|
||||
|
||||
|
||||
// Base styles
|
||||
// --------------------------------------------------
|
||||
|
||||
// Core
|
||||
.btn {
|
||||
display: inline-block;
|
||||
.ie7-inline-block();
|
||||
padding: 4px 12px;
|
||||
margin-bottom: 0; // For input.btn
|
||||
font-size: @baseFontSize;
|
||||
line-height: @baseLineHeight;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
.buttonBackground(@btnBackground, @btnBackgroundHighlight, @grayDark, 0 1px 1px rgba(255,255,255,.75));
|
||||
border: 1px solid @btnBorder;
|
||||
*border: 0; // Remove the border to prevent IE7's black border on input:focus
|
||||
border-bottom-color: darken(@btnBorder, 10%);
|
||||
.border-radius(@baseBorderRadius);
|
||||
.ie7-restore-left-whitespace(); // Give IE7 some love
|
||||
.box-shadow(~"inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05)");
|
||||
|
||||
// Hover/focus state
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: @grayDark;
|
||||
text-decoration: none;
|
||||
background-position: 0 -15px;
|
||||
|
||||
// transition is only when going to hover/focus, otherwise the background
|
||||
// behind the gradient (there for IE<=9 fallback) gets mismatched
|
||||
.transition(background-position .1s linear);
|
||||
}
|
||||
|
||||
// Focus state for keyboard and accessibility
|
||||
&:focus {
|
||||
.tab-focus();
|
||||
}
|
||||
|
||||
// Active state
|
||||
&.active,
|
||||
&:active {
|
||||
background-image: none;
|
||||
outline: 0;
|
||||
.box-shadow(~"inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05)");
|
||||
}
|
||||
|
||||
// Disabled state
|
||||
&.disabled,
|
||||
&[disabled] {
|
||||
cursor: default;
|
||||
background-image: none;
|
||||
.opacity(65);
|
||||
.box-shadow(none);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Button Sizes
|
||||
// --------------------------------------------------
|
||||
|
||||
// Large
|
||||
.btn-large {
|
||||
padding: @paddingLarge;
|
||||
font-size: @fontSizeLarge;
|
||||
.border-radius(@borderRadiusLarge);
|
||||
}
|
||||
.btn-large [class^="icon-"],
|
||||
.btn-large [class*=" icon-"] {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
// Small
|
||||
.btn-small {
|
||||
padding: @paddingSmall;
|
||||
font-size: @fontSizeSmall;
|
||||
.border-radius(@borderRadiusSmall);
|
||||
}
|
||||
.btn-small [class^="icon-"],
|
||||
.btn-small [class*=" icon-"] {
|
||||
margin-top: 0;
|
||||
}
|
||||
.btn-mini [class^="icon-"],
|
||||
.btn-mini [class*=" icon-"] {
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
// Mini
|
||||
.btn-mini {
|
||||
padding: @paddingMini;
|
||||
font-size: @fontSizeMini;
|
||||
.border-radius(@borderRadiusSmall);
|
||||
}
|
||||
|
||||
|
||||
// Block button
|
||||
// -------------------------
|
||||
|
||||
.btn-block {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
.box-sizing(border-box);
|
||||
}
|
||||
|
||||
// Vertically space out multiple block buttons
|
||||
.btn-block + .btn-block {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
// Specificity overrides
|
||||
input[type="submit"],
|
||||
input[type="reset"],
|
||||
input[type="button"] {
|
||||
&.btn-block {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Alternate buttons
|
||||
// --------------------------------------------------
|
||||
|
||||
// Provide *some* extra contrast for those who can get it
|
||||
.btn-primary.active,
|
||||
.btn-warning.active,
|
||||
.btn-danger.active,
|
||||
.btn-success.active,
|
||||
.btn-info.active,
|
||||
.btn-inverse.active {
|
||||
color: rgba(255,255,255,.75);
|
||||
}
|
||||
|
||||
// Set the backgrounds
|
||||
// -------------------------
|
||||
.btn-primary {
|
||||
.buttonBackground(@btnPrimaryBackground, @btnPrimaryBackgroundHighlight);
|
||||
}
|
||||
// Warning appears are orange
|
||||
.btn-warning {
|
||||
.buttonBackground(@btnWarningBackground, @btnWarningBackgroundHighlight);
|
||||
}
|
||||
// Danger and error appear as red
|
||||
.btn-danger {
|
||||
.buttonBackground(@btnDangerBackground, @btnDangerBackgroundHighlight);
|
||||
}
|
||||
// Success appears as green
|
||||
.btn-success {
|
||||
.buttonBackground(@btnSuccessBackground, @btnSuccessBackgroundHighlight);
|
||||
}
|
||||
// Info appears as a neutral blue
|
||||
.btn-info {
|
||||
.buttonBackground(@btnInfoBackground, @btnInfoBackgroundHighlight);
|
||||
}
|
||||
// Inverse appears as dark gray
|
||||
.btn-inverse {
|
||||
.buttonBackground(@btnInverseBackground, @btnInverseBackgroundHighlight);
|
||||
}
|
||||
|
||||
|
||||
// Cross-browser Jank
|
||||
// --------------------------------------------------
|
||||
|
||||
button.btn,
|
||||
input[type="submit"].btn {
|
||||
|
||||
// Firefox 3.6 only I believe
|
||||
&::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
// IE7 has some default padding on button controls
|
||||
*padding-top: 3px;
|
||||
*padding-bottom: 3px;
|
||||
|
||||
&.btn-large {
|
||||
*padding-top: 7px;
|
||||
*padding-bottom: 7px;
|
||||
}
|
||||
&.btn-small {
|
||||
*padding-top: 3px;
|
||||
*padding-bottom: 3px;
|
||||
}
|
||||
&.btn-mini {
|
||||
*padding-top: 1px;
|
||||
*padding-bottom: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Link buttons
|
||||
// --------------------------------------------------
|
||||
|
||||
// Make a button look and behave like a link
|
||||
.btn-link,
|
||||
.btn-link:active,
|
||||
.btn-link[disabled] {
|
||||
background-color: transparent;
|
||||
background-image: none;
|
||||
.box-shadow(none);
|
||||
}
|
||||
.btn-link {
|
||||
border-color: transparent;
|
||||
cursor: pointer;
|
||||
color: @linkColor;
|
||||
.border-radius(0);
|
||||
}
|
||||
.btn-link:hover,
|
||||
.btn-link:focus {
|
||||
color: @linkColorHover;
|
||||
text-decoration: underline;
|
||||
background-color: transparent;
|
||||
}
|
||||
.btn-link[disabled]:hover,
|
||||
.btn-link[disabled]:focus {
|
||||
color: @grayDark;
|
||||
text-decoration: none;
|
||||
}
|
||||
158
src/UI/Content/Bootstrap/carousel.less
Normal file
158
src/UI/Content/Bootstrap/carousel.less
Normal file
@@ -0,0 +1,158 @@
|
||||
//
|
||||
// Carousel
|
||||
// --------------------------------------------------
|
||||
|
||||
|
||||
.carousel {
|
||||
position: relative;
|
||||
margin-bottom: @baseLineHeight;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.carousel-inner {
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.carousel-inner {
|
||||
|
||||
> .item {
|
||||
display: none;
|
||||
position: relative;
|
||||
.transition(.6s ease-in-out left);
|
||||
|
||||
// Account for jankitude on images
|
||||
> img,
|
||||
> a > img {
|
||||
display: block;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
|
||||
> .active,
|
||||
> .next,
|
||||
> .prev { display: block; }
|
||||
|
||||
> .active {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
> .next,
|
||||
> .prev {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
> .next {
|
||||
left: 100%;
|
||||
}
|
||||
> .prev {
|
||||
left: -100%;
|
||||
}
|
||||
> .next.left,
|
||||
> .prev.right {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
> .active.left {
|
||||
left: -100%;
|
||||
}
|
||||
> .active.right {
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Left/right controls for nav
|
||||
// ---------------------------
|
||||
|
||||
.carousel-control {
|
||||
position: absolute;
|
||||
top: 40%;
|
||||
left: 15px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin-top: -20px;
|
||||
font-size: 60px;
|
||||
font-weight: 100;
|
||||
line-height: 30px;
|
||||
color: @white;
|
||||
text-align: center;
|
||||
background: @grayDarker;
|
||||
border: 3px solid @white;
|
||||
.border-radius(23px);
|
||||
.opacity(50);
|
||||
|
||||
// we can't have this transition here
|
||||
// because webkit cancels the carousel
|
||||
// animation if you trip this while
|
||||
// in the middle of another animation
|
||||
// ;_;
|
||||
// .transition(opacity .2s linear);
|
||||
|
||||
// Reposition the right one
|
||||
&.right {
|
||||
left: auto;
|
||||
right: 15px;
|
||||
}
|
||||
|
||||
// Hover/focus state
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: @white;
|
||||
text-decoration: none;
|
||||
.opacity(90);
|
||||
}
|
||||
}
|
||||
|
||||
// Carousel indicator pips
|
||||
// -----------------------------
|
||||
.carousel-indicators {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 15px;
|
||||
z-index: 5;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
|
||||
li {
|
||||
display: block;
|
||||
float: left;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin-left: 5px;
|
||||
text-indent: -999px;
|
||||
background-color: #ccc;
|
||||
background-color: rgba(255,255,255,.25);
|
||||
border-radius: 5px;
|
||||
}
|
||||
.active {
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
// Caption for text below images
|
||||
// -----------------------------
|
||||
|
||||
.carousel-caption {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding: 15px;
|
||||
background: @grayDark;
|
||||
background: rgba(0,0,0,.75);
|
||||
}
|
||||
.carousel-caption h4,
|
||||
.carousel-caption p {
|
||||
color: @white;
|
||||
line-height: @baseLineHeight;
|
||||
}
|
||||
.carousel-caption h4 {
|
||||
margin: 0 0 5px;
|
||||
}
|
||||
.carousel-caption p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
32
src/UI/Content/Bootstrap/close.less
Normal file
32
src/UI/Content/Bootstrap/close.less
Normal file
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// Close icons
|
||||
// --------------------------------------------------
|
||||
|
||||
|
||||
.close {
|
||||
float: right;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
line-height: @baseLineHeight;
|
||||
color: @black;
|
||||
text-shadow: 0 1px 0 rgba(255,255,255,1);
|
||||
.opacity(20);
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: @black;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
.opacity(40);
|
||||
}
|
||||
}
|
||||
|
||||
// Additional properties for button version
|
||||
// iOS requires the button element instead of an anchor tag.
|
||||
// If you want the anchor version, it requires `href="#"`.
|
||||
button.close {
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
61
src/UI/Content/Bootstrap/code.less
Normal file
61
src/UI/Content/Bootstrap/code.less
Normal file
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// Code (inline and blocK)
|
||||
// --------------------------------------------------
|
||||
|
||||
|
||||
// Inline and block code styles
|
||||
code,
|
||||
pre {
|
||||
padding: 0 3px 2px;
|
||||
#font > #family > .monospace;
|
||||
font-size: @baseFontSize - 2;
|
||||
color: @grayDark;
|
||||
.border-radius(3px);
|
||||
}
|
||||
|
||||
// Inline code
|
||||
code {
|
||||
padding: 2px 4px;
|
||||
color: #d14;
|
||||
background-color: #f7f7f9;
|
||||
border: 1px solid #e1e1e8;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
// Blocks of code
|
||||
pre {
|
||||
display: block;
|
||||
padding: (@baseLineHeight - 1) / 2;
|
||||
margin: 0 0 @baseLineHeight / 2;
|
||||
font-size: @baseFontSize - 1; // 14px to 13px
|
||||
line-height: @baseLineHeight;
|
||||
word-break: break-all;
|
||||
word-wrap: break-word;
|
||||
white-space: pre;
|
||||
white-space: pre-wrap;
|
||||
background-color: #f5f5f5;
|
||||
border: 1px solid #ccc; // fallback for IE7-8
|
||||
border: 1px solid rgba(0,0,0,.15);
|
||||
.border-radius(@baseBorderRadius);
|
||||
|
||||
// Make prettyprint styles more spaced out for readability
|
||||
&.prettyprint {
|
||||
margin-bottom: @baseLineHeight;
|
||||
}
|
||||
|
||||
// Account for some code outputs that place code tags in pre tags
|
||||
code {
|
||||
padding: 0;
|
||||
color: inherit;
|
||||
white-space: pre;
|
||||
white-space: pre-wrap;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Enable scrollable blocks of code
|
||||
.pre-scrollable {
|
||||
max-height: 340px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
22
src/UI/Content/Bootstrap/component-animations.less
Normal file
22
src/UI/Content/Bootstrap/component-animations.less
Normal file
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// Component animations
|
||||
// --------------------------------------------------
|
||||
|
||||
|
||||
.fade {
|
||||
opacity: 0;
|
||||
.transition(opacity .15s linear);
|
||||
&.in {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.collapse {
|
||||
position: relative;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
.transition(height .35s ease);
|
||||
&.in {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
248
src/UI/Content/Bootstrap/dropdowns.less
Normal file
248
src/UI/Content/Bootstrap/dropdowns.less
Normal file
@@ -0,0 +1,248 @@
|
||||
//
|
||||
// Dropdown menus
|
||||
// --------------------------------------------------
|
||||
|
||||
|
||||
// Use the .menu class on any <li> element within the topbar or ul.tabs and you'll get some superfancy dropdowns
|
||||
.dropup,
|
||||
.dropdown {
|
||||
position: relative;
|
||||
}
|
||||
.dropdown-toggle {
|
||||
// The caret makes the toggle a bit too tall in IE7
|
||||
*margin-bottom: -3px;
|
||||
}
|
||||
.dropdown-toggle:active,
|
||||
.open .dropdown-toggle {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
// Dropdown arrow/caret
|
||||
// --------------------
|
||||
.caret {
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
vertical-align: top;
|
||||
border-top: 4px solid @black;
|
||||
border-right: 4px solid transparent;
|
||||
border-left: 4px solid transparent;
|
||||
content: "";
|
||||
}
|
||||
|
||||
// Place the caret
|
||||
.dropdown .caret {
|
||||
margin-top: 8px;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
// The dropdown menu (ul)
|
||||
// ----------------------
|
||||
.dropdown-menu {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
z-index: @zindexDropdown;
|
||||
display: none; // none by default, but block on "open" of the menu
|
||||
float: left;
|
||||
min-width: 160px;
|
||||
padding: 5px 0;
|
||||
margin: 2px 0 0; // override default ul
|
||||
list-style: none;
|
||||
background-color: @dropdownBackground;
|
||||
border: 1px solid #ccc; // Fallback for IE7-8
|
||||
border: 1px solid @dropdownBorder;
|
||||
*border-right-width: 2px;
|
||||
*border-bottom-width: 2px;
|
||||
.border-radius(6px);
|
||||
.box-shadow(0 5px 10px rgba(0,0,0,.2));
|
||||
-webkit-background-clip: padding-box;
|
||||
-moz-background-clip: padding;
|
||||
background-clip: padding-box;
|
||||
|
||||
// Aligns the dropdown menu to right
|
||||
&.pull-right {
|
||||
right: 0;
|
||||
left: auto;
|
||||
}
|
||||
|
||||
// Dividers (basically an hr) within the dropdown
|
||||
.divider {
|
||||
.nav-divider(@dropdownDividerTop, @dropdownDividerBottom);
|
||||
}
|
||||
|
||||
// Links within the dropdown menu
|
||||
> li > a {
|
||||
display: block;
|
||||
padding: 3px 20px;
|
||||
clear: both;
|
||||
font-weight: normal;
|
||||
line-height: @baseLineHeight;
|
||||
color: @dropdownLinkColor;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
// Hover/Focus state
|
||||
// -----------
|
||||
.dropdown-menu > li > a:hover,
|
||||
.dropdown-menu > li > a:focus,
|
||||
.dropdown-submenu:hover > a,
|
||||
.dropdown-submenu:focus > a {
|
||||
text-decoration: none;
|
||||
color: @dropdownLinkColorHover;
|
||||
#gradient > .vertical(@dropdownLinkBackgroundHover, darken(@dropdownLinkBackgroundHover, 5%));
|
||||
}
|
||||
|
||||
// Active state
|
||||
// ------------
|
||||
.dropdown-menu > .active > a,
|
||||
.dropdown-menu > .active > a:hover,
|
||||
.dropdown-menu > .active > a:focus {
|
||||
color: @dropdownLinkColorActive;
|
||||
text-decoration: none;
|
||||
outline: 0;
|
||||
#gradient > .vertical(@dropdownLinkBackgroundActive, darken(@dropdownLinkBackgroundActive, 5%));
|
||||
}
|
||||
|
||||
// Disabled state
|
||||
// --------------
|
||||
// Gray out text and ensure the hover/focus state remains gray
|
||||
.dropdown-menu > .disabled > a,
|
||||
.dropdown-menu > .disabled > a:hover,
|
||||
.dropdown-menu > .disabled > a:focus {
|
||||
color: @grayLight;
|
||||
}
|
||||
// Nuke hover/focus effects
|
||||
.dropdown-menu > .disabled > a:hover,
|
||||
.dropdown-menu > .disabled > a:focus {
|
||||
text-decoration: none;
|
||||
background-color: transparent;
|
||||
background-image: none; // Remove CSS gradient
|
||||
.reset-filter();
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
// Open state for the dropdown
|
||||
// ---------------------------
|
||||
.open {
|
||||
// IE7's z-index only goes to the nearest positioned ancestor, which would
|
||||
// make the menu appear below buttons that appeared later on the page
|
||||
*z-index: @zindexDropdown;
|
||||
|
||||
& > .dropdown-menu {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
// Backdrop to catch body clicks on mobile, etc.
|
||||
// ---------------------------
|
||||
.dropdown-backdrop {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
top: 0;
|
||||
z-index: @zindexDropdown - 10;
|
||||
}
|
||||
|
||||
// Right aligned dropdowns
|
||||
// ---------------------------
|
||||
.pull-right > .dropdown-menu {
|
||||
right: 0;
|
||||
left: auto;
|
||||
}
|
||||
|
||||
// Allow for dropdowns to go bottom up (aka, dropup-menu)
|
||||
// ------------------------------------------------------
|
||||
// Just add .dropup after the standard .dropdown class and you're set, bro.
|
||||
// TODO: abstract this so that the navbar fixed styles are not placed here?
|
||||
.dropup,
|
||||
.navbar-fixed-bottom .dropdown {
|
||||
// Reverse the caret
|
||||
.caret {
|
||||
border-top: 0;
|
||||
border-bottom: 4px solid @black;
|
||||
content: "";
|
||||
}
|
||||
// Different positioning for bottom up menu
|
||||
.dropdown-menu {
|
||||
top: auto;
|
||||
bottom: 100%;
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
// Sub menus
|
||||
// ---------------------------
|
||||
.dropdown-submenu {
|
||||
position: relative;
|
||||
}
|
||||
// Default dropdowns
|
||||
.dropdown-submenu > .dropdown-menu {
|
||||
top: 0;
|
||||
left: 100%;
|
||||
margin-top: -6px;
|
||||
margin-left: -1px;
|
||||
.border-radius(0 6px 6px 6px);
|
||||
}
|
||||
.dropdown-submenu:hover > .dropdown-menu {
|
||||
display: block;
|
||||
}
|
||||
|
||||
// Dropups
|
||||
.dropup .dropdown-submenu > .dropdown-menu {
|
||||
top: auto;
|
||||
bottom: 0;
|
||||
margin-top: 0;
|
||||
margin-bottom: -2px;
|
||||
.border-radius(5px 5px 5px 0);
|
||||
}
|
||||
|
||||
// Caret to indicate there is a submenu
|
||||
.dropdown-submenu > a:after {
|
||||
display: block;
|
||||
content: " ";
|
||||
float: right;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-color: transparent;
|
||||
border-style: solid;
|
||||
border-width: 5px 0 5px 5px;
|
||||
border-left-color: darken(@dropdownBackground, 20%);
|
||||
margin-top: 5px;
|
||||
margin-right: -10px;
|
||||
}
|
||||
.dropdown-submenu:hover > a:after {
|
||||
border-left-color: @dropdownLinkColorHover;
|
||||
}
|
||||
|
||||
// Left aligned submenus
|
||||
.dropdown-submenu.pull-left {
|
||||
// Undo the float
|
||||
// Yes, this is awkward since .pull-left adds a float, but it sticks to our conventions elsewhere.
|
||||
float: none;
|
||||
|
||||
// Positioning the submenu
|
||||
> .dropdown-menu {
|
||||
left: -100%;
|
||||
margin-left: 10px;
|
||||
.border-radius(6px 0 6px 6px);
|
||||
}
|
||||
}
|
||||
|
||||
// Tweak nav headers
|
||||
// -----------------
|
||||
// Increase padding from 15px to 20px on sides
|
||||
.dropdown .dropdown-menu .nav-header {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
// Typeahead
|
||||
// ---------
|
||||
.typeahead {
|
||||
z-index: 1051;
|
||||
margin-top: 2px; // give it some space to breathe
|
||||
.border-radius(@baseBorderRadius);
|
||||
}
|
||||
690
src/UI/Content/Bootstrap/forms.less
Normal file
690
src/UI/Content/Bootstrap/forms.less
Normal file
@@ -0,0 +1,690 @@
|
||||
//
|
||||
// Forms
|
||||
// --------------------------------------------------
|
||||
|
||||
|
||||
// GENERAL STYLES
|
||||
// --------------
|
||||
|
||||
// Make all forms have space below them
|
||||
form {
|
||||
margin: 0 0 @baseLineHeight;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
// Groups of fields with labels on top (legends)
|
||||
legend {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin-bottom: @baseLineHeight;
|
||||
font-size: @baseFontSize * 1.5;
|
||||
line-height: @baseLineHeight * 2;
|
||||
color: @grayDark;
|
||||
border: 0;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
|
||||
// Small
|
||||
small {
|
||||
font-size: @baseLineHeight * .75;
|
||||
color: @grayLight;
|
||||
}
|
||||
}
|
||||
|
||||
// Set font for forms
|
||||
label,
|
||||
input,
|
||||
button,
|
||||
select,
|
||||
textarea {
|
||||
#font > .shorthand(@baseFontSize,normal,@baseLineHeight); // Set size, weight, line-height here
|
||||
}
|
||||
input,
|
||||
button,
|
||||
select,
|
||||
textarea {
|
||||
font-family: @baseFontFamily; // And only set font-family here for those that need it (note the missing label element)
|
||||
}
|
||||
|
||||
// Identify controls by their labels
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
// Form controls
|
||||
// -------------------------
|
||||
|
||||
// Shared size and type resets
|
||||
select,
|
||||
textarea,
|
||||
input[type="text"],
|
||||
input[type="password"],
|
||||
input[type="datetime"],
|
||||
input[type="datetime-local"],
|
||||
input[type="date"],
|
||||
input[type="month"],
|
||||
input[type="time"],
|
||||
input[type="week"],
|
||||
input[type="number"],
|
||||
input[type="email"],
|
||||
input[type="url"],
|
||||
input[type="search"],
|
||||
input[type="tel"],
|
||||
input[type="color"],
|
||||
.uneditable-input {
|
||||
display: inline-block;
|
||||
height: @baseLineHeight;
|
||||
padding: 4px 6px;
|
||||
margin-bottom: @baseLineHeight / 2;
|
||||
font-size: @baseFontSize;
|
||||
line-height: @baseLineHeight;
|
||||
color: @gray;
|
||||
.border-radius(@inputBorderRadius);
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
// Reset appearance properties for textual inputs and textarea
|
||||
// Declare width for legacy (can't be on input[type=*] selectors or it's too specific)
|
||||
input,
|
||||
textarea,
|
||||
.uneditable-input {
|
||||
width: 206px; // plus 12px padding and 2px border
|
||||
}
|
||||
// Reset height since textareas have rows
|
||||
textarea {
|
||||
height: auto;
|
||||
}
|
||||
// Everything else
|
||||
textarea,
|
||||
input[type="text"],
|
||||
input[type="password"],
|
||||
input[type="datetime"],
|
||||
input[type="datetime-local"],
|
||||
input[type="date"],
|
||||
input[type="month"],
|
||||
input[type="time"],
|
||||
input[type="week"],
|
||||
input[type="number"],
|
||||
input[type="email"],
|
||||
input[type="url"],
|
||||
input[type="search"],
|
||||
input[type="tel"],
|
||||
input[type="color"],
|
||||
.uneditable-input {
|
||||
background-color: @inputBackground;
|
||||
border: 1px solid @inputBorder;
|
||||
.box-shadow(inset 0 1px 1px rgba(0,0,0,.075));
|
||||
.transition(~"border linear .2s, box-shadow linear .2s");
|
||||
|
||||
// Focus state
|
||||
&:focus {
|
||||
border-color: rgba(82,168,236,.8);
|
||||
outline: 0;
|
||||
outline: thin dotted \9; /* IE6-9 */
|
||||
.box-shadow(~"inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6)");
|
||||
}
|
||||
}
|
||||
|
||||
// Position radios and checkboxes better
|
||||
input[type="radio"],
|
||||
input[type="checkbox"] {
|
||||
margin: 4px 0 0;
|
||||
*margin-top: 0; /* IE7 */
|
||||
margin-top: 1px \9; /* IE8-9 */
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
// Reset width of input images, buttons, radios, checkboxes
|
||||
input[type="file"],
|
||||
input[type="image"],
|
||||
input[type="submit"],
|
||||
input[type="reset"],
|
||||
input[type="button"],
|
||||
input[type="radio"],
|
||||
input[type="checkbox"] {
|
||||
width: auto; // Override of generic input selector
|
||||
}
|
||||
|
||||
// Set the height of select and file controls to match text inputs
|
||||
select,
|
||||
input[type="file"] {
|
||||
height: @inputHeight; /* In IE7, the height of the select element cannot be changed by height, only font-size */
|
||||
*margin-top: 4px; /* For IE7, add top margin to align select with labels */
|
||||
line-height: @inputHeight;
|
||||
}
|
||||
|
||||
// Make select elements obey height by applying a border
|
||||
select {
|
||||
width: 220px; // default input width + 10px of padding that doesn't get applied
|
||||
border: 1px solid @inputBorder;
|
||||
background-color: @inputBackground; // Chrome on Linux and Mobile Safari need background-color
|
||||
}
|
||||
|
||||
// Make multiple select elements height not fixed
|
||||
select[multiple],
|
||||
select[size] {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
// Focus for select, file, radio, and checkbox
|
||||
select:focus,
|
||||
input[type="file"]:focus,
|
||||
input[type="radio"]:focus,
|
||||
input[type="checkbox"]:focus {
|
||||
.tab-focus();
|
||||
}
|
||||
|
||||
|
||||
// Uneditable inputs
|
||||
// -------------------------
|
||||
|
||||
// Make uneditable inputs look inactive
|
||||
.uneditable-input,
|
||||
.uneditable-textarea {
|
||||
color: @grayLight;
|
||||
background-color: darken(@inputBackground, 1%);
|
||||
border-color: @inputBorder;
|
||||
.box-shadow(inset 0 1px 2px rgba(0,0,0,.025));
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
// For text that needs to appear as an input but should not be an input
|
||||
.uneditable-input {
|
||||
overflow: hidden; // prevent text from wrapping, but still cut it off like an input does
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
// Make uneditable textareas behave like a textarea
|
||||
.uneditable-textarea {
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
|
||||
// Placeholder
|
||||
// -------------------------
|
||||
|
||||
// Placeholder text gets special styles because when browsers invalidate entire lines if it doesn't understand a selector
|
||||
input,
|
||||
textarea {
|
||||
.placeholder();
|
||||
}
|
||||
|
||||
|
||||
// CHECKBOXES & RADIOS
|
||||
// -------------------
|
||||
|
||||
// Indent the labels to position radios/checkboxes as hanging
|
||||
.radio,
|
||||
.checkbox {
|
||||
min-height: @baseLineHeight; // clear the floating input if there is no label text
|
||||
padding-left: 20px;
|
||||
}
|
||||
.radio input[type="radio"],
|
||||
.checkbox input[type="checkbox"] {
|
||||
float: left;
|
||||
margin-left: -20px;
|
||||
}
|
||||
|
||||
// Move the options list down to align with labels
|
||||
.controls > .radio:first-child,
|
||||
.controls > .checkbox:first-child {
|
||||
padding-top: 5px; // has to be padding because margin collaspes
|
||||
}
|
||||
|
||||
// Radios and checkboxes on same line
|
||||
// TODO v3: Convert .inline to .control-inline
|
||||
.radio.inline,
|
||||
.checkbox.inline {
|
||||
display: inline-block;
|
||||
padding-top: 5px;
|
||||
margin-bottom: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.radio.inline + .radio.inline,
|
||||
.checkbox.inline + .checkbox.inline {
|
||||
margin-left: 10px; // space out consecutive inline controls
|
||||
}
|
||||
|
||||
|
||||
|
||||
// INPUT SIZES
|
||||
// -----------
|
||||
|
||||
// General classes for quick sizes
|
||||
.input-mini { width: 60px; }
|
||||
.input-small { width: 90px; }
|
||||
.input-medium { width: 150px; }
|
||||
.input-large { width: 210px; }
|
||||
.input-xlarge { width: 270px; }
|
||||
.input-xxlarge { width: 530px; }
|
||||
|
||||
// Grid style input sizes
|
||||
input[class*="span"],
|
||||
select[class*="span"],
|
||||
textarea[class*="span"],
|
||||
.uneditable-input[class*="span"],
|
||||
// Redeclare since the fluid row class is more specific
|
||||
.row-fluid input[class*="span"],
|
||||
.row-fluid select[class*="span"],
|
||||
.row-fluid textarea[class*="span"],
|
||||
.row-fluid .uneditable-input[class*="span"] {
|
||||
float: none;
|
||||
margin-left: 0;
|
||||
}
|
||||
// Ensure input-prepend/append never wraps
|
||||
.input-append input[class*="span"],
|
||||
.input-append .uneditable-input[class*="span"],
|
||||
.input-prepend input[class*="span"],
|
||||
.input-prepend .uneditable-input[class*="span"],
|
||||
.row-fluid input[class*="span"],
|
||||
.row-fluid select[class*="span"],
|
||||
.row-fluid textarea[class*="span"],
|
||||
.row-fluid .uneditable-input[class*="span"],
|
||||
.row-fluid .input-prepend [class*="span"],
|
||||
.row-fluid .input-append [class*="span"] {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// GRID SIZING FOR INPUTS
|
||||
// ----------------------
|
||||
|
||||
// Grid sizes
|
||||
#grid > .input(@gridColumnWidth, @gridGutterWidth);
|
||||
|
||||
// Control row for multiple inputs per line
|
||||
.controls-row {
|
||||
.clearfix(); // Clear the float from controls
|
||||
}
|
||||
|
||||
// Float to collapse white-space for proper grid alignment
|
||||
.controls-row [class*="span"],
|
||||
// Redeclare the fluid grid collapse since we undo the float for inputs
|
||||
.row-fluid .controls-row [class*="span"] {
|
||||
float: left;
|
||||
}
|
||||
// Explicity set top padding on all checkboxes/radios, not just first-child
|
||||
.controls-row .checkbox[class*="span"],
|
||||
.controls-row .radio[class*="span"] {
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// DISABLED STATE
|
||||
// --------------
|
||||
|
||||
// Disabled and read-only inputs
|
||||
input[disabled],
|
||||
select[disabled],
|
||||
textarea[disabled],
|
||||
input[readonly],
|
||||
select[readonly],
|
||||
textarea[readonly] {
|
||||
cursor: not-allowed;
|
||||
background-color: @inputDisabledBackground;
|
||||
}
|
||||
// Explicitly reset the colors here
|
||||
input[type="radio"][disabled],
|
||||
input[type="checkbox"][disabled],
|
||||
input[type="radio"][readonly],
|
||||
input[type="checkbox"][readonly] {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// FORM FIELD FEEDBACK STATES
|
||||
// --------------------------
|
||||
|
||||
// Warning
|
||||
.control-group.warning {
|
||||
.formFieldState(@warningText, @warningText, @warningBackground);
|
||||
}
|
||||
// Error
|
||||
.control-group.error {
|
||||
.formFieldState(@errorText, @errorText, @errorBackground);
|
||||
}
|
||||
// Success
|
||||
.control-group.success {
|
||||
.formFieldState(@successText, @successText, @successBackground);
|
||||
}
|
||||
// Success
|
||||
.control-group.info {
|
||||
.formFieldState(@infoText, @infoText, @infoBackground);
|
||||
}
|
||||
|
||||
// HTML5 invalid states
|
||||
// Shares styles with the .control-group.error above
|
||||
input:focus:invalid,
|
||||
textarea:focus:invalid,
|
||||
select:focus:invalid {
|
||||
color: #b94a48;
|
||||
border-color: #ee5f5b;
|
||||
&:focus {
|
||||
border-color: darken(#ee5f5b, 10%);
|
||||
@shadow: 0 0 6px lighten(#ee5f5b, 20%);
|
||||
.box-shadow(@shadow);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// FORM ACTIONS
|
||||
// ------------
|
||||
|
||||
.form-actions {
|
||||
padding: (@baseLineHeight - 1) 20px @baseLineHeight;
|
||||
margin-top: @baseLineHeight;
|
||||
margin-bottom: @baseLineHeight;
|
||||
background-color: @formActionsBackground;
|
||||
border-top: 1px solid #e5e5e5;
|
||||
.clearfix(); // Adding clearfix to allow for .pull-right button containers
|
||||
}
|
||||
|
||||
|
||||
|
||||
// HELP TEXT
|
||||
// ---------
|
||||
|
||||
.help-block,
|
||||
.help-inline {
|
||||
color: lighten(@textColor, 15%); // lighten the text some for contrast
|
||||
}
|
||||
|
||||
.help-block {
|
||||
display: block; // account for any element using help-block
|
||||
margin-bottom: @baseLineHeight / 2;
|
||||
}
|
||||
|
||||
.help-inline {
|
||||
display: inline-block;
|
||||
.ie7-inline-block();
|
||||
vertical-align: middle;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// INPUT GROUPS
|
||||
// ------------
|
||||
|
||||
// Allow us to put symbols and text within the input field for a cleaner look
|
||||
.input-append,
|
||||
.input-prepend {
|
||||
display: inline-block;
|
||||
margin-bottom: @baseLineHeight / 2;
|
||||
vertical-align: middle;
|
||||
font-size: 0; // white space collapse hack
|
||||
white-space: nowrap; // Prevent span and input from separating
|
||||
|
||||
// Reset the white space collapse hack
|
||||
input,
|
||||
select,
|
||||
.uneditable-input,
|
||||
.dropdown-menu,
|
||||
.popover {
|
||||
font-size: @baseFontSize;
|
||||
}
|
||||
|
||||
input,
|
||||
select,
|
||||
.uneditable-input {
|
||||
position: relative; // placed here by default so that on :focus we can place the input above the .add-on for full border and box-shadow goodness
|
||||
margin-bottom: 0; // prevent bottom margin from screwing up alignment in stacked forms
|
||||
*margin-left: 0;
|
||||
vertical-align: top;
|
||||
.border-radius(0 @inputBorderRadius @inputBorderRadius 0);
|
||||
// Make input on top when focused so blue border and shadow always show
|
||||
&:focus {
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
.add-on {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
height: @baseLineHeight;
|
||||
min-width: 16px;
|
||||
padding: 4px 5px;
|
||||
font-size: @baseFontSize;
|
||||
font-weight: normal;
|
||||
line-height: @baseLineHeight;
|
||||
text-align: center;
|
||||
text-shadow: 0 1px 0 @white;
|
||||
background-color: @grayLighter;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
.add-on,
|
||||
.btn,
|
||||
.btn-group > .dropdown-toggle {
|
||||
vertical-align: top;
|
||||
.border-radius(0);
|
||||
}
|
||||
.active {
|
||||
background-color: lighten(@green, 30);
|
||||
border-color: @green;
|
||||
}
|
||||
}
|
||||
|
||||
.input-prepend {
|
||||
.add-on,
|
||||
.btn {
|
||||
margin-right: -1px;
|
||||
}
|
||||
.add-on:first-child,
|
||||
.btn:first-child {
|
||||
// FYI, `.btn:first-child` accounts for a button group that's prepended
|
||||
.border-radius(@inputBorderRadius 0 0 @inputBorderRadius);
|
||||
}
|
||||
}
|
||||
|
||||
.input-append {
|
||||
input,
|
||||
select,
|
||||
.uneditable-input {
|
||||
.border-radius(@inputBorderRadius 0 0 @inputBorderRadius);
|
||||
+ .btn-group .btn:last-child {
|
||||
.border-radius(0 @inputBorderRadius @inputBorderRadius 0);
|
||||
}
|
||||
}
|
||||
.add-on,
|
||||
.btn,
|
||||
.btn-group {
|
||||
margin-left: -1px;
|
||||
}
|
||||
.add-on:last-child,
|
||||
.btn:last-child,
|
||||
.btn-group:last-child > .dropdown-toggle {
|
||||
.border-radius(0 @inputBorderRadius @inputBorderRadius 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all border-radius for inputs with both prepend and append
|
||||
.input-prepend.input-append {
|
||||
input,
|
||||
select,
|
||||
.uneditable-input {
|
||||
.border-radius(0);
|
||||
+ .btn-group .btn {
|
||||
.border-radius(0 @inputBorderRadius @inputBorderRadius 0);
|
||||
}
|
||||
}
|
||||
.add-on:first-child,
|
||||
.btn:first-child {
|
||||
margin-right: -1px;
|
||||
.border-radius(@inputBorderRadius 0 0 @inputBorderRadius);
|
||||
}
|
||||
.add-on:last-child,
|
||||
.btn:last-child {
|
||||
margin-left: -1px;
|
||||
.border-radius(0 @inputBorderRadius @inputBorderRadius 0);
|
||||
}
|
||||
.btn-group:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// SEARCH FORM
|
||||
// -----------
|
||||
|
||||
input.search-query {
|
||||
padding-right: 14px;
|
||||
padding-right: 4px \9;
|
||||
padding-left: 14px;
|
||||
padding-left: 4px \9; /* IE7-8 doesn't have border-radius, so don't indent the padding */
|
||||
margin-bottom: 0; // Remove the default margin on all inputs
|
||||
.border-radius(15px);
|
||||
}
|
||||
|
||||
/* Allow for input prepend/append in search forms */
|
||||
.form-search .input-append .search-query,
|
||||
.form-search .input-prepend .search-query {
|
||||
.border-radius(0); // Override due to specificity
|
||||
}
|
||||
.form-search .input-append .search-query {
|
||||
.border-radius(14px 0 0 14px);
|
||||
}
|
||||
.form-search .input-append .btn {
|
||||
.border-radius(0 14px 14px 0);
|
||||
}
|
||||
.form-search .input-prepend .search-query {
|
||||
.border-radius(0 14px 14px 0);
|
||||
}
|
||||
.form-search .input-prepend .btn {
|
||||
.border-radius(14px 0 0 14px);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// HORIZONTAL & VERTICAL FORMS
|
||||
// ---------------------------
|
||||
|
||||
// Common properties
|
||||
// -----------------
|
||||
|
||||
.form-search,
|
||||
.form-inline,
|
||||
.form-horizontal {
|
||||
input,
|
||||
textarea,
|
||||
select,
|
||||
.help-inline,
|
||||
.uneditable-input,
|
||||
.input-prepend,
|
||||
.input-append {
|
||||
display: inline-block;
|
||||
.ie7-inline-block();
|
||||
margin-bottom: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
// Re-hide hidden elements due to specifity
|
||||
.hide {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.form-search label,
|
||||
.form-inline label,
|
||||
.form-search .btn-group,
|
||||
.form-inline .btn-group {
|
||||
display: inline-block;
|
||||
}
|
||||
// Remove margin for input-prepend/-append
|
||||
.form-search .input-append,
|
||||
.form-inline .input-append,
|
||||
.form-search .input-prepend,
|
||||
.form-inline .input-prepend {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
// Inline checkbox/radio labels (remove padding on left)
|
||||
.form-search .radio,
|
||||
.form-search .checkbox,
|
||||
.form-inline .radio,
|
||||
.form-inline .checkbox {
|
||||
padding-left: 0;
|
||||
margin-bottom: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
// Remove float and margin, set to inline-block
|
||||
.form-search .radio input[type="radio"],
|
||||
.form-search .checkbox input[type="checkbox"],
|
||||
.form-inline .radio input[type="radio"],
|
||||
.form-inline .checkbox input[type="checkbox"] {
|
||||
float: left;
|
||||
margin-right: 3px;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
|
||||
// Margin to space out fieldsets
|
||||
.control-group {
|
||||
margin-bottom: @baseLineHeight / 2;
|
||||
}
|
||||
|
||||
// Legend collapses margin, so next element is responsible for spacing
|
||||
legend + .control-group {
|
||||
margin-top: @baseLineHeight;
|
||||
-webkit-margin-top-collapse: separate;
|
||||
}
|
||||
|
||||
// Horizontal-specific styles
|
||||
// --------------------------
|
||||
|
||||
.form-horizontal {
|
||||
// Increase spacing between groups
|
||||
.control-group {
|
||||
margin-bottom: @baseLineHeight;
|
||||
.clearfix();
|
||||
}
|
||||
// Float the labels left
|
||||
.control-label {
|
||||
float: left;
|
||||
width: @horizontalComponentOffset - 20;
|
||||
padding-top: 5px;
|
||||
text-align: right;
|
||||
}
|
||||
// Move over all input controls and content
|
||||
.controls {
|
||||
// Super jank IE7 fix to ensure the inputs in .input-append and input-prepend
|
||||
// don't inherit the margin of the parent, in this case .controls
|
||||
*display: inline-block;
|
||||
*padding-left: 20px;
|
||||
margin-left: @horizontalComponentOffset;
|
||||
*margin-left: 0;
|
||||
&:first-child {
|
||||
*padding-left: @horizontalComponentOffset;
|
||||
}
|
||||
}
|
||||
// Remove bottom margin on block level help text since that's accounted for on .control-group
|
||||
.help-block {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
// And apply it only to .help-block instances that follow a form control
|
||||
input,
|
||||
select,
|
||||
textarea,
|
||||
.uneditable-input,
|
||||
.input-prepend,
|
||||
.input-append {
|
||||
+ .help-block {
|
||||
margin-top: @baseLineHeight / 2;
|
||||
}
|
||||
}
|
||||
// Move over buttons in .form-actions to align with .controls
|
||||
.form-actions {
|
||||
padding-left: @horizontalComponentOffset;
|
||||
}
|
||||
}
|
||||
21
src/UI/Content/Bootstrap/grid.less
Normal file
21
src/UI/Content/Bootstrap/grid.less
Normal file
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// Grid system
|
||||
// --------------------------------------------------
|
||||
|
||||
|
||||
// Fixed (940px)
|
||||
#grid > .core(@gridColumnWidth, @gridGutterWidth);
|
||||
|
||||
// Fluid (940px)
|
||||
#grid > .fluid(@fluidGridColumnWidth, @fluidGridGutterWidth);
|
||||
|
||||
// Reset utility classes due to specificity
|
||||
[class*="span"].hide,
|
||||
.row-fluid [class*="span"].hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[class*="span"].pull-right,
|
||||
.row-fluid [class*="span"].pull-right {
|
||||
float: right;
|
||||
}
|
||||
25
src/UI/Content/Bootstrap/hero-unit.less
Normal file
25
src/UI/Content/Bootstrap/hero-unit.less
Normal file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// Hero unit
|
||||
// --------------------------------------------------
|
||||
|
||||
|
||||
.hero-unit {
|
||||
padding: 60px;
|
||||
margin-bottom: 30px;
|
||||
font-size: 18px;
|
||||
font-weight: 200;
|
||||
line-height: @baseLineHeight * 1.5;
|
||||
color: @heroUnitLeadColor;
|
||||
background-color: @heroUnitBackground;
|
||||
.border-radius(6px);
|
||||
h1 {
|
||||
margin-bottom: 0;
|
||||
font-size: 60px;
|
||||
line-height: 1;
|
||||
color: @heroUnitHeadingColor;
|
||||
letter-spacing: -1px;
|
||||
}
|
||||
li {
|
||||
line-height: @baseLineHeight * 1.5; // Reset since we specify in type.less
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user