mirror of
https://github.com/chylex/TweetDuck.git
synced 2025-09-14 10:32:10 +02:00
Compare commits
42 Commits
Author | SHA1 | Date | |
---|---|---|---|
85d15b32e9 | |||
3d3b695c85 | |||
88b7cac298 | |||
c7efa631e1 | |||
aeb90cbb1a | |||
ae1c59847f | |||
651d9be57c | |||
eeb32db6fb | |||
daa0780644 | |||
8502f9105f | |||
16ced3d827 | |||
1c16187346 | |||
2fe058d9cb | |||
cefdadd53a | |||
c21c10df63 | |||
b4d359d30c | |||
1e8c62ac25 | |||
c578f36644 | |||
c973d0cff4 | |||
0c3d9ae46a | |||
a834e8b6a2 | |||
9f5580d983 | |||
e94e3cecf8 | |||
1991f7f50f | |||
9eb4e623e7 | |||
ad28d2279f | |||
1e3de31fc3 | |||
f85bd41b96 | |||
563124b68c | |||
63de08c635 | |||
8a0a215443 | |||
f1b7cd633e | |||
458eeeccda | |||
464e758b94 | |||
4c61047e9b | |||
1bf9e7fb56 | |||
ca69554f37 | |||
cf7029037e | |||
418388b0ab | |||
a0f8689d4f | |||
95007bdd26 | |||
ab91540deb |
4
.gitattributes
vendored
4
.gitattributes
vendored
@@ -1,5 +1,3 @@
|
|||||||
# Auto detect text files and perform LF normalization
|
# Auto detect text files and perform LF normalization
|
||||||
* text=auto
|
* text=auto eof=lf
|
||||||
|
|
||||||
*.txt text eof=lf
|
|
||||||
*.cs diff=csharp
|
*.cs diff=csharp
|
||||||
|
3
.github/FUNDING.yml
vendored
3
.github/FUNDING.yml
vendored
@@ -1,4 +1,3 @@
|
|||||||
# These are supported funding model platforms
|
github: chylex
|
||||||
|
|
||||||
patreon: chylex
|
patreon: chylex
|
||||||
ko_fi: chylex
|
ko_fi: chylex
|
||||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@@ -7,6 +7,10 @@ bld/*
|
|||||||
!bld/*.bat
|
!bld/*.bat
|
||||||
!bld/Resources
|
!bld/Resources
|
||||||
|
|
||||||
|
# Rider
|
||||||
|
.idea/.idea.TweetDuck/.idea/dictionaries
|
||||||
|
.idea/.idea.TweetDuck/.idea/misc.xml
|
||||||
|
|
||||||
# User-specific files
|
# User-specific files
|
||||||
*.suo
|
*.suo
|
||||||
*.user
|
*.user
|
||||||
|
8
.idea/.idea.TweetDuck/.idea/.gitignore
generated
vendored
Normal file
8
.idea/.idea.TweetDuck/.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Rider ignored files
|
||||||
|
/projectSettingsUpdater.xml
|
||||||
|
/modules.xml
|
||||||
|
/contentModel.xml
|
||||||
|
/.idea.TweetDuck.iml
|
169
.idea/.idea.TweetDuck/.idea/codeStyles/Project.xml
generated
Normal file
169
.idea/.idea.TweetDuck/.idea/codeStyles/Project.xml
generated
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<code_scheme name="Project" version="173">
|
||||||
|
<option name="AUTODETECT_INDENTS" value="false" />
|
||||||
|
<option name="LINE_SEPARATOR" value=" " />
|
||||||
|
<option name="RIGHT_MARGIN" value="999" />
|
||||||
|
<option name="FORMATTER_TAGS_ENABLED" value="true" />
|
||||||
|
<CssCodeStyleSettings>
|
||||||
|
<option name="HEX_COLOR_LOWER_CASE" value="true" />
|
||||||
|
</CssCodeStyleSettings>
|
||||||
|
<HTMLCodeStyleSettings>
|
||||||
|
<option name="HTML_ALIGN_TEXT" value="true" />
|
||||||
|
<option name="HTML_SPACE_INSIDE_EMPTY_TAG" value="true" />
|
||||||
|
<option name="HTML_ELEMENTS_TO_REMOVE_NEW_LINE_BEFORE" value="" />
|
||||||
|
<option name="HTML_DO_NOT_INDENT_CHILDREN_OF" value="" />
|
||||||
|
</HTMLCodeStyleSettings>
|
||||||
|
<JSCodeStyleSettings version="0">
|
||||||
|
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
||||||
|
<option name="SPACE_WITHIN_ARRAY_INITIALIZER_BRACKETS" value="true" />
|
||||||
|
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_CLASS_LBRACE" value="false" />
|
||||||
|
<option name="FORCE_QUOTE_STYlE" value="true" />
|
||||||
|
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
|
||||||
|
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
||||||
|
<option name="USE_CHAINED_CALLS_GROUP_INDENTS" value="true" />
|
||||||
|
<option name="SPACE_BEFORE_ASYNC_ARROW_LPAREN" value="false" />
|
||||||
|
</JSCodeStyleSettings>
|
||||||
|
<JSON>
|
||||||
|
<option name="OBJECT_WRAPPING" value="5" />
|
||||||
|
<option name="ARRAY_WRAPPING" value="5" />
|
||||||
|
</JSON>
|
||||||
|
<Properties>
|
||||||
|
<option name="KEEP_BLANK_LINES" value="true" />
|
||||||
|
</Properties>
|
||||||
|
<TypeScriptCodeStyleSettings version="0">
|
||||||
|
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_CLASS_LBRACE" value="false" />
|
||||||
|
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
|
||||||
|
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
||||||
|
<option name="USE_CHAINED_CALLS_GROUP_INDENTS" value="true" />
|
||||||
|
<option name="SPACE_BEFORE_ASYNC_ARROW_LPAREN" value="false" />
|
||||||
|
</TypeScriptCodeStyleSettings>
|
||||||
|
<XML>
|
||||||
|
<option name="XML_SPACE_INSIDE_EMPTY_TAG" value="true" />
|
||||||
|
</XML>
|
||||||
|
<codeStyleSettings language="CSS">
|
||||||
|
<indentOptions>
|
||||||
|
<option name="INDENT_SIZE" value="2" />
|
||||||
|
<option name="TAB_SIZE" value="2" />
|
||||||
|
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="HTML">
|
||||||
|
<option name="LINE_COMMENT_AT_FIRST_COLUMN" value="false" />
|
||||||
|
<option name="BLOCK_COMMENT_AT_FIRST_COLUMN" value="false" />
|
||||||
|
<indentOptions>
|
||||||
|
<option name="INDENT_SIZE" value="2" />
|
||||||
|
<option name="TAB_SIZE" value="2" />
|
||||||
|
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="JSON">
|
||||||
|
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
|
||||||
|
<option name="SPACE_WITHIN_BRACKETS" value="true" />
|
||||||
|
<option name="SPACE_WITHIN_BRACES" value="true" />
|
||||||
|
<indentOptions>
|
||||||
|
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="JavaScript">
|
||||||
|
<option name="ELSE_ON_NEW_LINE" value="true" />
|
||||||
|
<option name="ALIGN_MULTILINE_TERNARY_OPERATION" value="true" />
|
||||||
|
<option name="ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION" value="true" />
|
||||||
|
<option name="SPACE_BEFORE_WHILE_PARENTHESES" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_FOR_PARENTHESES" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_CATCH_PARENTHESES" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_SWITCH_PARENTHESES" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_METHOD_LBRACE" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_IF_LBRACE" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_ELSE_LBRACE" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_WHILE_LBRACE" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_FOR_LBRACE" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_DO_LBRACE" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_SWITCH_LBRACE" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_TRY_LBRACE" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_CATCH_LBRACE" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_FINALLY_LBRACE" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_ELSE_KEYWORD" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_WHILE_KEYWORD" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_CATCH_KEYWORD" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_FINALLY_KEYWORD" value="false" />
|
||||||
|
<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" />
|
||||||
|
<indentOptions>
|
||||||
|
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="LESS">
|
||||||
|
<indentOptions>
|
||||||
|
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="SASS">
|
||||||
|
<indentOptions>
|
||||||
|
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="SCSS">
|
||||||
|
<indentOptions>
|
||||||
|
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="SQL">
|
||||||
|
<indentOptions>
|
||||||
|
<option name="USE_TAB_CHARACTER" value="true" />
|
||||||
|
<option name="SMART_TABS" value="true" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="TOML">
|
||||||
|
<indentOptions>
|
||||||
|
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="TypeScript">
|
||||||
|
<option name="ELSE_ON_NEW_LINE" value="true" />
|
||||||
|
<option name="ALIGN_MULTILINE_TERNARY_OPERATION" value="true" />
|
||||||
|
<option name="ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION" value="true" />
|
||||||
|
<option name="SPACE_BEFORE_WHILE_PARENTHESES" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_FOR_PARENTHESES" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_CATCH_PARENTHESES" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_SWITCH_PARENTHESES" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_METHOD_LBRACE" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_IF_LBRACE" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_ELSE_LBRACE" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_WHILE_LBRACE" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_FOR_LBRACE" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_DO_LBRACE" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_SWITCH_LBRACE" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_TRY_LBRACE" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_CATCH_LBRACE" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_FINALLY_LBRACE" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_ELSE_KEYWORD" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_WHILE_KEYWORD" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_CATCH_KEYWORD" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_FINALLY_KEYWORD" value="false" />
|
||||||
|
<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" />
|
||||||
|
<indentOptions>
|
||||||
|
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="XML">
|
||||||
|
<indentOptions>
|
||||||
|
<option name="INDENT_SIZE" value="2" />
|
||||||
|
<option name="TAB_SIZE" value="2" />
|
||||||
|
<option name="SMART_TABS" value="true" />
|
||||||
|
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="yaml">
|
||||||
|
<indentOptions>
|
||||||
|
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
|
</code_scheme>
|
||||||
|
</component>
|
5
.idea/.idea.TweetDuck/.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/.idea.TweetDuck/.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<state>
|
||||||
|
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||||
|
</state>
|
||||||
|
</component>
|
6
.idea/.idea.TweetDuck/.idea/encodings.xml
generated
Normal file
6
.idea/.idea.TweetDuck/.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding" defaultCharsetForPropertiesFiles="UTF-8">
|
||||||
|
<file url="PROJECT" charset="UTF-8" />
|
||||||
|
</component>
|
||||||
|
</project>
|
8
.idea/.idea.TweetDuck/.idea/indexLayout.xml
generated
Normal file
8
.idea/.idea.TweetDuck/.idea/indexLayout.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ContentModelUserStore">
|
||||||
|
<attachedFolders />
|
||||||
|
<explicitIncludes />
|
||||||
|
<explicitExcludes />
|
||||||
|
</component>
|
||||||
|
</project>
|
771
.idea/.idea.TweetDuck/.idea/inspectionProfiles/Project.xml
generated
Normal file
771
.idea/.idea.TweetDuck/.idea/inspectionProfiles/Project.xml
generated
Normal file
@@ -0,0 +1,771 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project" />
|
||||||
|
<inspection_tool class="AccessToNonThreadSafeStaticFieldFromInstance" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="nonThreadSafeClasses">
|
||||||
|
<value />
|
||||||
|
</option>
|
||||||
|
<option name="nonThreadSafeTypes" value="" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="AccessToStaticFieldLockedOnInstance" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="AddOperatorModifier" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="AmbiguousFieldAccess" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="AmbiguousMethodCall" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="AnonymousInnerClassMayBeStatic" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ArrayEquality" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="AssertEqualsCalledOnArray" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="AssertsWithoutMessagesTestNG" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="AssignmentOrReturnOfFieldWithMutableType" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="AssignmentToCatchBlockParameter" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="AssignmentToLambdaParameter" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="AssignmentToMethodParameter" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoreTransformationOfOriginalParameter" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="AssignmentToStaticFieldFromInstanceMethod" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="AssignmentUsedAsCondition" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="AutoBoxing" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoreAddedToCollection" value="false" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="AutoCloseableResource" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoredTypes" value="java.util.stream.Stream,java.util.stream.IntStream,java.util.stream.LongStream,java.util.stream.DoubleStream,net.minecraft.client.Minecraft,net.minecraft.client.MainWindow" />
|
||||||
|
<option name="METHOD_MATCHER_CONFIG" value="java.util.Formatter,format,java.io.Writer,append,com.google.common.base.Preconditions,checkNotNull,org.hibernate.Session,close,java.io.PrintWriter,printf,net.minecraft.client.MinecraftClient,getInstance|getWindow" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="AutoUnboxing" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="AwaitNotInLoop" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="AwaitWithoutCorrespondingSignal" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="BadOddness" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="BigDecimalEquals" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="BigDecimalLegacyMethod" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="BooleanExpressionMayBeConditional" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="CallToNativeMethodWhileLocked" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="CallableParameterUseCaseInTypeContextInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="CascadeStringReplacementInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="USE_SHORT_ARRAYS_SYNTAX" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="CastConflictsWithInstanceof" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="CastToIncompatibleInterface" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ChainedEquality" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ChannelResource" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="insideTryAllowed" value="false" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="ClassIndependentOfModule" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ClassLoaderInstantiation" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ClassMayBeInterface" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||||
|
<option name="reportClassesWithNonAbstractMethods" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="ClassMethodNameMatchesFieldNameInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="ClassNameDiffersFromFileName" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ClassNewInstance" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ClassOnlyUsedInOneModule" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ClassOnlyUsedInOnePackage" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ClassOverridesFieldOfSuperClassInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="REPORT_PRIVATE_REDEFINITION" value="false" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="ClassUnconnectedToPackage" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ClassWithOnlyPrivateConstructors" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="CloneableClassInSecureContext" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="CollectionContainsUrl" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="CollectionsFieldAccessReplaceableByMethodCall" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ComparableImplementedButEqualsNotOverridden" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ComparatorNotSerializable" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="CompareToUsesNonFinalVariable" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ComposeMissingKeys" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ConditionSignal" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ConditionalExpression" enabled="true" level="INFORMATION" enabled_by_default="true">
|
||||||
|
<option name="ignoreSimpleAssignmentsAndReturns" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="ConditionalExpressionWithIdenticalBranchesJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ConfusingElse" enabled="false" level="WEAK WARNING" enabled_by_default="false">
|
||||||
|
<option name="reportWhenNoStatementFollow" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="ConfusingMainMethod" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ConfusingOctalEscape" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ConfusingPlusesOrMinusesJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ConstantJUnitAssertArgument" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ConstantMathCall" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ConstantTestNGAssertArgument" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ControlFlowStatementWithoutBraces" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ConvertJavadoc" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ConvertLambdaToReference" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ConvertOldAnnotations" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="CssConvertColorToHexInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="CssConvertColorToRgbInspection" enabled="true" level="INFORMATION" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="CssMissingSemicolon" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="CssReplaceWithShorthandUnsafely" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="CustomClassloader" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="CustomSecurityManager" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="CyclicClassDependency" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="CyclicPackageDependency" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="DateToString" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="DefaultNotLastCaseInSwitch" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="DisallowWritingIntoStaticPropertiesInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="DisconnectedForeachInstructionInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="DisjointPackage" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="DivideByZeroJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="DocumentWriteJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="DoubleBraceInitialization" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="DoubleCheckedLocking" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoreOnVolatileVariables" value="false" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="DriverManagerGetConnection" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="DuplicateBooleanBranch" enabled="true" level="WARNING" 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="ES6ConvertIndexedForToForOf" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ES6ConvertLetToConst" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ES6ConvertToForOf" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ES6ShorthandObjectProperty" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ES6TopLevelAwaitExpression" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="EmptyDirectory" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="EmptyStatementBody" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="m_reportEmptyBlocks" value="true" />
|
||||||
|
<option name="commentsAreContent" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="EmptySynchronizedStatement" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="EnumSwitchStatementWhichMissesCases" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoreSwitchStatementsWithDefault" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="EqualsCalledOnEnumConstant" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="EqualsHashCodeCalledOnUrl" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="EqualsUsesNonFinalVariable" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ErrorRethrown" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ExceptionNameDoesntEndWithException" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ExceptionPackage" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ExpectedExceptionNeverThrownTestNG" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ExplicitArgumentCanBeLambda" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ExtendsThread" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ExtendsThrowable" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ExternalizableWithSerializationMethods" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="FallthruInSwitchStatement" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="FieldAccessedSynchronizedAndUnsynchronized" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="countGettersAndSetters" value="false" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="FieldDeclarationSideOnly" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="FieldHidesSuperclassField" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="m_ignoreInvisibleFields" value="false" />
|
||||||
|
<option name="ignoreStaticFields" value="false" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="FieldMayBeStatic" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="Finalize" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoreTrivialFinalizers" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="FinalizeNotProtected" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="FixedTimeStartWithInspection" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="FloatingPointEquality" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="FoldInitializerAndIfToElvis" enabled="false" level="INFO" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="ForLoopThatDoesntUseLoopVariableJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ForgottenDebugOutputInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||||
|
<option name="configuration">
|
||||||
|
<list>
|
||||||
|
<option value="\Codeception\Util\Debug::debug" />
|
||||||
|
<option value="\Codeception\Util\Debug::pause" />
|
||||||
|
<option value="\Doctrine\Common\Util\Debug::dump" />
|
||||||
|
<option value="\Doctrine\Common\Util\Debug::export" />
|
||||||
|
<option value="\Illuminate\Support\Debug\Dumper::dump" />
|
||||||
|
<option value="\Symfony\Component\Debug\Debug::enable" />
|
||||||
|
<option value="\Symfony\Component\Debug\DebugClassLoader::enable" />
|
||||||
|
<option value="\Symfony\Component\Debug\ErrorHandler::register" />
|
||||||
|
<option value="\Symfony\Component\Debug\ExceptionHandler::register" />
|
||||||
|
<option value="\TYPO3\CMS\Core\Utility\DebugUtility::debug" />
|
||||||
|
<option value="\Zend\Debug\Debug::dump" />
|
||||||
|
<option value="\Zend\Di\Display\Console::export" />
|
||||||
|
<option value="dd" />
|
||||||
|
<option value="debug_print_backtrace" />
|
||||||
|
<option value="debug_zval_dump" />
|
||||||
|
<option value="dpm" />
|
||||||
|
<option value="dpq" />
|
||||||
|
<option value="dsm" />
|
||||||
|
<option value="dump" />
|
||||||
|
<option value="dvm" />
|
||||||
|
<option value="error_log" />
|
||||||
|
<option value="kpr" />
|
||||||
|
<option value="phpinfo" />
|
||||||
|
<option value="print_r" />
|
||||||
|
<option value="var_dump" />
|
||||||
|
<option value="var_export" />
|
||||||
|
<option value="wp_die" />
|
||||||
|
<option value="xdebug_break" />
|
||||||
|
<option value="xdebug_call_class" />
|
||||||
|
<option value="xdebug_call_file" />
|
||||||
|
<option value="xdebug_call_function" />
|
||||||
|
<option value="xdebug_call_line" />
|
||||||
|
<option value="xdebug_code_coverage_started" />
|
||||||
|
<option value="xdebug_debug_zval" />
|
||||||
|
<option value="xdebug_debug_zval_stdout" />
|
||||||
|
<option value="xdebug_dump_superglobals" />
|
||||||
|
<option value="xdebug_enable" />
|
||||||
|
<option value="xdebug_get_code_coverage" />
|
||||||
|
<option value="xdebug_get_collected_errors" />
|
||||||
|
<option value="xdebug_get_declared_vars" />
|
||||||
|
<option value="xdebug_get_function_stack" />
|
||||||
|
<option value="xdebug_get_headers" />
|
||||||
|
<option value="xdebug_get_monitored_functions" />
|
||||||
|
<option value="xdebug_get_profiler_filename" />
|
||||||
|
<option value="xdebug_get_stack_depth" />
|
||||||
|
<option value="xdebug_get_tracefile_name" />
|
||||||
|
<option value="xdebug_is_enabled" />
|
||||||
|
<option value="xdebug_memory_usage" />
|
||||||
|
<option value="xdebug_peak_memory_usage" />
|
||||||
|
<option value="xdebug_print_function_stack" />
|
||||||
|
<option value="xdebug_start_code_coverage" />
|
||||||
|
<option value="xdebug_start_error_collection" />
|
||||||
|
<option value="xdebug_start_function_monitor" />
|
||||||
|
<option value="xdebug_start_trace" />
|
||||||
|
<option value="xdebug_stop_code_coverage" />
|
||||||
|
<option value="xdebug_stop_error_collection" />
|
||||||
|
<option value="xdebug_stop_function_monitor" />
|
||||||
|
<option value="xdebug_stop_trace" />
|
||||||
|
<option value="xdebug_time_index" />
|
||||||
|
<option value="xdebug_var_dump" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
<option name="migratedIntoUserSpace" value="true" />
|
||||||
|
</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="0" />
|
||||||
|
<option name="m_maxLength" value="99" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="FunctionWithInconsistentReturnsJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="HashCodeUsesNonFinalVariable" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="HibernateResource" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="insideTryAllowed" value="false" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="HtmlMissingClosingTag" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="HtmlPresentationalElement" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="HtmlRequiredTitleAttribute" enabled="true" level="INFORMATION" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="IOResource" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoredTypesString" value="java.io.ByteArrayOutputStream,java.io.ByteArrayInputStream,java.io.StringBufferInputStream,java.io.CharArrayWriter,java.io.CharArrayReader,java.io.StringWriter,java.io.StringReader" />
|
||||||
|
<option name="insideTryAllowed" value="false" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="IfStatementWithIdenticalBranchesJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ImplicitDefaultCharsetUsage" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="InconsistentLanguageLevel" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="InconsistentLineSeparators" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="IncrementDecrementOperationEquivalentInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="InnerClassOnInterface" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="m_ignoreInnerInterfaces" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="InnerClassReferencedViaSubclass" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="InnerClassVariableHidesOuterClassVariable" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="m_ignoreInvisibleFields" value="false" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="InsertLiteralUnderscores" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="InstanceofCatchParameter" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="InstanceofIncompatibleInterface" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="InstanceofThis" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="InterfaceMayBeAnnotatedFunctional" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="IsEmptyFunctionUsageInspection" enabled="false" level="WARNING" enabled_by_default="false">
|
||||||
|
<option name="SUGGEST_TO_USE_COUNT_CHECK" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="IsNullFunctionUsageInspection" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="IteratorNextDoesNotThrowNoSuchElementException" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="JDBCExecuteWithNonConstantString" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="JDBCPrepareStatementWithNonConstantString" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="JDBCResource" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="insideTryAllowed" value="false" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="JNDIResource" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="insideTryAllowed" value="false" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="JSArrowFunctionBracesCanBeRemoved" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="JSClassNamingConvention" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="m_minLength" value="0" />
|
||||||
|
<option name="m_maxLength" value="99" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="JSConstructorReturnsPrimitive" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="JSEqualityComparisonWithCoercion.TS" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="mySeverity" value="Always" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="JSJoinVariableDeclarationAndAssignment" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="JSMissingSwitchBranches" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="JSNonASCIINames" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="myAllowOnlyAscii" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="JSNonStrictModeUsed" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="JSOctalInteger" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="myReportNonStrictEs5" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="JSUndeclaredVariable" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||||
|
<option name="myCheckGlobalDefinitions" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="JSUnusedGlobalSymbols" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="myReportUnusedDefinitions" value="true" />
|
||||||
|
<option name="myReportUnusedProperties" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="JUnitDatapoint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="JUnitRule" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="JUnitTestNG" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="JavadocHtmlLint" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="JoinDeclarationAndAssignmentJava" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="LambdaCanBeMethodCall" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="LengthOneStringInIndexOf" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="LengthOneStringsInConcatenation" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="LoadLibraryWithNonConstantString" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="LocalCanBeFinal" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="REPORT_VARIABLES" value="true" />
|
||||||
|
<option name="REPORT_PARAMETERS" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="LocalVariableDeclarationSideOnly" 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="0" />
|
||||||
|
<option name="m_maxLength" value="99" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="MalformedSetUpTearDown" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="MapReplaceableByEnumMap" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="MethodCallSideOnly" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="MethodMayBeStatic" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="m_onlyPrivateOrFinal" value="false" />
|
||||||
|
<option name="m_ignoreEmptyMethods" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="MethodMayBeSynchronized" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="MethodOverloadsParentMethod" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="reportIncompatibleParameters" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="MethodOverridesInaccessibleMethodOfSuper" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="MethodOverridesStaticMethod" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="MethodSideOnly" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="MisorderedAssertEqualsArgumentsTestNG" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="MisorderedAssertEqualsParameters" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="MisorderedModifiersInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="MissingOverrideAnnotation" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoreObjectMethods" value="false" />
|
||||||
|
<option name="ignoreAnonymousClassMethods" value="false" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="MissortedModifiers" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="m_requireAnnotationsFirst" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="MultipleTopLevelClassesInFile" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="NakedNotify" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="NegatedConditional" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="m_ignoreNegatedNullComparison" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="NestedAssignment" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="NestedClassSideOnly" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="NestedConditionalExpressionJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="NestedSwitchStatement" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="NestedSynchronizedStatement" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="NestedTernaryOperatorInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="NewExpressionSideOnly" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="NonBlockStatementBodyJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="NonExceptionNameEndsWithException" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="NonFinalClone" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="NonFinalFieldInEnum" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="NonFinalFieldOfException" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="NonFinalUtilityClass" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="NonReproducibleMathCall" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="NonSerializableFieldInSerializableClass" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignorableAnnotations">
|
||||||
|
<value />
|
||||||
|
</option>
|
||||||
|
<option name="ignoreAnonymousInnerClasses" value="false" />
|
||||||
|
<option name="superClassString" value="java.awt.Component" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="NonSerializableObjectBoundToHttpSession" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="NonSerializableObjectPassedToObjectStream" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="NonSerializableWithSerialVersionUIDField" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="NonSerializableWithSerializationMethods" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="NonShortCircuitBoolean" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="NonSynchronizedMethodOverridesSynchronizedMethod" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="NonThreadSafeLazyInitialization" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="NotOptimalIfConditionsInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="REPORT_DUPLICATE_CONDITIONS" value="false" />
|
||||||
|
<option name="SUGGEST_OPTIMIZING_CONDITIONS" value="false" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="NotifyCalledOnCondition" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="NotifyWithoutCorrespondingWait" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="NullThrown" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="NumericToString" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="OCInconsistentNaming" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ObjectAllocationIgnoredJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ObjectInstantiationInEqualsHashCode" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ObjectNotify" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ObjectToString" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ObsoleteCollection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoreRequiredObsoleteCollectionTypes" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="OctalAndDecimalIntegersMixed" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="OffsetOperationsInspection" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="OneTimeUseVariablesInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ALLOW_LONG_STATEMENTS" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="OverloadedVarargsMethod" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PackageInMultipleModules" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ParameterHidingMemberVariable" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="m_ignoreInvisibleFields" value="true" />
|
||||||
|
<option name="m_ignoreStaticMethodParametersHidingInstanceFields" value="true" />
|
||||||
|
<option name="m_ignoreForConstructors" value="true" />
|
||||||
|
<option name="m_ignoreForPropertySetters" value="true" />
|
||||||
|
<option name="m_ignoreForAbstractMethods" value="false" />
|
||||||
|
</inspection_tool>
|
||||||
|
<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="0" />
|
||||||
|
<option name="m_maxLength" value="99" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="ParameterizedParametersStaticCollection" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PhpAssignmentInConditionInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PhpAssignmentReplaceableWithOperatorAssignmentInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PhpAssignmentReplaceableWithPrefixExpressionInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PhpCSValidationInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false">
|
||||||
|
<option name="EXTENSIONS" value="php,js,css,inc" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PhpClassNamingConventionInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="m_minLength" value="0" />
|
||||||
|
<option name="m_maxLength" value="0" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PhpClosureCanBeConvertedToShortArrowFunctionInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PhpCompoundNamespaceDepthInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PhpConstantNamingConventionInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="m_minLength" value="0" />
|
||||||
|
<option name="m_maxLength" value="0" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PhpConstantReassignmentInspection" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PhpDivisionByZeroInspection" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PhpDocMissingThrowsInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||||
|
<option name="SKIP_ON_EMPTY_PHPDOC" value="false" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PhpFunctionNamingConventionInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="m_minLength" value="0" />
|
||||||
|
<option name="m_maxLength" value="0" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PhpInconsistentReturnPointsInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ALLOW_RETURN_NULL_IN_VOID" value="false" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PhpLongTypeFormInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PhpLoopCanBeConvertedToArrayFillInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PhpLoopCanBeConvertedToArrayFilterInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PhpLoopCanBeConvertedToArrayMapInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PhpMethodNamingConventionInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="m_minLength" value="0" />
|
||||||
|
<option name="m_maxLength" value="0" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PhpMethodOrClassCallIsNotCaseSensitiveInspection" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PhpMissingParentCallMagicInspection" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PhpMissingReturnTypeInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PhpMissingStrictTypesDeclarationInspection" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PhpMissingVisibilityInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PhpNewClassMissingParameterListInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PhpNonCanonicalElementsOrderInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PhpOverridingMethodVisibilityInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PhpPropertyNamingConventionInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="m_regex" value="[a-z][_a-z\d]*" />
|
||||||
|
<option name="m_minLength" value="0" />
|
||||||
|
<option name="m_maxLength" value="0" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PhpRedundantClosingTagInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="PhpSeparateElseIfInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PhpShortOpenTagInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PhpSingleStatementWithBracesInspection" enabled="false" level="INFORMATION" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="PhpStatementHasEmptyBodyInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="myCommentsCountAsContent" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PhpStatementWithoutBracesInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PhpStaticAsDynamicMethodCallInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||||
|
<option name="SHOW_FOR_MAGIC" value="false" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PhpTraditionalSyntaxArrayLiteralInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PhpTraitsUseListInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PhpUndefinedCallbackInspection" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PhpUndefinedClassConstantInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="DOWNGRADE_SEVERITY" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PhpUndefinedMethodInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="DOWNGRADE_SEVERITY" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PhpUnused" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||||
|
<option name="SHOW_UNUSED_BY_ENTRIES" value="false" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PhpUnusedParameterInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="DONT_REPORT_ANONYMOUS" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PhpVarUsageInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PhpVariableNamingConventionInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="m_minLength" value="0" />
|
||||||
|
<option name="m_maxLength" value="0" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PhpVariableVariableInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PointlessBitwiseExpressionJS" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="m_ignoreExpressionsContainingConstants" value="false" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="ProblematicVarargsMethodOverride" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ProblematicWhitespace" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PropertyCanBeStaticInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ProtectedMemberInFinalClass" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PublicFieldAccessedInSynchronizedContext" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PublicStaticArrayField" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PublicStaticCollectionField" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PyAugmentAssignmentInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PyClassicStyleClassInspection" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PyMissingTypeHintsInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||||
|
<option name="m_onlyWhenTypesAreKnown" value="false" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="RawTypeCanBeGeneric" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ReadObjectAndWriteObjectPrivate" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ReadObjectInitialization" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ReadResolveAndWriteReplaceProtected" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="RedundantElseClauseInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="RedundantImplements" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoreSerializable" value="true" />
|
||||||
|
<option name="ignoreCloneable" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="RedundantObjectTypeCheck" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="RedundantSuppression" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="IGNORE_ALL" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="ReferencingObjectsInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="RegExpOctalEscape" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ReplaceAssignmentWithOperatorAssignment" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoreLazyOperators" value="true" />
|
||||||
|
<option name="ignoreObscureOperators" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="ReplaceAssignmentWithOperatorAssignmentJS" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ReplaceCollectionCountWithSize" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ReplaceGuardClauseWithFunctionCall" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ReplaceStringFormatWithLiteral" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ReplaceSubstringWithDropLast" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ReplaceSubstringWithIndexingOperation" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ReplaceSubstringWithSubstringAfter" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ReplaceSubstringWithSubstringBefore" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ReplaceSubstringWithTake" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ResultOfObjectAllocationIgnored" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ResultSetIndexZero" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ReturnOfInnerClass" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ReuseOfLocalVariableJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="RsSimplifyBooleanExpression" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="RuntimeExec" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="RuntimeExecWithNonConstantString" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="SafeLock" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="SecurityAdvisoriesInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="optionConfiguration">
|
||||||
|
<list>
|
||||||
|
<option value="barryvdh/laravel-debugbar" />
|
||||||
|
<option value="behat/behat" />
|
||||||
|
<option value="brianium/paratest" />
|
||||||
|
<option value="codeception/codeception" />
|
||||||
|
<option value="codedungeon/phpunit-result-printer" />
|
||||||
|
<option value="composer/composer" />
|
||||||
|
<option value="doctrine/coding-standard" />
|
||||||
|
<option value="filp/whoops" />
|
||||||
|
<option value="friendsofphp/php-cs-fixer" />
|
||||||
|
<option value="humbug/humbug" />
|
||||||
|
<option value="infection/infection" />
|
||||||
|
<option value="jakub-onderka/php-parallel-lint" />
|
||||||
|
<option value="johnkary/phpunit-speedtrap" />
|
||||||
|
<option value="kalessil/production-dependencies-guard" />
|
||||||
|
<option value="mikey179/vfsStream" />
|
||||||
|
<option value="mockery/mockery" />
|
||||||
|
<option value="mybuilder/phpunit-accelerator" />
|
||||||
|
<option value="orchestra/testbench" />
|
||||||
|
<option value="pdepend/pdepend" />
|
||||||
|
<option value="phan/phan" />
|
||||||
|
<option value="phing/phing" />
|
||||||
|
<option value="phpcompatibility/php-compatibility" />
|
||||||
|
<option value="phpmd/phpmd" />
|
||||||
|
<option value="phpro/grumphp" />
|
||||||
|
<option value="phpspec/phpspec" />
|
||||||
|
<option value="phpspec/prophecy" />
|
||||||
|
<option value="phpstan/phpstan" />
|
||||||
|
<option value="phpunit/phpunit" />
|
||||||
|
<option value="povils/phpmnd" />
|
||||||
|
<option value="roave/security-advisories" />
|
||||||
|
<option value="satooshi/php-coveralls" />
|
||||||
|
<option value="sebastian/phpcpd" />
|
||||||
|
<option value="slevomat/coding-standard" />
|
||||||
|
<option value="spatie/phpunit-watcher" />
|
||||||
|
<option value="squizlabs/php_codesniffer" />
|
||||||
|
<option value="sstalle/php7cc" />
|
||||||
|
<option value="symfony/debug" />
|
||||||
|
<option value="symfony/maker-bundle" />
|
||||||
|
<option value="symfony/phpunit-bridge" />
|
||||||
|
<option value="symfony/var-dumper" />
|
||||||
|
<option value="vimeo/psalm" />
|
||||||
|
<option value="wimg/php-compatibility" />
|
||||||
|
<option value="wp-coding-standards/wpcs" />
|
||||||
|
<option value="yiisoft/yii2-coding-standards" />
|
||||||
|
<option value="yiisoft/yii2-debug" />
|
||||||
|
<option value="yiisoft/yii2-gii" />
|
||||||
|
<option value="zendframework/zend-coding-standard" />
|
||||||
|
<option value="zendframework/zend-debug" />
|
||||||
|
<option value="zendframework/zend-test" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="SerialPersistentFieldsWithWrongSignature" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="SerialVersionUIDNotStaticFinal" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="SerializableHasSerialVersionUIDField" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoreAnonymousInnerClasses" value="false" />
|
||||||
|
<option name="superClassString" value="java.awt.Component" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="SerializableHasSerializationMethods" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoreAnonymousInnerClasses" value="false" />
|
||||||
|
<option name="superClassString" value="java.awt.Component" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="SerializableInnerClassHasSerialVersionUIDField" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoreAnonymousInnerClasses" value="false" />
|
||||||
|
<option name="superClassString" value="java.awt.Component" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="SerializableInnerClassWithNonSerializableOuterClass" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoreAnonymousInnerClasses" value="false" />
|
||||||
|
<option name="superClassString" value="java.awt.Component" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="SerializableStoresNonSerializable" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="SerializableWithUnconstructableAncestor" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="SetReplaceableByEnumSet" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="SeveralTargetsMessage" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="SharedThreadLocalRandom" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ShortEchoTagCanBeUsedInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="SignalWithoutCorrespondingAwait" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="SimpleDateFormatWithoutLocale" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="SimplifiableAnnotation" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="SimplifiableIfStatement" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="SingleStatementInBlock" enabled="false" level="INFORMATION" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="SizeReplaceableByIsEmpty" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="SleepWhileHoldingLock" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="SocketResource" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="insideTryAllowed" value="false" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
|
||||||
|
<option name="processCode" value="true" />
|
||||||
|
<option name="processLiterals" value="true" />
|
||||||
|
<option name="processComments" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="SqlGotoInspection" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="SqlRedundantOrderingDirectionInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="SqlWithoutWhereInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="myDontWarnForLimit" value="false" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="StrTrUsageAsStrReplaceInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="StringBufferToStringInConcatenation" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="StringConcatenationInFormatCall" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="StringConcatenationInMessageFormatCall" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="StringConcatenationMissingWhitespace" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="StringEqualsEmptyString" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="SUPPRESS_FOR_VALUES_WHICH_COULD_BE_NULL" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="StringReplaceableByStringBuffer" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="onlyWarnOnLoop" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="StringToUpperWithoutLocale" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="SubStrUsedAsStrPosInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="SubtractionInCompareTo" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="SuspiciousArrayCast" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="SuspiciousIndentAfterControlStatement" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="SuspiciousLiteralUnderscore" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="SwitchStatementWithConfusingDeclaration" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="SynchronizationOnStaticField" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="SynchronizeOnLock" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="SynchronizeOnThis" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="SynchronizedMethod" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="m_includeNativeMethods" value="true" />
|
||||||
|
<option name="ignoreSynchronizedSuperMethods" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="SynchronizedOnLiteralObject" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="SystemGC" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="SystemGetenv" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="SystemProperties" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="SystemRunFinalizersOnExit" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="SystemSetSecurityManager" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="TestCaseInProductCode" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="TestCaseWithConstructor" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="TestCaseWithNoTestMethods" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoreSupers" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="TestMethodInProductCode" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="TestMethodWithoutAssertion" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="TextLabelInSwitchStatementJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ThreadLocalNotStaticFinal" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ThreadPriority" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ThreadStartInConstruction" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ThreadStopSuspendResume" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ThreadYield" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ThrowCaughtLocally" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoreRethrownExceptions" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="ThrowRawExceptionInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="TimeToString" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="TooBroadScope" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||||
|
<option name="m_allowConstructorAsInitializer" value="false" />
|
||||||
|
<option name="m_onlyLookAtBlocks" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="TransientFieldInNonSerializableClass" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="TransientFieldNotInitialized" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="TrivialStringConcatenation" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="UnNecessaryDoubleQuotesInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="UnaryPlus" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="UnclearBinaryExpression" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="UnconditionalWait" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="UnconstrainedVariableType" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="UndeclaredTests" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="UnknownInspectionInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="UnnecessarilyQualifiedStaticUsage" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="m_ignoreStaticFieldAccesses" value="false" />
|
||||||
|
<option name="m_ignoreStaticMethodCalls" value="false" />
|
||||||
|
<option name="m_ignoreStaticAccessFromStaticContext" value="false" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="UnnecessarilyQualifiedStaticallyImportedElement" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="UnnecessaryBoxing" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="onlyReportSuperfluouslyBoxed" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="UnnecessaryConstantArrayCreationExpression" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="UnnecessaryConstructor" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoreAnnotations" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="UnnecessaryLocalVariable" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="m_ignoreImmediatelyReturnedVariables" value="true" />
|
||||||
|
<option name="m_ignoreAnnotatedVariables" value="false" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="UnnecessaryLocalVariableJS" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="m_ignoreImmediatelyReturnedVariables" value="true" />
|
||||||
|
<option name="m_ignoreAnnotatedVariables" value="false" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="UnnecessaryParentheses" enabled="true" level="INFORMATION" enabled_by_default="true">
|
||||||
|
<option name="ignoreClarifyingParentheses" value="true" />
|
||||||
|
<option name="ignoreParenthesesOnConditionals" value="false" />
|
||||||
|
<option name="ignoreParenthesesOnLambdaParameter" value="false" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="UnnecessaryToStringCall" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="UnnecessaryUnaryMinus" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="UnnecessaryUnboxing" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="onlyReportSuperfluouslyUnboxed" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="UnsetConstructsCanBeMergedInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="UnterminatedStatementJS" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoreSemicolonAtEndOfBlock" value="false" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="UnusedCatchParameterJS" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="m_ignoreCatchBlocksWithComments" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="UnusedProperty" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="UseOfAWTPeerClass" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="UseOfJDBCDriverClass" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="UseOfObsoleteAssert" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="UseOfObsoleteDateTimeApi" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="UseOfProcessBuilder" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="UseOfPropertiesAsHashtable" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="UseOfSunClasses" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="UsingInclusionReturnValueInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="UtilityClassWithPublicConstructor" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="UtilityClassWithoutPrivateConstructor" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignorableAnnotations">
|
||||||
|
<value />
|
||||||
|
</option>
|
||||||
|
<option name="ignoreClassesWithOnlyMain" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="VariableNotUsedInsideIf" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="VariableUseSideOnly" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="VoidExpressionJS" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="VolatileArrayField" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="WaitCalledOnCondition" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="WaitNotInLoop" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="WaitNotifyNotInSynchronizedContext" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="WaitOrAwaitWithoutTimeout" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="WaitWhileHoldingTwoLocks" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="WaitWithoutCorrespondingNotify" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="WhileLoopSpinsOnField" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoreNonEmtpyLoops" value="false" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="ZeroLengthArrayInitialization" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
</profile>
|
||||||
|
</component>
|
6
.idea/.idea.TweetDuck/.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
.idea/.idea.TweetDuck/.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<settings>
|
||||||
|
<option name="PROJECT_PROFILE" value="Project" />
|
||||||
|
<version value="1.0" />
|
||||||
|
</settings>
|
||||||
|
</component>
|
7
.idea/.idea.TweetDuck/.idea/riderModule.iml
generated
Normal file
7
.idea/.idea.TweetDuck/.idea/riderModule.iml
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="RIDER_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$/../.." />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
20
.idea/.idea.TweetDuck/.idea/runConfigurations/TweetDuck.xml
generated
Normal file
20
.idea/.idea.TweetDuck/.idea/runConfigurations/TweetDuck.xml
generated
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="TweetDuck" type="DotNetProject" factoryName=".NET Project">
|
||||||
|
<option name="EXE_PATH" value="$PROJECT_DIR$/bin/x86/Debug/TweetDuck.exe" />
|
||||||
|
<option name="PROGRAM_PARAMETERS" value="-datafolder TweetDuckDebug -nogdpr" />
|
||||||
|
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/bin/x86/Debug" />
|
||||||
|
<option name="PASS_PARENT_ENVS" value="1" />
|
||||||
|
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||||
|
<option name="USE_MONO" value="0" />
|
||||||
|
<option name="RUNTIME_ARGUMENTS" value="" />
|
||||||
|
<option name="PROJECT_PATH" value="$PROJECT_DIR$/TweetDuck.csproj" />
|
||||||
|
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
|
||||||
|
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
||||||
|
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
||||||
|
<option name="PROJECT_KIND" value="Console" />
|
||||||
|
<option name="PROJECT_TFM" value=".NETFramework,Version=v4.7.2" />
|
||||||
|
<method v="2">
|
||||||
|
<option name="Build" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
11
.idea/.idea.TweetDuck/.idea/vcs.xml
generated
Normal file
11
.idea/.idea.TweetDuck/.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="GitSharedSettings">
|
||||||
|
<option name="FORCE_PUSH_PROHIBITED_PATTERNS">
|
||||||
|
<list />
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
@@ -1,58 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using TweetDuck.Utils;
|
|
||||||
using TweetLib.Core.Application;
|
|
||||||
|
|
||||||
namespace TweetDuck.Application{
|
|
||||||
class LockHandler : IAppLockHandler{
|
|
||||||
private const int WaitRetryDelay = 250;
|
|
||||||
private const int RestoreFailTimeout = 2000;
|
|
||||||
private const int CloseNaturallyTimeout = 10000;
|
|
||||||
private const int CloseKillTimeout = 5000;
|
|
||||||
|
|
||||||
bool IAppLockHandler.RestoreProcess(Process process){
|
|
||||||
if (process.MainWindowHandle == IntPtr.Zero){ // restore if the original process is in tray
|
|
||||||
NativeMethods.BroadcastMessage(Program.WindowRestoreMessage, (uint)process.Id, 0);
|
|
||||||
|
|
||||||
if (WindowsUtils.TrySleepUntil(() => CheckProcessExited(process) || (process.MainWindowHandle != IntPtr.Zero && process.Responding), RestoreFailTimeout, WaitRetryDelay)){
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IAppLockHandler.CloseProcess(Process process){
|
|
||||||
try{
|
|
||||||
if (process.CloseMainWindow()){
|
|
||||||
// ReSharper disable once AccessToDisposedClosure
|
|
||||||
WindowsUtils.TrySleepUntil(() => CheckProcessExited(process), CloseNaturallyTimeout, WaitRetryDelay);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!process.HasExited){
|
|
||||||
process.Kill();
|
|
||||||
// ReSharper disable once AccessToDisposedClosure
|
|
||||||
WindowsUtils.TrySleepUntil(() => CheckProcessExited(process), CloseKillTimeout, WaitRetryDelay);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (process.HasExited){
|
|
||||||
process.Dispose();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}catch(Exception ex) when (ex is InvalidOperationException || ex is Win32Exception){
|
|
||||||
bool hasExited = CheckProcessExited(process);
|
|
||||||
process.Dispose();
|
|
||||||
return hasExited;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool CheckProcessExited(Process process){
|
|
||||||
process.Refresh();
|
|
||||||
return process.HasExited;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,9 +1,21 @@
|
|||||||
using System.Diagnostics;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using TweetLib.Core.Application;
|
using TweetLib.Core.Application;
|
||||||
|
|
||||||
namespace TweetDuck.Application{
|
namespace TweetDuck.Application{
|
||||||
class SystemHandler : IAppSystemHandler{
|
class SystemHandler : IAppSystemHandler{
|
||||||
|
void IAppSystemHandler.OpenAssociatedProgram(string path){
|
||||||
|
try{
|
||||||
|
using(Process.Start(new ProcessStartInfo{
|
||||||
|
FileName = path,
|
||||||
|
ErrorDialog = true
|
||||||
|
})){}
|
||||||
|
}catch(Exception e){
|
||||||
|
Program.Reporter.HandleException("Error Opening Program", "Could not open the associated program for " + path, true, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void IAppSystemHandler.OpenFileExplorer(string path){
|
void IAppSystemHandler.OpenFileExplorer(string path){
|
||||||
if (File.Exists(path)){
|
if (File.Exists(path)){
|
||||||
using(Process.Start("explorer.exe", "/select,\"" + path.Replace('/', '\\') + "\"")){}
|
using(Process.Start("explorer.exe", "/select,\"" + path.Replace('/', '\\') + "\"")){}
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Controls;
|
using TweetDuck.Controls;
|
||||||
using TweetLib.Core.Features.Updates;
|
using TweetLib.Core.Systems.Updates;
|
||||||
|
|
||||||
namespace TweetDuck.Browser.Bridge{
|
namespace TweetDuck.Browser.Bridge{
|
||||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||||
|
50
Browser/Data/ResourceHandlers.cs
Normal file
50
Browser/Data/ResourceHandlers.cs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using CefSharp;
|
||||||
|
|
||||||
|
namespace TweetDuck.Browser.Data{
|
||||||
|
sealed class ResourceHandlers{
|
||||||
|
private readonly ConcurrentDictionary<string, Func<IResourceHandler>> handlers = new ConcurrentDictionary<string, Func<IResourceHandler>>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
public bool HasHandler(IRequest request){
|
||||||
|
return handlers.ContainsKey(request.Url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IResourceHandler GetHandler(IRequest request){
|
||||||
|
return handlers.TryGetValue(request.Url, out var factory) ? factory() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Register(string url, Func<IResourceHandler> factory){
|
||||||
|
if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri)){
|
||||||
|
handlers.AddOrUpdate(uri.AbsoluteUri, factory, (key, prev) => factory);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Register(ResourceLink link){
|
||||||
|
return Register(link.Url, link.Factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Unregister(string url){
|
||||||
|
return handlers.TryRemove(url, out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Unregister(ResourceLink link){
|
||||||
|
return Unregister(link.Url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Func<IResourceHandler> ForString(string str){
|
||||||
|
return () => ResourceHandler.FromString(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Func<IResourceHandler> ForString(string str, string mimeType){
|
||||||
|
return () => ResourceHandler.FromString(str, mimeType: mimeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Func<IResourceHandler> ForBytes(byte[] bytes, string mimeType){
|
||||||
|
return () => ResourceHandler.FromByteArray(bytes, mimeType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,13 +1,14 @@
|
|||||||
using CefSharp;
|
using System;
|
||||||
|
using CefSharp;
|
||||||
|
|
||||||
namespace TweetDuck.Browser.Data{
|
namespace TweetDuck.Browser.Data{
|
||||||
sealed class ResourceLink{
|
sealed class ResourceLink{
|
||||||
public string Url { get; }
|
public string Url { get; }
|
||||||
public IResourceHandler Handler { get; }
|
public Func<IResourceHandler> Factory { get; }
|
||||||
|
|
||||||
public ResourceLink(string url, IResourceHandler handler){
|
public ResourceLink(string url, Func<IResourceHandler> factory){
|
||||||
this.Url = url;
|
this.Url = url;
|
||||||
this.Handler = handler;
|
this.Factory = factory;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -16,11 +16,12 @@ using TweetDuck.Dialogs;
|
|||||||
using TweetDuck.Dialogs.Settings;
|
using TweetDuck.Dialogs.Settings;
|
||||||
using TweetDuck.Management;
|
using TweetDuck.Management;
|
||||||
using TweetDuck.Management.Analytics;
|
using TweetDuck.Management.Analytics;
|
||||||
|
using TweetDuck.Plugins;
|
||||||
using TweetDuck.Updates;
|
using TweetDuck.Updates;
|
||||||
using TweetDuck.Utils;
|
using TweetDuck.Utils;
|
||||||
using TweetLib.Core.Features.Plugins;
|
using TweetLib.Core.Features.Plugins;
|
||||||
using TweetLib.Core.Features.Plugins.Events;
|
using TweetLib.Core.Features.Plugins.Events;
|
||||||
using TweetLib.Core.Features.Updates;
|
using TweetLib.Core.Systems.Updates;
|
||||||
|
|
||||||
namespace TweetDuck.Browser{
|
namespace TweetDuck.Browser{
|
||||||
sealed partial class FormBrowser : Form, AnalyticsFile.IProvider{
|
sealed partial class FormBrowser : Form, AnalyticsFile.IProvider{
|
||||||
@@ -43,7 +44,7 @@ namespace TweetDuck.Browser{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string UpdateInstallerPath { get; private set; }
|
public UpdateInstaller UpdateInstaller { get; private set; }
|
||||||
private bool ignoreUpdateCheckError;
|
private bool ignoreUpdateCheckError;
|
||||||
|
|
||||||
public AnalyticsFile AnalyticsFile => analytics?.File ?? AnalyticsFile.Dummy;
|
public AnalyticsFile AnalyticsFile => analytics?.File ?? AnalyticsFile.Dummy;
|
||||||
@@ -65,7 +66,7 @@ namespace TweetDuck.Browser{
|
|||||||
private VideoPlayer videoPlayer;
|
private VideoPlayer videoPlayer;
|
||||||
private AnalyticsManager analytics;
|
private AnalyticsManager analytics;
|
||||||
|
|
||||||
public FormBrowser(){
|
public FormBrowser(PluginSchemeFactory pluginScheme){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
Text = Program.BrandName;
|
Text = Program.BrandName;
|
||||||
@@ -74,6 +75,7 @@ namespace TweetDuck.Browser{
|
|||||||
this.plugins.Reloaded += plugins_Reloaded;
|
this.plugins.Reloaded += plugins_Reloaded;
|
||||||
this.plugins.Executed += plugins_Executed;
|
this.plugins.Executed += plugins_Executed;
|
||||||
this.plugins.Reload();
|
this.plugins.Reload();
|
||||||
|
pluginScheme.Setup(plugins);
|
||||||
|
|
||||||
this.notification = new FormNotificationTweet(this, plugins);
|
this.notification = new FormNotificationTweet(this, plugins);
|
||||||
this.notification.Show();
|
this.notification.Show();
|
||||||
@@ -144,6 +146,8 @@ namespace TweetDuck.Browser{
|
|||||||
|
|
||||||
private void RestoreWindow(){
|
private void RestoreWindow(){
|
||||||
Config.BrowserWindow.Restore(this, true);
|
Config.BrowserWindow.Restore(this, true);
|
||||||
|
browser.PrepareSize(ClientSize);
|
||||||
|
|
||||||
prevState = WindowState;
|
prevState = WindowState;
|
||||||
isLoaded = true;
|
isLoaded = true;
|
||||||
}
|
}
|
||||||
@@ -212,6 +216,7 @@ namespace TweetDuck.Browser{
|
|||||||
}
|
}
|
||||||
|
|
||||||
timerResize.Stop();
|
timerResize.Stop();
|
||||||
|
browser.PrepareSize(ClientSize); // needed to pre-size browser control when launched in maximized state
|
||||||
|
|
||||||
if (Location != ControlExtensions.InvisibleLocation){
|
if (Location != ControlExtensions.InvisibleLocation){
|
||||||
Config.BrowserWindow.Save(this);
|
Config.BrowserWindow.Save(this);
|
||||||
@@ -231,7 +236,7 @@ namespace TweetDuck.Browser{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void FormBrowser_FormClosed(object sender, FormClosedEventArgs e){
|
private void FormBrowser_FormClosed(object sender, FormClosedEventArgs e){
|
||||||
if (isLoaded && UpdateInstallerPath == null){
|
if (isLoaded && UpdateInstaller == null){
|
||||||
updateBridge.Cleanup();
|
updateBridge.Cleanup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -258,9 +263,7 @@ namespace TweetDuck.Browser{
|
|||||||
|
|
||||||
private void plugins_Reloaded(object sender, PluginErrorEventArgs e){
|
private void plugins_Reloaded(object sender, PluginErrorEventArgs e){
|
||||||
if (e.HasErrors){
|
if (e.HasErrors){
|
||||||
this.InvokeAsyncSafe(() => { // TODO not needed but makes code consistent...
|
|
||||||
FormMessage.Error("Error Loading Plugins", "The following plugins will not be available until the issues are resolved:\n\n" + string.Join("\n\n", e.Errors), FormMessage.OK);
|
FormMessage.Error("Error Loading Plugins", "The following plugins will not be available until the issues are resolved:\n\n" + string.Join("\n\n", e.Errors), FormMessage.OK);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isLoaded){
|
if (isLoaded){
|
||||||
@@ -309,7 +312,7 @@ namespace TweetDuck.Browser{
|
|||||||
UpdateDownloadStatus status = update.DownloadStatus;
|
UpdateDownloadStatus status = update.DownloadStatus;
|
||||||
|
|
||||||
if (status == UpdateDownloadStatus.Done){
|
if (status == UpdateDownloadStatus.Done){
|
||||||
UpdateInstallerPath = update.InstallerPath;
|
UpdateInstaller = new UpdateInstaller(update.InstallerPath);
|
||||||
ForceClose();
|
ForceClose();
|
||||||
}
|
}
|
||||||
else if (status != UpdateDownloadStatus.Canceled && FormMessage.Error("Update Has Failed", "Could not automatically download the update: " + (update.DownloadError?.Message ?? "unknown error") + "\n\nWould you like to open the website and try downloading the update manually?", FormMessage.Yes, FormMessage.No)){
|
else if (status != UpdateDownloadStatus.Canceled && FormMessage.Error("Update Has Failed", "Could not automatically download the update: " + (update.DownloadError?.Message ?? "unknown error") + "\n\nWould you like to open the website and try downloading the update manually?", FormMessage.Yes, FormMessage.No)){
|
||||||
|
@@ -30,6 +30,6 @@ namespace TweetDuck.Browser.Handling{
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnDraggableRegionsChanged(IWebBrowser browserControl, IBrowser browser, IList<DraggableRegion> regions){}
|
public void OnDraggableRegionsChanged(IWebBrowser browserControl, IBrowser browser, IFrame frame, IList<DraggableRegion> regions){}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -82,7 +82,8 @@ namespace TweetDuck.Browser.Handling.General{
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IJsDialogHandler.OnJSBeforeUnload(IWebBrowser browserControl, IBrowser browser, string message, bool isReload, IJsDialogCallback callback){
|
bool IJsDialogHandler.OnBeforeUnloadDialog(IWebBrowser browserControl, IBrowser browser, string messageText, bool isReload, IJsDialogCallback callback){
|
||||||
|
callback.Dispose();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -33,7 +33,7 @@ namespace TweetDuck.Browser.Handling{
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool IKeyboardHandler.OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut){
|
bool IKeyboardHandler.OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut){
|
||||||
if (type == KeyType.RawKeyDown && !browser.FocusedFrame.Url.StartsWith("chrome-devtools://")){
|
if (type == KeyType.RawKeyDown && !browser.FocusedFrame.Url.StartsWith("devtools://")){
|
||||||
return HandleRawKey(browserControl, browser, (Keys)windowsKeyCode, modifiers);
|
return HandleRawKey(browserControl, browser, (Keys)windowsKeyCode, modifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,79 +1,20 @@
|
|||||||
using System;
|
using CefSharp;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.Specialized;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using CefSharp;
|
|
||||||
using CefSharp.Handler;
|
using CefSharp.Handler;
|
||||||
using TweetDuck.Browser.Handling.General;
|
using TweetDuck.Browser.Handling.General;
|
||||||
using TweetDuck.Utils;
|
|
||||||
using TweetLib.Core.Utils;
|
|
||||||
|
|
||||||
namespace TweetDuck.Browser.Handling{
|
namespace TweetDuck.Browser.Handling{
|
||||||
class RequestHandlerBase : DefaultRequestHandler{
|
class RequestHandlerBase : RequestHandler{
|
||||||
private static readonly Regex TweetDeckResourceUrl = new Regex(@"/dist/(.*?)\.(.*?)\.(css|js)$");
|
|
||||||
private static readonly SortedList<string, string> TweetDeckHashes = new SortedList<string, string>(4);
|
|
||||||
|
|
||||||
public static void LoadResourceRewriteRules(string rules){
|
|
||||||
if (string.IsNullOrEmpty(rules)){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
TweetDeckHashes.Clear();
|
|
||||||
|
|
||||||
foreach(string rule in rules.Replace(" ", "").ToLower().Split(',')){
|
|
||||||
var (key, hash) = StringUtils.SplitInTwo(rule, '=') ?? throw new ArgumentException("A rule must have one '=' character: " + rule);
|
|
||||||
|
|
||||||
if (hash.All(chr => char.IsDigit(chr) || (chr >= 'a' && chr <= 'f'))){
|
|
||||||
TweetDeckHashes.Add(key, hash);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
throw new ArgumentException("Invalid hash characters: " + rule);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly bool autoReload;
|
private readonly bool autoReload;
|
||||||
|
|
||||||
public RequestHandlerBase(bool autoReload){
|
public RequestHandlerBase(bool autoReload){
|
||||||
this.autoReload = autoReload;
|
this.autoReload = autoReload;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool OnOpenUrlFromTab(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture){
|
protected override bool OnOpenUrlFromTab(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture){
|
||||||
return LifeSpanHandler.HandleLinkClick(browserControl, targetDisposition, targetUrl);
|
return LifeSpanHandler.HandleLinkClick(browserControl, targetDisposition, targetUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback){
|
protected override void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status){
|
||||||
if (BrowserUtils.HasDevTools){
|
|
||||||
NameValueCollection headers = request.Headers;
|
|
||||||
headers.Remove("x-devtools-emulate-network-conditions-client-id");
|
|
||||||
request.Headers = headers;
|
|
||||||
}
|
|
||||||
|
|
||||||
return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response){
|
|
||||||
if ((request.ResourceType == ResourceType.Script || request.ResourceType == ResourceType.Stylesheet) && TweetDeckHashes.Count > 0){
|
|
||||||
string url = request.Url;
|
|
||||||
Match match = TweetDeckResourceUrl.Match(url);
|
|
||||||
|
|
||||||
if (match.Success && TweetDeckHashes.TryGetValue($"{match.Groups[1]}.{match.Groups[3]}", out string hash)){
|
|
||||||
if (match.Groups[2].Value == hash){
|
|
||||||
Program.Reporter.LogVerbose("[RequestHandlerBase] Accepting " + url);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
Program.Reporter.LogVerbose("[RequestHandlerBase] Replacing " + url + " hash with " + hash);
|
|
||||||
request.Url = TweetDeckResourceUrl.Replace(url, $"/dist/$1.{hash}.$3");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return base.OnResourceResponse(browserControl, browser, frame, request, response);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status){
|
|
||||||
if (autoReload){
|
if (autoReload){
|
||||||
browser.Reload();
|
browser.Reload();
|
||||||
}
|
}
|
||||||
|
@@ -1,42 +1,13 @@
|
|||||||
using System.Collections.Specialized;
|
using CefSharp;
|
||||||
using CefSharp;
|
|
||||||
using TweetDuck.Browser.Handling.Filters;
|
|
||||||
using TweetDuck.Utils;
|
|
||||||
using TweetLib.Core.Features.Twitter;
|
using TweetLib.Core.Features.Twitter;
|
||||||
|
|
||||||
namespace TweetDuck.Browser.Handling{
|
namespace TweetDuck.Browser.Handling{
|
||||||
sealed class RequestHandlerBrowser : RequestHandlerBase{
|
sealed class RequestHandlerBrowser : RequestHandlerBase{
|
||||||
private const string UrlVendorResource = "/dist/vendor";
|
|
||||||
private const string UrlLoadingSpinner = "/backgrounds/spinner_blue";
|
|
||||||
|
|
||||||
public string BlockNextUserNavUrl { get; set; }
|
public string BlockNextUserNavUrl { get; set; }
|
||||||
|
|
||||||
public RequestHandlerBrowser() : base(true){}
|
public RequestHandlerBrowser() : base(true){}
|
||||||
|
|
||||||
public override CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback){
|
protected override bool OnBeforeBrowse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, bool userGesture, bool isRedirect){
|
||||||
if (request.ResourceType == ResourceType.MainFrame){
|
|
||||||
if (request.Url.EndsWith("//twitter.com/")){
|
|
||||||
request.Url = TwitterUrls.TweetDeck; // redirect plain twitter.com requests, fixes bugs with login 2FA
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (request.ResourceType == ResourceType.Script){
|
|
||||||
string url = request.Url;
|
|
||||||
|
|
||||||
if (url.Contains("analytics.")){
|
|
||||||
callback.Dispose();
|
|
||||||
return CefReturnValue.Cancel;
|
|
||||||
}
|
|
||||||
else if (url.Contains(UrlVendorResource)){
|
|
||||||
NameValueCollection headers = request.Headers;
|
|
||||||
headers["Accept-Encoding"] = "identity";
|
|
||||||
request.Headers = headers;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool OnBeforeBrowse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, bool userGesture, bool isRedirect){
|
|
||||||
if (userGesture && request.TransitionType == TransitionType.LinkClicked){
|
if (userGesture && request.TransitionType == TransitionType.LinkClicked){
|
||||||
bool block = request.Url == BlockNextUserNavUrl;
|
bool block = request.Url == BlockNextUserNavUrl;
|
||||||
BlockNextUserNavUrl = string.Empty;
|
BlockNextUserNavUrl = string.Empty;
|
||||||
@@ -48,22 +19,5 @@ namespace TweetDuck.Browser.Handling{
|
|||||||
|
|
||||||
return base.OnBeforeBrowse(browserControl, browser, frame, request, userGesture, isRedirect);
|
return base.OnBeforeBrowse(browserControl, browser, frame, request, userGesture, isRedirect);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response){
|
|
||||||
if (request.ResourceType == ResourceType.Image && request.Url.Contains(UrlLoadingSpinner)){
|
|
||||||
request.Url = TwitterUtils.LoadingSpinner.Url;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return base.OnResourceResponse(browserControl, browser, frame, request, response);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override IResponseFilter GetResourceResponseFilter(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response){
|
|
||||||
if (request.ResourceType == ResourceType.Script && request.Url.Contains(UrlVendorResource) && int.TryParse(response.ResponseHeaders["Content-Length"], out int totalBytes)){
|
|
||||||
return new ResponseFilterVendor(totalBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
return base.GetResourceResponseFilter(browserControl, browser, frame, request, response);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,43 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using CefSharp;
|
|
||||||
using TweetDuck.Browser.Data;
|
|
||||||
|
|
||||||
namespace TweetDuck.Browser.Handling{
|
|
||||||
sealed class ResourceHandlerFactory : IResourceHandlerFactory{
|
|
||||||
public bool HasHandlers => !handlers.IsEmpty;
|
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<string, IResourceHandler> handlers = new ConcurrentDictionary<string, IResourceHandler>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
public IResourceHandler GetResourceHandler(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request){
|
|
||||||
try{
|
|
||||||
return handlers.TryGetValue(request.Url, out IResourceHandler handler) ? handler : null;
|
|
||||||
}finally{
|
|
||||||
request.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// registration
|
|
||||||
|
|
||||||
public bool RegisterHandler(string url, IResourceHandler handler){
|
|
||||||
if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri)){
|
|
||||||
handlers.AddOrUpdate(uri.AbsoluteUri, handler, (key, prev) => handler);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool RegisterHandler(ResourceLink link){
|
|
||||||
return RegisterHandler(link.Url, link.Handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool UnregisterHandler(string url){
|
|
||||||
return handlers.TryRemove(url, out IResourceHandler _);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool UnregisterHandler(ResourceLink link){
|
|
||||||
return UnregisterHandler(link.Url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,7 +1,9 @@
|
|||||||
using System.Collections.Specialized;
|
using System;
|
||||||
|
using System.Collections.Specialized;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using CefSharp;
|
using CefSharp;
|
||||||
|
using CefSharp.Callback;
|
||||||
|
|
||||||
namespace TweetDuck.Browser.Handling{
|
namespace TweetDuck.Browser.Handling{
|
||||||
sealed class ResourceHandlerNotification : IResourceHandler{
|
sealed class ResourceHandlerNotification : IResourceHandler{
|
||||||
@@ -20,8 +22,14 @@ namespace TweetDuck.Browser.Handling{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IResourceHandler.ProcessRequest(IRequest request, ICallback callback){
|
bool IResourceHandler.Open(IRequest request, out bool handleRequest, ICallback callback){
|
||||||
callback.Continue();
|
callback.Dispose();
|
||||||
|
handleRequest = true;
|
||||||
|
|
||||||
|
if (dataIn != null){
|
||||||
|
dataIn.Position = 0;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,31 +39,39 @@ namespace TweetDuck.Browser.Handling{
|
|||||||
response.MimeType = "text/html";
|
response.MimeType = "text/html";
|
||||||
response.StatusCode = 200;
|
response.StatusCode = 200;
|
||||||
response.StatusText = "OK";
|
response.StatusText = "OK";
|
||||||
response.ResponseHeaders = headers;
|
response.Headers = headers;
|
||||||
responseLength = dataIn?.Length ?? -1;
|
responseLength = dataIn?.Length ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IResourceHandler.Read(Stream dataOut, out int bytesRead, IResourceReadCallback callback){
|
||||||
|
callback?.Dispose(); // TODO unnecessary null check once ReadResponse is removed
|
||||||
|
|
||||||
|
try{
|
||||||
|
byte[] buffer = new byte[Math.Min(dataIn.Length - dataIn.Position, dataOut.Length)];
|
||||||
|
int length = buffer.Length;
|
||||||
|
|
||||||
|
dataIn.Read(buffer, 0, length);
|
||||||
|
dataOut.Write(buffer, 0, length);
|
||||||
|
bytesRead = length;
|
||||||
|
}catch{ // catch IOException, possibly NullReferenceException if dataIn is null
|
||||||
|
bytesRead = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytesRead > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IResourceHandler.Skip(long bytesToSkip, out long bytesSkipped, IResourceSkipCallback callback){
|
||||||
|
bytesSkipped = -2; // ERR_FAILED
|
||||||
|
callback.Dispose();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IResourceHandler.ProcessRequest(IRequest request, ICallback callback){
|
||||||
|
return ((IResourceHandler)this).Open(request, out bool _, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IResourceHandler.ReadResponse(Stream dataOut, out int bytesRead, ICallback callback){
|
bool IResourceHandler.ReadResponse(Stream dataOut, out int bytesRead, ICallback callback){
|
||||||
callback.Dispose();
|
return ((IResourceHandler)this).Read(dataOut, out bytesRead, null);
|
||||||
|
|
||||||
try{
|
|
||||||
int length = (int)dataIn.Length;
|
|
||||||
|
|
||||||
dataIn.CopyTo(dataOut, length);
|
|
||||||
bytesRead = length;
|
|
||||||
return true;
|
|
||||||
}catch{ // catch IOException, possibly NullReferenceException if dataIn is null
|
|
||||||
bytesRead = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IResourceHandler.CanGetCookie(Cookie cookie){
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IResourceHandler.CanSetCookie(Cookie cookie){
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IResourceHandler.Cancel(){}
|
void IResourceHandler.Cancel(){}
|
||||||
|
35
Browser/Handling/ResourceRequestHandler.cs
Normal file
35
Browser/Handling/ResourceRequestHandler.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using CefSharp;
|
||||||
|
using TweetDuck.Browser.Data;
|
||||||
|
|
||||||
|
namespace TweetDuck.Browser.Handling{
|
||||||
|
abstract class ResourceRequestHandler : CefSharp.Handler.ResourceRequestHandler{
|
||||||
|
private class SelfFactoryImpl : IResourceRequestHandlerFactory{
|
||||||
|
private readonly ResourceRequestHandler me;
|
||||||
|
|
||||||
|
public SelfFactoryImpl(ResourceRequestHandler me){
|
||||||
|
this.me = me;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IResourceRequestHandlerFactory.HasHandlers { get; } = true;
|
||||||
|
|
||||||
|
[SuppressMessage("ReSharper", "RedundantAssignment")]
|
||||||
|
IResourceRequestHandler IResourceRequestHandlerFactory.GetResourceRequestHandler(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling){
|
||||||
|
disableDefaultHandling = me.ResourceHandlers.HasHandler(request);
|
||||||
|
return me;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IResourceRequestHandlerFactory SelfFactory { get; }
|
||||||
|
public ResourceHandlers ResourceHandlers { get; }
|
||||||
|
|
||||||
|
protected ResourceRequestHandler(){
|
||||||
|
this.SelfFactory = new SelfFactoryImpl(this);
|
||||||
|
this.ResourceHandlers = new ResourceHandlers();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override IResourceHandler GetResourceHandler(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request){
|
||||||
|
return ResourceHandlers.GetHandler(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
69
Browser/Handling/ResourceRequestHandlerBase.cs
Normal file
69
Browser/Handling/ResourceRequestHandlerBase.cs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using CefSharp;
|
||||||
|
using TweetDuck.Utils;
|
||||||
|
using TweetLib.Core.Utils;
|
||||||
|
|
||||||
|
namespace TweetDuck.Browser.Handling{
|
||||||
|
class ResourceRequestHandlerBase : ResourceRequestHandler{
|
||||||
|
private static readonly Regex TweetDeckResourceUrl = new Regex(@"/dist/(.*?)\.(.*?)\.(css|js)$");
|
||||||
|
private static readonly SortedList<string, string> TweetDeckHashes = new SortedList<string, string>(4);
|
||||||
|
|
||||||
|
public static void LoadResourceRewriteRules(string rules){
|
||||||
|
if (string.IsNullOrEmpty(rules)){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TweetDeckHashes.Clear();
|
||||||
|
|
||||||
|
foreach(string rule in rules.Replace(" ", "").ToLower().Split(',')){
|
||||||
|
var (key, hash) = StringUtils.SplitInTwo(rule, '=') ?? throw new ArgumentException("A rule must have one '=' character: " + rule);
|
||||||
|
|
||||||
|
if (hash.All(chr => char.IsDigit(chr) || (chr >= 'a' && chr <= 'f'))){
|
||||||
|
TweetDeckHashes.Add(key, hash);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
throw new ArgumentException("Invalid hash characters: " + rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback){
|
||||||
|
if (request.ResourceType == ResourceType.CspReport){
|
||||||
|
callback.Dispose();
|
||||||
|
return CefReturnValue.Cancel;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BrowserUtils.HasDevTools){
|
||||||
|
NameValueCollection headers = request.Headers;
|
||||||
|
headers.Remove("x-devtools-emulate-network-conditions-client-id");
|
||||||
|
request.Headers = headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response){
|
||||||
|
if ((request.ResourceType == ResourceType.Script || request.ResourceType == ResourceType.Stylesheet) && TweetDeckHashes.Count > 0){
|
||||||
|
string url = request.Url;
|
||||||
|
Match match = TweetDeckResourceUrl.Match(url);
|
||||||
|
|
||||||
|
if (match.Success && TweetDeckHashes.TryGetValue($"{match.Groups[1]}.{match.Groups[3]}", out string hash)){
|
||||||
|
if (match.Groups[2].Value == hash){
|
||||||
|
Program.Reporter.LogVerbose("[RequestHandlerBase] Accepting " + url);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
Program.Reporter.LogVerbose("[RequestHandlerBase] Replacing " + url + " hash with " + hash);
|
||||||
|
request.Url = TweetDeckResourceUrl.Replace(url, $"/dist/$1.{hash}.$3");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.OnResourceResponse(browserControl, browser, frame, request, response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
52
Browser/Handling/ResourceRequestHandlerBrowser.cs
Normal file
52
Browser/Handling/ResourceRequestHandlerBrowser.cs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
using CefSharp;
|
||||||
|
using TweetDuck.Browser.Handling.Filters;
|
||||||
|
using TweetDuck.Utils;
|
||||||
|
using TweetLib.Core.Features.Twitter;
|
||||||
|
|
||||||
|
namespace TweetDuck.Browser.Handling{
|
||||||
|
class ResourceRequestHandlerBrowser : ResourceRequestHandlerBase{
|
||||||
|
private const string UrlVendorResource = "/dist/vendor";
|
||||||
|
private const string UrlLoadingSpinner = "/backgrounds/spinner_blue";
|
||||||
|
private const string UrlVersionCheck = "/web/dist/version.json";
|
||||||
|
|
||||||
|
protected override CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback){
|
||||||
|
if (request.ResourceType == ResourceType.MainFrame){
|
||||||
|
if (request.Url.EndsWith("//twitter.com/")){
|
||||||
|
request.Url = TwitterUrls.TweetDeck; // redirect plain twitter.com requests, fixes bugs with login 2FA
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (request.ResourceType == ResourceType.Image){
|
||||||
|
if (request.Url.Contains(UrlLoadingSpinner)){
|
||||||
|
request.Url = TwitterUtils.LoadingSpinner.Url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (request.ResourceType == ResourceType.Script){
|
||||||
|
string url = request.Url;
|
||||||
|
|
||||||
|
if (url.Contains("analytics.")){
|
||||||
|
callback.Dispose();
|
||||||
|
return CefReturnValue.Cancel;
|
||||||
|
}
|
||||||
|
else if (url.Contains(UrlVendorResource)){
|
||||||
|
request.SetHeaderByName("Accept-Encoding", "identity", overwrite: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (request.ResourceType == ResourceType.Xhr){
|
||||||
|
if (request.Url.Contains(UrlVersionCheck)){
|
||||||
|
callback.Dispose();
|
||||||
|
return CefReturnValue.Cancel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override IResponseFilter GetResourceResponseFilter(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response){
|
||||||
|
if (request.ResourceType == ResourceType.Script && request.Url.Contains(UrlVendorResource) && int.TryParse(response.Headers["Content-Length"], out int totalBytes)){
|
||||||
|
return new ResponseFilterVendor(totalBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.GetResourceResponseFilter(browserControl, browser, frame, request, response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +1,5 @@
|
|||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using CefSharp;
|
|
||||||
using CefSharp.WinForms;
|
using CefSharp.WinForms;
|
||||||
using TweetDuck.Browser.Data;
|
using TweetDuck.Browser.Data;
|
||||||
using TweetDuck.Browser.Handling;
|
using TweetDuck.Browser.Handling;
|
||||||
@@ -14,7 +13,9 @@ using TweetLib.Core.Features.Twitter;
|
|||||||
|
|
||||||
namespace TweetDuck.Browser.Notification{
|
namespace TweetDuck.Browser.Notification{
|
||||||
abstract partial class FormNotificationBase : Form, AnalyticsFile.IProvider{
|
abstract partial class FormNotificationBase : Form, AnalyticsFile.IProvider{
|
||||||
public static readonly ResourceLink AppLogo = new ResourceLink("https://ton.twimg.com/tduck/avatar", ResourceHandler.FromByteArray(Properties.Resources.avatar, "image/png"));
|
public static readonly ResourceLink AppLogo = new ResourceLink("https://ton.twimg.com/tduck/avatar", ResourceHandlers.ForBytes(Properties.Resources.avatar, "image/png"));
|
||||||
|
|
||||||
|
protected const string BlankURL = TwitterUrls.TweetDeck + "/?blank";
|
||||||
|
|
||||||
public static string FontSize = null;
|
public static string FontSize = null;
|
||||||
public static string HeadLayout = null;
|
public static string HeadLayout = null;
|
||||||
@@ -130,16 +131,19 @@ namespace TweetDuck.Browser.Notification{
|
|||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
this.owner.FormClosed += owner_FormClosed;
|
this.owner.FormClosed += owner_FormClosed;
|
||||||
|
|
||||||
ResourceHandlerFactory resourceHandlerFactory = new ResourceHandlerFactory();
|
var resourceRequestHandler = new ResourceRequestHandlerBase();
|
||||||
resourceHandlerFactory.RegisterHandler(TwitterUrls.TweetDeck, this.resourceHandler);
|
var resourceHandlers = resourceRequestHandler.ResourceHandlers;
|
||||||
resourceHandlerFactory.RegisterHandler(AppLogo);
|
|
||||||
|
|
||||||
this.browser = new ChromiumWebBrowser("about:blank"){
|
resourceHandlers.Register(BlankURL, ResourceHandlers.ForString(string.Empty));
|
||||||
|
resourceHandlers.Register(TwitterUrls.TweetDeck, () => this.resourceHandler);
|
||||||
|
resourceHandlers.Register(AppLogo);
|
||||||
|
|
||||||
|
this.browser = new ChromiumWebBrowser(BlankURL){
|
||||||
MenuHandler = new ContextMenuNotification(this, enableContextMenu),
|
MenuHandler = new ContextMenuNotification(this, enableContextMenu),
|
||||||
JsDialogHandler = new JavaScriptDialogHandler(),
|
JsDialogHandler = new JavaScriptDialogHandler(),
|
||||||
LifeSpanHandler = new LifeSpanHandler(),
|
LifeSpanHandler = new LifeSpanHandler(),
|
||||||
RequestHandler = new RequestHandlerBase(false),
|
RequestHandler = new RequestHandlerBase(false),
|
||||||
ResourceHandlerFactory = resourceHandlerFactory
|
ResourceRequestHandlerFactory = resourceRequestHandler.SelfFactory
|
||||||
};
|
};
|
||||||
|
|
||||||
this.browser.Dock = DockStyle.None;
|
this.browser.Dock = DockStyle.None;
|
||||||
@@ -185,7 +189,7 @@ namespace TweetDuck.Browser.Notification{
|
|||||||
// notification methods
|
// notification methods
|
||||||
|
|
||||||
public virtual void HideNotification(){
|
public virtual void HideNotification(){
|
||||||
browser.Load("about:blank");
|
browser.Load(BlankURL);
|
||||||
DisplayTooltip(null);
|
DisplayTooltip(null);
|
||||||
|
|
||||||
Location = ControlExtensions.InvisibleLocation;
|
Location = ControlExtensions.InvisibleLocation;
|
||||||
|
@@ -12,6 +12,7 @@ using TweetLib.Core.Data;
|
|||||||
using TweetLib.Core.Features.Notifications;
|
using TweetLib.Core.Features.Notifications;
|
||||||
using TweetLib.Core.Features.Plugins;
|
using TweetLib.Core.Features.Plugins;
|
||||||
using TweetLib.Core.Features.Plugins.Enums;
|
using TweetLib.Core.Features.Plugins.Enums;
|
||||||
|
using TweetLib.Core.Features.Twitter;
|
||||||
|
|
||||||
namespace TweetDuck.Browser.Notification{
|
namespace TweetDuck.Browser.Notification{
|
||||||
abstract partial class FormNotificationMain : FormNotificationBase{
|
abstract partial class FormNotificationMain : FormNotificationBase{
|
||||||
@@ -72,12 +73,12 @@ namespace TweetDuck.Browser.Notification{
|
|||||||
this.timerBarHeight = BrowserUtils.Scale(4, DpiScale);
|
this.timerBarHeight = BrowserUtils.Scale(4, DpiScale);
|
||||||
|
|
||||||
browser.KeyboardHandler = new KeyboardHandlerNotification(this);
|
browser.KeyboardHandler = new KeyboardHandlerNotification(this);
|
||||||
browser.RegisterAsyncJsObject("$TD", new TweetDeckBridge.Notification(owner, this));
|
browser.RegisterJsBridge("$TD", new TweetDeckBridge.Notification(owner, this));
|
||||||
|
|
||||||
browser.LoadingStateChanged += Browser_LoadingStateChanged;
|
browser.LoadingStateChanged += Browser_LoadingStateChanged;
|
||||||
browser.FrameLoadEnd += Browser_FrameLoadEnd;
|
browser.FrameLoadEnd += Browser_FrameLoadEnd;
|
||||||
|
|
||||||
plugins.Register(PluginEnvironment.Notification, new PluginDispatcher(browser));
|
plugins.Register(PluginEnvironment.Notification, new PluginDispatcher(browser, url => TwitterUrls.IsTweetDeck(url) && url != BlankURL));
|
||||||
|
|
||||||
mouseHookDelegate = MouseHookProc;
|
mouseHookDelegate = MouseHookProc;
|
||||||
Disposed += (sender, args) => StopMouseHook(true);
|
Disposed += (sender, args) => StopMouseHook(true);
|
||||||
@@ -113,7 +114,15 @@ namespace TweetDuck.Browser.Notification{
|
|||||||
int eventType = wParam.ToInt32();
|
int eventType = wParam.ToInt32();
|
||||||
|
|
||||||
if (eventType == NativeMethods.WM_MOUSEWHEEL && IsCursorOverBrowser){
|
if (eventType == NativeMethods.WM_MOUSEWHEEL && IsCursorOverBrowser){
|
||||||
browser.SendMouseWheelEvent(0, 0, 0, BrowserUtils.Scale(NativeMethods.GetMouseHookData(lParam), Config.NotificationScrollSpeed * 0.01), CefEventFlags.None);
|
int delta = BrowserUtils.Scale(NativeMethods.GetMouseHookData(lParam), Config.NotificationScrollSpeed * 0.01);
|
||||||
|
|
||||||
|
if (Config.EnableSmoothScrolling){
|
||||||
|
browser.ExecuteScriptAsync("window.TDGF_scrollSmoothly", (int)Math.Round(-delta / 0.6));
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
browser.SendMouseWheelEvent(0, 0, 0, delta, CefEventFlags.None);
|
||||||
|
}
|
||||||
|
|
||||||
return NativeMethods.HOOK_HANDLED;
|
return NativeMethods.HOOK_HANDLED;
|
||||||
}
|
}
|
||||||
else if (eventType == NativeMethods.WM_XBUTTONDOWN && DesktopBounds.Contains(Cursor.Position)){
|
else if (eventType == NativeMethods.WM_XBUTTONDOWN && DesktopBounds.Contains(Cursor.Position)){
|
||||||
@@ -154,7 +163,7 @@ namespace TweetDuck.Browser.Notification{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void Browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
|
private void Browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
|
||||||
if (!e.IsLoading && browser.Address != "about:blank"){
|
if (!e.IsLoading && browser.Address != BlankURL){
|
||||||
this.InvokeSafe(() => {
|
this.InvokeSafe(() => {
|
||||||
Visible = true; // ensures repaint before moving the window to a visible location
|
Visible = true; // ensures repaint before moving the window to a visible location
|
||||||
timerDisplayDelay.Start();
|
timerDisplayDelay.Start();
|
||||||
@@ -165,7 +174,7 @@ namespace TweetDuck.Browser.Notification{
|
|||||||
private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
|
private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
|
||||||
IFrame frame = e.Frame;
|
IFrame frame = e.Frame;
|
||||||
|
|
||||||
if (frame.IsMain && browser.Address != "about:blank"){
|
if (frame.IsMain && browser.Address != BlankURL){
|
||||||
frame.ExecuteJavaScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Environment.Notification));
|
frame.ExecuteJavaScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Environment.Notification));
|
||||||
CefScriptExecutor.RunFile(frame, "notification.js");
|
CefScriptExecutor.RunFile(frame, "notification.js");
|
||||||
}
|
}
|
||||||
|
@@ -23,7 +23,7 @@ namespace TweetDuck.Browser.Notification.Screenshot{
|
|||||||
|
|
||||||
int realWidth = BrowserUtils.Scale(width, DpiScale);
|
int realWidth = BrowserUtils.Scale(width, DpiScale);
|
||||||
|
|
||||||
browser.RegisterAsyncJsObject("$TD_NotificationScreenshot", new ScreenshotBridge(this, SetScreenshotHeight, callback));
|
browser.RegisterJsBridge("$TD_NotificationScreenshot", new ScreenshotBridge(this, SetScreenshotHeight, callback));
|
||||||
|
|
||||||
browser.LoadingStateChanged += (sender, args) => {
|
browser.LoadingStateChanged += (sender, args) => {
|
||||||
if (args.IsLoading){
|
if (args.IsLoading){
|
||||||
|
@@ -1,7 +1,9 @@
|
|||||||
using System.Drawing;
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using CefSharp;
|
using CefSharp;
|
||||||
|
using TweetDuck.Browser.Data;
|
||||||
using TweetDuck.Controls;
|
using TweetDuck.Controls;
|
||||||
using TweetDuck.Dialogs;
|
using TweetDuck.Dialogs;
|
||||||
using TweetDuck.Dialogs.Settings;
|
using TweetDuck.Dialogs.Settings;
|
||||||
@@ -11,7 +13,7 @@ namespace TweetDuck.Browser.Notification{
|
|||||||
static class SoundNotification{
|
static class SoundNotification{
|
||||||
public const string SupportedFormats = "*.wav;*.ogg;*.mp3;*.flac;*.opus;*.weba;*.webm";
|
public const string SupportedFormats = "*.wav;*.ogg;*.mp3;*.flac;*.opus;*.weba;*.webm";
|
||||||
|
|
||||||
public static IResourceHandler CreateFileHandler(string path){
|
public static Func<IResourceHandler> CreateFileHandler(string path){
|
||||||
string mimeType = Path.GetExtension(path) switch{
|
string mimeType = Path.GetExtension(path) switch{
|
||||||
".weba" => "audio/webm",
|
".weba" => "audio/webm",
|
||||||
".webm" => "audio/webm",
|
".webm" => "audio/webm",
|
||||||
@@ -24,7 +26,7 @@ namespace TweetDuck.Browser.Notification{
|
|||||||
};
|
};
|
||||||
|
|
||||||
try{
|
try{
|
||||||
return ResourceHandler.FromFilePath(path, mimeType);
|
return ResourceHandlers.ForBytes(File.ReadAllBytes(path), mimeType);
|
||||||
}catch{
|
}catch{
|
||||||
FormBrowser browser = FormManager.TryFind<FormBrowser>();
|
FormBrowser browser = FormManager.TryFind<FormBrowser>();
|
||||||
|
|
||||||
|
@@ -6,6 +6,7 @@ using CefSharp;
|
|||||||
using CefSharp.WinForms;
|
using CefSharp.WinForms;
|
||||||
using TweetDuck.Browser.Adapters;
|
using TweetDuck.Browser.Adapters;
|
||||||
using TweetDuck.Browser.Bridge;
|
using TweetDuck.Browser.Bridge;
|
||||||
|
using TweetDuck.Browser.Data;
|
||||||
using TweetDuck.Browser.Handling;
|
using TweetDuck.Browser.Handling;
|
||||||
using TweetDuck.Browser.Handling.General;
|
using TweetDuck.Browser.Handling.General;
|
||||||
using TweetDuck.Browser.Notification;
|
using TweetDuck.Browser.Notification;
|
||||||
@@ -44,13 +45,16 @@ namespace TweetDuck.Browser{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private readonly ChromiumWebBrowser browser;
|
private readonly ChromiumWebBrowser browser;
|
||||||
private readonly ResourceHandlerFactory resourceHandlerFactory = new ResourceHandlerFactory();
|
private readonly ResourceHandlers resourceHandlers;
|
||||||
|
|
||||||
private string prevSoundNotificationPath = null;
|
private string prevSoundNotificationPath = null;
|
||||||
|
|
||||||
public TweetDeckBrowser(FormBrowser owner, PluginManager plugins, TweetDeckBridge tdBridge, UpdateBridge updateBridge){
|
public TweetDeckBrowser(FormBrowser owner, PluginManager plugins, TweetDeckBridge tdBridge, UpdateBridge updateBridge){
|
||||||
resourceHandlerFactory.RegisterHandler(FormNotificationBase.AppLogo);
|
var resourceRequestHandler = new ResourceRequestHandlerBrowser();
|
||||||
resourceHandlerFactory.RegisterHandler(TwitterUtils.LoadingSpinner);
|
resourceHandlers = resourceRequestHandler.ResourceHandlers;
|
||||||
|
|
||||||
|
resourceHandlers.Register(FormNotificationBase.AppLogo);
|
||||||
|
resourceHandlers.Register(TwitterUtils.LoadingSpinner);
|
||||||
|
|
||||||
RequestHandlerBrowser requestHandler = new RequestHandlerBrowser();
|
RequestHandlerBrowser requestHandler = new RequestHandlerBrowser();
|
||||||
|
|
||||||
@@ -62,7 +66,7 @@ namespace TweetDuck.Browser{
|
|||||||
KeyboardHandler = new KeyboardHandlerBrowser(owner),
|
KeyboardHandler = new KeyboardHandlerBrowser(owner),
|
||||||
LifeSpanHandler = new LifeSpanHandler(),
|
LifeSpanHandler = new LifeSpanHandler(),
|
||||||
RequestHandler = requestHandler,
|
RequestHandler = requestHandler,
|
||||||
ResourceHandlerFactory = resourceHandlerFactory
|
ResourceRequestHandlerFactory = resourceRequestHandler.SelfFactory
|
||||||
};
|
};
|
||||||
|
|
||||||
this.browser.LoadingStateChanged += browser_LoadingStateChanged;
|
this.browser.LoadingStateChanged += browser_LoadingStateChanged;
|
||||||
@@ -70,8 +74,8 @@ namespace TweetDuck.Browser{
|
|||||||
this.browser.FrameLoadEnd += browser_FrameLoadEnd;
|
this.browser.FrameLoadEnd += browser_FrameLoadEnd;
|
||||||
this.browser.LoadError += browser_LoadError;
|
this.browser.LoadError += browser_LoadError;
|
||||||
|
|
||||||
this.browser.RegisterAsyncJsObject("$TD", tdBridge);
|
this.browser.RegisterJsBridge("$TD", tdBridge);
|
||||||
this.browser.RegisterAsyncJsObject("$TDU", updateBridge);
|
this.browser.RegisterJsBridge("$TDU", updateBridge);
|
||||||
|
|
||||||
this.browser.BrowserSettings.BackgroundColor = (uint)TwitterUtils.BackgroundColor.ToArgb();
|
this.browser.BrowserSettings.BackgroundColor = (uint)TwitterUtils.BackgroundColor.ToArgb();
|
||||||
this.browser.Dock = DockStyle.None;
|
this.browser.Dock = DockStyle.None;
|
||||||
@@ -79,7 +83,7 @@ namespace TweetDuck.Browser{
|
|||||||
this.browser.SetupZoomEvents();
|
this.browser.SetupZoomEvents();
|
||||||
|
|
||||||
owner.Controls.Add(browser);
|
owner.Controls.Add(browser);
|
||||||
plugins.Register(PluginEnvironment.Browser, new PluginDispatcher(browser));
|
plugins.Register(PluginEnvironment.Browser, new PluginDispatcher(browser, TwitterUrls.IsTweetDeck));
|
||||||
|
|
||||||
Config.MuteToggled += Config_MuteToggled;
|
Config.MuteToggled += Config_MuteToggled;
|
||||||
Config.SoundNotificationChanged += Config_SoundNotificationInfoChanged;
|
Config.SoundNotificationChanged += Config_SoundNotificationInfoChanged;
|
||||||
@@ -87,6 +91,12 @@ namespace TweetDuck.Browser{
|
|||||||
|
|
||||||
// setup and management
|
// setup and management
|
||||||
|
|
||||||
|
public void PrepareSize(Size size){
|
||||||
|
if (!Ready){
|
||||||
|
browser.Size = size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void OnBrowserReady(){
|
private void OnBrowserReady(){
|
||||||
if (!Ready){
|
if (!Ready){
|
||||||
browser.Location = Point.Empty;
|
browser.Location = Point.Empty;
|
||||||
@@ -127,7 +137,7 @@ namespace TweetDuck.Browser{
|
|||||||
|
|
||||||
if (TwitterUrls.IsTwitter(url)){
|
if (TwitterUrls.IsTwitter(url)){
|
||||||
string css = Program.Resources.Load("styles/twitter.css");
|
string css = Program.Resources.Load("styles/twitter.css");
|
||||||
resourceHandlerFactory.RegisterHandler(TwitterStyleUrl, ResourceHandler.FromString(css, mimeType: "text/css"));
|
resourceHandlers.Register(TwitterStyleUrl, ResourceHandlers.ForString(css, "text/css"));
|
||||||
|
|
||||||
CefScriptExecutor.RunFile(frame, "twitter.js");
|
CefScriptExecutor.RunFile(frame, "twitter.js");
|
||||||
}
|
}
|
||||||
@@ -166,7 +176,7 @@ namespace TweetDuck.Browser{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (url == ErrorUrl){
|
if (url == ErrorUrl){
|
||||||
resourceHandlerFactory.UnregisterHandler(ErrorUrl);
|
resourceHandlers.Unregister(ErrorUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,7 +192,7 @@ namespace TweetDuck.Browser{
|
|||||||
string errorName = Enum.GetName(typeof(CefErrorCode), e.ErrorCode);
|
string errorName = Enum.GetName(typeof(CefErrorCode), e.ErrorCode);
|
||||||
string errorTitle = StringUtils.ConvertPascalCaseToScreamingSnakeCase(errorName ?? string.Empty);
|
string errorTitle = StringUtils.ConvertPascalCaseToScreamingSnakeCase(errorName ?? string.Empty);
|
||||||
|
|
||||||
resourceHandlerFactory.RegisterHandler(ErrorUrl, ResourceHandler.FromString(errorPage.Replace("{err}", errorTitle)));
|
resourceHandlers.Register(ErrorUrl, ResourceHandlers.ForString(errorPage.Replace("{err}", errorTitle)));
|
||||||
browser.Load(ErrorUrl);
|
browser.Load(ErrorUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -202,10 +212,10 @@ namespace TweetDuck.Browser{
|
|||||||
prevSoundNotificationPath = newNotificationPath;
|
prevSoundNotificationPath = newNotificationPath;
|
||||||
|
|
||||||
if (hasCustomSound){
|
if (hasCustomSound){
|
||||||
resourceHandlerFactory.RegisterHandler(soundUrl, SoundNotification.CreateFileHandler(newNotificationPath));
|
resourceHandlers.Register(soundUrl, SoundNotification.CreateFileHandler(newNotificationPath));
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
resourceHandlerFactory.UnregisterHandler(soundUrl);
|
resourceHandlers.Unregister(soundUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using TweetDuck.Browser.Data;
|
using TweetDuck.Browser.Data;
|
||||||
using TweetLib.Core.Features.Configuration;
|
|
||||||
using TweetLib.Core.Features.Plugins.Config;
|
using TweetLib.Core.Features.Plugins.Config;
|
||||||
using TweetLib.Core.Serialization.Converters;
|
using TweetLib.Core.Serialization.Converters;
|
||||||
|
using TweetLib.Core.Systems.Configuration;
|
||||||
using TweetLib.Core.Utils;
|
using TweetLib.Core.Utils;
|
||||||
|
|
||||||
namespace TweetDuck.Configuration{
|
namespace TweetDuck.Configuration{
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using TweetLib.Core.Features.Configuration;
|
|
||||||
using TweetLib.Core.Features.Plugins;
|
using TweetLib.Core.Features.Plugins;
|
||||||
using TweetLib.Core.Features.Plugins.Config;
|
using TweetLib.Core.Features.Plugins.Config;
|
||||||
using TweetLib.Core.Features.Plugins.Events;
|
using TweetLib.Core.Features.Plugins.Events;
|
||||||
|
using TweetLib.Core.Systems.Configuration;
|
||||||
|
|
||||||
namespace TweetDuck.Configuration{
|
namespace TweetDuck.Configuration{
|
||||||
sealed class PluginConfig : BaseConfig, IPluginConfig{
|
sealed class PluginConfig : BaseConfig, IPluginConfig{
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
using TweetLib.Core.Features.Configuration;
|
using TweetLib.Core.Systems.Configuration;
|
||||||
|
|
||||||
namespace TweetDuck.Configuration{
|
namespace TweetDuck.Configuration{
|
||||||
sealed class SystemConfig : BaseConfig{
|
sealed class SystemConfig : BaseConfig{
|
||||||
|
@@ -3,9 +3,9 @@ using System.Drawing;
|
|||||||
using TweetDuck.Browser;
|
using TweetDuck.Browser;
|
||||||
using TweetDuck.Browser.Data;
|
using TweetDuck.Browser.Data;
|
||||||
using TweetDuck.Controls;
|
using TweetDuck.Controls;
|
||||||
using TweetLib.Core.Features.Configuration;
|
|
||||||
using TweetLib.Core.Features.Notifications;
|
using TweetLib.Core.Features.Notifications;
|
||||||
using TweetLib.Core.Features.Twitter;
|
using TweetLib.Core.Features.Twitter;
|
||||||
|
using TweetLib.Core.Systems.Configuration;
|
||||||
|
|
||||||
namespace TweetDuck.Configuration{
|
namespace TweetDuck.Configuration{
|
||||||
sealed class UserConfig : BaseConfig{
|
sealed class UserConfig : BaseConfig{
|
||||||
|
@@ -17,7 +17,7 @@ namespace TweetDuck.Dialogs{
|
|||||||
private const string GuideUrl = "https://tweetduck.chylex.com/guide/v2/";
|
private const string GuideUrl = "https://tweetduck.chylex.com/guide/v2/";
|
||||||
private const string GuidePathRegex = @"^guide(?:/v\d+)?(?:/(#.*))?";
|
private const string GuidePathRegex = @"^guide(?:/v\d+)?(?:/(#.*))?";
|
||||||
|
|
||||||
private static readonly ResourceLink DummyPage = new ResourceLink("http://td/dummy", ResourceHandler.FromString(""));
|
private static readonly ResourceLink DummyPage = new ResourceLink("http://td/dummy", ResourceHandlers.ForString(string.Empty));
|
||||||
|
|
||||||
public static bool CheckGuideUrl(string url, out string hash){
|
public static bool CheckGuideUrl(string url, out string hash){
|
||||||
if (!url.Contains("//tweetduck.chylex.com/guide")){
|
if (!url.Contains("//tweetduck.chylex.com/guide")){
|
||||||
@@ -69,8 +69,8 @@ namespace TweetDuck.Dialogs{
|
|||||||
Size = new Size(owner.Size.Width * 3 / 4, owner.Size.Height * 3 / 4);
|
Size = new Size(owner.Size.Width * 3 / 4, owner.Size.Height * 3 / 4);
|
||||||
VisibleChanged += (sender, args) => this.MoveToCenter(owner);
|
VisibleChanged += (sender, args) => this.MoveToCenter(owner);
|
||||||
|
|
||||||
ResourceHandlerFactory resourceHandlerFactory = new ResourceHandlerFactory();
|
var resourceRequestHandler = new ResourceRequestHandlerBase();
|
||||||
resourceHandlerFactory.RegisterHandler(DummyPage);
|
resourceRequestHandler.ResourceHandlers.Register(DummyPage);
|
||||||
|
|
||||||
this.browser = new ChromiumWebBrowser(url){
|
this.browser = new ChromiumWebBrowser(url){
|
||||||
MenuHandler = new ContextMenuGuide(owner),
|
MenuHandler = new ContextMenuGuide(owner),
|
||||||
@@ -78,7 +78,7 @@ namespace TweetDuck.Dialogs{
|
|||||||
KeyboardHandler = new KeyboardHandlerBase(),
|
KeyboardHandler = new KeyboardHandlerBase(),
|
||||||
LifeSpanHandler = new LifeSpanHandler(),
|
LifeSpanHandler = new LifeSpanHandler(),
|
||||||
RequestHandler = new RequestHandlerBase(true),
|
RequestHandler = new RequestHandlerBase(true),
|
||||||
ResourceHandlerFactory = resourceHandlerFactory
|
ResourceRequestHandlerFactory = resourceRequestHandler.SelfFactory
|
||||||
};
|
};
|
||||||
|
|
||||||
browser.LoadingStateChanged += browser_LoadingStateChanged;
|
browser.LoadingStateChanged += browser_LoadingStateChanged;
|
||||||
|
@@ -12,7 +12,7 @@ using TweetDuck.Management;
|
|||||||
using TweetDuck.Management.Analytics;
|
using TweetDuck.Management.Analytics;
|
||||||
using TweetDuck.Utils;
|
using TweetDuck.Utils;
|
||||||
using TweetLib.Core.Features.Plugins;
|
using TweetLib.Core.Features.Plugins;
|
||||||
using TweetLib.Core.Features.Updates;
|
using TweetLib.Core.Systems.Updates;
|
||||||
|
|
||||||
namespace TweetDuck.Dialogs{
|
namespace TweetDuck.Dialogs{
|
||||||
sealed partial class FormSettings : Form, FormManager.IAppDialog{
|
sealed partial class FormSettings : Form, FormManager.IAppDialog{
|
||||||
|
@@ -5,7 +5,7 @@ using System.Windows.Forms;
|
|||||||
using TweetDuck.Browser.Handling.General;
|
using TweetDuck.Browser.Handling.General;
|
||||||
using TweetDuck.Controls;
|
using TweetDuck.Controls;
|
||||||
using TweetDuck.Utils;
|
using TweetDuck.Utils;
|
||||||
using TweetLib.Core.Features.Updates;
|
using TweetLib.Core.Systems.Updates;
|
||||||
using TweetLib.Core.Utils;
|
using TweetLib.Core.Utils;
|
||||||
|
|
||||||
namespace TweetDuck.Dialogs.Settings{
|
namespace TweetDuck.Dialogs.Settings{
|
||||||
|
@@ -23,7 +23,6 @@ namespace TweetDuck.Dialogs.Settings{
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
this.notification.Activated += notification_Activated;
|
|
||||||
this.notification.Show();
|
this.notification.Show();
|
||||||
|
|
||||||
Disposed += (sender, args) => this.notification.Dispose();
|
Disposed += (sender, args) => this.notification.Dispose();
|
||||||
@@ -139,11 +138,6 @@ namespace TweetDuck.Dialogs.Settings{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notification_Activated(object sender, EventArgs e){
|
|
||||||
notification.Hide();
|
|
||||||
notification.Activated -= notification_Activated;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void notification_Move(object sender, EventArgs e){
|
private void notification_Move(object sender, EventArgs e){
|
||||||
if (radioLocCustom.Checked && notification.Location != ControlExtensions.InvisibleLocation){
|
if (radioLocCustom.Checked && notification.Location != ControlExtensions.InvisibleLocation){
|
||||||
Config.CustomNotificationPosition = notification.Location;
|
Config.CustomNotificationPosition = notification.Location;
|
||||||
|
@@ -72,6 +72,14 @@ namespace TweetDuck.Dialogs.Settings{
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (dialog.ShowDialog() == DialogResult.OK){
|
if (dialog.ShowDialog() == DialogResult.OK){
|
||||||
|
try{
|
||||||
|
if (new FileInfo(dialog.FileName).Length > (1024 * 1024) && !FormMessage.Warning("Sound Notification", "The sound file is larger than 1 MB, this will cause increased memory usage. Use this file anyway?", FormMessage.Yes, FormMessage.No)){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}catch{
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
tbCustomSound.Text = dialog.FileName;
|
tbCustomSound.Text = dialog.FileName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
129
Management/LockManager.cs
Normal file
129
Management/LockManager.cs
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using TweetDuck.Dialogs;
|
||||||
|
using TweetDuck.Utils;
|
||||||
|
using TweetLib.Core.Systems.Startup;
|
||||||
|
|
||||||
|
namespace TweetDuck.Management{
|
||||||
|
sealed class LockManager{
|
||||||
|
private const int WaitRetryDelay = 250;
|
||||||
|
private const int RestoreFailTimeout = 2000;
|
||||||
|
private const int CloseNaturallyTimeout = 10000;
|
||||||
|
private const int CloseKillTimeout = 5000;
|
||||||
|
|
||||||
|
private readonly LockFile lockFile;
|
||||||
|
|
||||||
|
public LockManager(string path){
|
||||||
|
this.lockFile = new LockFile(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Lock(bool wasRestarted){
|
||||||
|
return wasRestarted ? LaunchAfterRestart() : LaunchNormally();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Unlock(){
|
||||||
|
return lockFile.Unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Locking
|
||||||
|
|
||||||
|
private bool LaunchNormally(){
|
||||||
|
LockResult lockResult = lockFile.Lock();
|
||||||
|
|
||||||
|
if (lockResult is LockResult.HasProcess info){
|
||||||
|
if (!RestoreProcess(info.Process) && FormMessage.Error("TweetDuck is Already Running", "Another instance of TweetDuck is already running.\nDo you want to close it?", FormMessage.Yes, FormMessage.No)){
|
||||||
|
if (!CloseProcess(info.Process)){
|
||||||
|
FormMessage.Error("TweetDuck Has Failed :(", "Could not close the other process.", FormMessage.OK);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
info.Dispose();
|
||||||
|
lockResult = lockFile.Lock();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lockResult is LockResult.Fail fail){
|
||||||
|
ShowGenericException(fail);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (lockResult != LockResult.Success){
|
||||||
|
FormMessage.Error("TweetDuck Has Failed :(", "An unknown error occurred accessing the data folder. Please, make sure TweetDuck is not already running. If the problem persists, try restarting your system.", FormMessage.OK);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool LaunchAfterRestart(){
|
||||||
|
LockResult lockResult = lockFile.LockWait(10000, WaitRetryDelay);
|
||||||
|
|
||||||
|
while(lockResult != LockResult.Success){
|
||||||
|
if (lockResult is LockResult.Fail fail){
|
||||||
|
ShowGenericException(fail);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (!FormMessage.Warning("TweetDuck Cannot Restart", "TweetDuck is taking too long to close.", FormMessage.Retry, FormMessage.Exit)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
lockResult = lockFile.LockWait(5000, WaitRetryDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
|
||||||
|
private static void ShowGenericException(LockResult.Fail fail){
|
||||||
|
Program.Reporter.HandleException("TweetDuck Has Failed :(", "An unknown error occurred accessing the data folder. Please, make sure TweetDuck is not already running. If the problem persists, try restarting your system.", false, fail.Exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool RestoreProcess(Process process){
|
||||||
|
if (process.MainWindowHandle == IntPtr.Zero){ // restore if the original process is in tray
|
||||||
|
NativeMethods.BroadcastMessage(Program.WindowRestoreMessage, (uint)process.Id, 0);
|
||||||
|
|
||||||
|
if (WindowsUtils.TrySleepUntil(() => CheckProcessExited(process) || (process.MainWindowHandle != IntPtr.Zero && process.Responding), RestoreFailTimeout, WaitRetryDelay)){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool CloseProcess(Process process){
|
||||||
|
try{
|
||||||
|
if (process.CloseMainWindow()){
|
||||||
|
// ReSharper disable once AccessToDisposedClosure
|
||||||
|
WindowsUtils.TrySleepUntil(() => CheckProcessExited(process), CloseNaturallyTimeout, WaitRetryDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!process.HasExited){
|
||||||
|
process.Kill();
|
||||||
|
// ReSharper disable once AccessToDisposedClosure
|
||||||
|
WindowsUtils.TrySleepUntil(() => CheckProcessExited(process), CloseKillTimeout, WaitRetryDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.HasExited){
|
||||||
|
process.Dispose();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}catch(Exception ex) when (ex is InvalidOperationException || ex is Win32Exception){
|
||||||
|
bool hasExited = CheckProcessExited(process);
|
||||||
|
process.Dispose();
|
||||||
|
return hasExited;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool CheckProcessExited(Process process){
|
||||||
|
process.Refresh();
|
||||||
|
return process.HasExited;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using CefSharp;
|
using CefSharp;
|
||||||
using TweetDuck.Browser.Adapters;
|
using TweetDuck.Browser.Adapters;
|
||||||
|
using TweetDuck.Utils;
|
||||||
using TweetLib.Core.Browser;
|
using TweetLib.Core.Browser;
|
||||||
using TweetLib.Core.Features.Plugins;
|
using TweetLib.Core.Features.Plugins;
|
||||||
using TweetLib.Core.Features.Plugins.Events;
|
using TweetLib.Core.Features.Plugins.Events;
|
||||||
@@ -12,21 +13,23 @@ namespace TweetDuck.Plugins{
|
|||||||
|
|
||||||
private readonly IWebBrowser browser;
|
private readonly IWebBrowser browser;
|
||||||
private readonly IScriptExecutor executor;
|
private readonly IScriptExecutor executor;
|
||||||
|
private readonly Func<string, bool> executeOnUrl;
|
||||||
|
|
||||||
public PluginDispatcher(IWebBrowser browser){
|
public PluginDispatcher(IWebBrowser browser, Func<string, bool> executeOnUrl){
|
||||||
|
this.executeOnUrl = executeOnUrl;
|
||||||
this.browser = browser;
|
this.browser = browser;
|
||||||
this.browser.FrameLoadEnd += browser_FrameLoadEnd;
|
this.browser.FrameLoadEnd += browser_FrameLoadEnd;
|
||||||
this.executor = new CefScriptExecutor(browser);
|
this.executor = new CefScriptExecutor(browser);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IPluginDispatcher.AttachBridge(string name, object bridge){
|
void IPluginDispatcher.AttachBridge(string name, object bridge){
|
||||||
browser.RegisterAsyncJsObject(name, bridge);
|
browser.RegisterJsBridge(name, bridge);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
|
private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
|
||||||
IFrame frame = e.Frame;
|
IFrame frame = e.Frame;
|
||||||
|
|
||||||
if (frame.IsMain && TwitterUrls.IsTweetDeck(frame.Url)){
|
if (frame.IsMain && executeOnUrl(frame.Url)){
|
||||||
Ready?.Invoke(this, new PluginDispatchEventArgs(executor));
|
Ready?.Invoke(this, new PluginDispatchEventArgs(executor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
47
Plugins/PluginSchemeFactory.cs
Normal file
47
Plugins/PluginSchemeFactory.cs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text;
|
||||||
|
using CefSharp;
|
||||||
|
using TweetLib.Core.Browser;
|
||||||
|
using TweetLib.Core.Features.Plugins;
|
||||||
|
|
||||||
|
namespace TweetDuck.Plugins{
|
||||||
|
sealed class PluginSchemeFactory : ISchemeHandlerFactory{
|
||||||
|
public const string Name = PluginSchemeHandler<IResourceHandler>.Name;
|
||||||
|
|
||||||
|
private readonly PluginSchemeHandler<IResourceHandler> handler = new PluginSchemeHandler<IResourceHandler>(new ResourceProvider());
|
||||||
|
|
||||||
|
internal void Setup(PluginManager plugins){
|
||||||
|
handler.Setup(plugins);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request){
|
||||||
|
return handler.Process(request.Url);
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class ResourceProvider : IResourceProvider<IResourceHandler>{
|
||||||
|
private static ResourceHandler CreateHandler(byte[] bytes){
|
||||||
|
var handler = ResourceHandler.FromStream(new MemoryStream(bytes), autoDisposeStream: true);
|
||||||
|
handler.Headers.Set("Access-Control-Allow-Origin", "*");
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IResourceHandler Status(HttpStatusCode code, string message){
|
||||||
|
var handler = CreateHandler(Encoding.UTF8.GetBytes(message));
|
||||||
|
handler.StatusCode = (int)code;
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IResourceHandler File(byte[] bytes, string extension){
|
||||||
|
if (bytes.Length == 0){
|
||||||
|
return Status(HttpStatusCode.NoContent, "File is empty."); // FromByteArray crashes CEF internals with no contents
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
var handler = CreateHandler(bytes);
|
||||||
|
handler.MimeType = Cef.GetMimeType(extension);
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
65
Program.cs
65
Program.cs
@@ -11,10 +11,10 @@ using TweetDuck.Browser.Handling.General;
|
|||||||
using TweetDuck.Configuration;
|
using TweetDuck.Configuration;
|
||||||
using TweetDuck.Dialogs;
|
using TweetDuck.Dialogs;
|
||||||
using TweetDuck.Management;
|
using TweetDuck.Management;
|
||||||
|
using TweetDuck.Plugins;
|
||||||
using TweetDuck.Resources;
|
using TweetDuck.Resources;
|
||||||
using TweetDuck.Utils;
|
using TweetDuck.Utils;
|
||||||
using TweetLib.Core;
|
using TweetLib.Core;
|
||||||
using TweetLib.Core.Application.Helpers;
|
|
||||||
using TweetLib.Core.Collections;
|
using TweetLib.Core.Collections;
|
||||||
using TweetLib.Core.Utils;
|
using TweetLib.Core.Utils;
|
||||||
using Win = System.Windows.Forms;
|
using Win = System.Windows.Forms;
|
||||||
@@ -71,7 +71,6 @@ namespace TweetDuck{
|
|||||||
|
|
||||||
Lib.Initialize(new App.Builder{
|
Lib.Initialize(new App.Builder{
|
||||||
ErrorHandler = Reporter,
|
ErrorHandler = Reporter,
|
||||||
LockHandler = new LockHandler(),
|
|
||||||
SystemHandler = new SystemHandler(),
|
SystemHandler = new SystemHandler(),
|
||||||
ResourceHandler = Resources
|
ResourceHandler = Resources
|
||||||
});
|
});
|
||||||
@@ -94,43 +93,9 @@ namespace TweetDuck{
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Arguments.HasFlag(Arguments.ArgRestart)){
|
if (!LockManager.Lock(Arguments.HasFlag(Arguments.ArgRestart))){
|
||||||
LockManager.Result lockResult = LockManager.LockWait(10000);
|
|
||||||
|
|
||||||
while(lockResult != LockManager.Result.Success){
|
|
||||||
if (lockResult == LockManager.Result.Fail){
|
|
||||||
FormMessage.Error("TweetDuck Has Failed :(", "An unknown error occurred accessing the data folder. Please, make sure TweetDuck is not already running. If the problem persists, try restarting your system.", FormMessage.OK);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (!FormMessage.Warning("TweetDuck Cannot Restart", "TweetDuck is taking too long to close.", FormMessage.Retry, FormMessage.Exit)){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
lockResult = LockManager.LockWait(5000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
LockManager.Result lockResult = LockManager.Lock();
|
|
||||||
|
|
||||||
if (lockResult == LockManager.Result.HasProcess){
|
|
||||||
if (!LockManager.RestoreLockingProcess() && FormMessage.Error("TweetDuck is Already Running", "Another instance of TweetDuck is already running.\nDo you want to close it?", FormMessage.Yes, FormMessage.No)){
|
|
||||||
if (!LockManager.CloseLockingProcess()){
|
|
||||||
FormMessage.Error("TweetDuck Has Failed :(", "Could not close the other process.", FormMessage.OK);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
lockResult = LockManager.Lock();
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lockResult != LockManager.Result.Success){
|
|
||||||
FormMessage.Error("TweetDuck Has Failed :(", "An unknown error occurred accessing the data folder. Please, make sure TweetDuck is not already running. If the problem persists, try restarting your system.", FormMessage.OK);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Config.LoadAll();
|
Config.LoadAll();
|
||||||
|
|
||||||
@@ -143,11 +108,12 @@ namespace TweetDuck{
|
|||||||
|
|
||||||
if (Arguments.HasFlag(Arguments.ArgUpdated)){
|
if (Arguments.HasFlag(Arguments.ArgUpdated)){
|
||||||
WindowsUtils.TryDeleteFolderWhenAble(InstallerPath, 8000);
|
WindowsUtils.TryDeleteFolderWhenAble(InstallerPath, 8000);
|
||||||
|
WindowsUtils.TryDeleteFolderWhenAble(Path.Combine(StoragePath, "Service Worker"), 4000);
|
||||||
BrowserCache.TryClearNow();
|
BrowserCache.TryClearNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
try{
|
try{
|
||||||
RequestHandlerBase.LoadResourceRewriteRules(Arguments.GetValue(Arguments.ArgFreeze));
|
ResourceRequestHandlerBase.LoadResourceRewriteRules(Arguments.GetValue(Arguments.ArgFreeze));
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
FormMessage.Error("Resource Freeze", "Error parsing resource rewrite rules: " + e.Message, FormMessage.OK);
|
FormMessage.Error("Resource Freeze", "Error parsing resource rewrite rules: " + e.Message, FormMessage.OK);
|
||||||
return;
|
return;
|
||||||
@@ -160,7 +126,7 @@ namespace TweetDuck{
|
|||||||
|
|
||||||
CefSettings settings = new CefSettings{
|
CefSettings settings = new CefSettings{
|
||||||
UserAgent = BrowserUtils.UserAgentChrome,
|
UserAgent = BrowserUtils.UserAgentChrome,
|
||||||
BrowserSubprocessPath = BrandName + ".Browser.exe",
|
BrowserSubprocessPath = Path.Combine(ProgramPath, BrandName + ".Browser.exe"),
|
||||||
CachePath = StoragePath,
|
CachePath = StoragePath,
|
||||||
UserDataPath = CefDataPath,
|
UserDataPath = CefDataPath,
|
||||||
LogFile = ConsoleLogFilePath,
|
LogFile = ConsoleLogFilePath,
|
||||||
@@ -169,6 +135,17 @@ namespace TweetDuck{
|
|||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var pluginScheme = new PluginSchemeFactory();
|
||||||
|
|
||||||
|
settings.RegisterScheme(new CefCustomScheme{
|
||||||
|
SchemeName = PluginSchemeFactory.Name,
|
||||||
|
IsStandard = false,
|
||||||
|
IsSecure = true,
|
||||||
|
IsCorsEnabled = true,
|
||||||
|
IsCSPBypassing = true,
|
||||||
|
SchemeHandlerFactory = pluginScheme
|
||||||
|
});
|
||||||
|
|
||||||
CommandLineArgs.ReadCefArguments(Config.User.CustomCefArgs).ToDictionary(settings.CefCommandLineArgs);
|
CommandLineArgs.ReadCefArguments(Config.User.CustomCefArgs).ToDictionary(settings.CefCommandLineArgs);
|
||||||
BrowserUtils.SetupCefArgs(settings.CefCommandLineArgs);
|
BrowserUtils.SetupCefArgs(settings.CefCommandLineArgs);
|
||||||
|
|
||||||
@@ -176,18 +153,14 @@ namespace TweetDuck{
|
|||||||
|
|
||||||
Win.Application.ApplicationExit += (sender, args) => ExitCleanup();
|
Win.Application.ApplicationExit += (sender, args) => ExitCleanup();
|
||||||
|
|
||||||
FormBrowser mainForm = new FormBrowser();
|
FormBrowser mainForm = new FormBrowser(pluginScheme);
|
||||||
Resources.Initialize(mainForm);
|
Resources.Initialize(mainForm);
|
||||||
Win.Application.Run(mainForm);
|
Win.Application.Run(mainForm);
|
||||||
|
|
||||||
if (mainForm.UpdateInstallerPath != null){
|
if (mainForm.UpdateInstaller != null){
|
||||||
ExitCleanup();
|
ExitCleanup();
|
||||||
|
|
||||||
// ProgramPath has a trailing backslash
|
if (mainForm.UpdateInstaller.Launch()){
|
||||||
string updaterArgs = "/SP- /SILENT /FORCECLOSEAPPLICATIONS /UPDATEPATH=\"" + ProgramPath + "\" /RUNARGS=\"" + Arguments.GetCurrentForInstallerCmd() + "\"" + (IsPortable ? " /PORTABLE=1" : "");
|
|
||||||
bool runElevated = !IsPortable || !FileUtils.CheckFolderWritePermission(ProgramPath);
|
|
||||||
|
|
||||||
if (WindowsUtils.OpenAssociatedProgram(mainForm.UpdateInstallerPath, updaterArgs, runElevated)){
|
|
||||||
Win.Application.Exit();
|
Win.Application.Exit();
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
|
@@ -68,6 +68,7 @@ let main (argv: string[]) =
|
|||||||
line.TrimStart()
|
line.TrimStart()
|
||||||
|
|
||||||
// Functions (File Management)
|
// Functions (File Management)
|
||||||
|
|
||||||
let copyFile source target =
|
let copyFile source target =
|
||||||
File.Copy(source, target, true)
|
File.Copy(source, target, true)
|
||||||
|
|
||||||
@@ -94,7 +95,7 @@ let main (argv: string[]) =
|
|||||||
// Functions (File Processing)
|
// Functions (File Processing)
|
||||||
|
|
||||||
let byPattern path pattern =
|
let byPattern path pattern =
|
||||||
Directory.EnumerateFiles(path, pattern, SearchOption.AllDirectories)
|
Directory.EnumerateFiles(path, pattern, SearchOption.AllDirectories) |> Seq.filter (fun (file: string) -> not (file.Contains(importsDir)))
|
||||||
|
|
||||||
let exceptEndingWith name =
|
let exceptEndingWith name =
|
||||||
Seq.filter (fun (file: string) -> not (file.EndsWith(name)))
|
Seq.filter (fun (file: string) -> not (file.EndsWith(name)))
|
||||||
@@ -121,7 +122,7 @@ let main (argv: string[]) =
|
|||||||
let includeVersion = relativePath.StartsWith(@"scripts\") && not (relativePath.StartsWith(@"scripts\imports\"))
|
let includeVersion = relativePath.StartsWith(@"scripts\") && not (relativePath.StartsWith(@"scripts\imports\"))
|
||||||
let finalLines = if includeVersion then seq { yield "#" + version; yield! lines } else lines
|
let finalLines = if includeVersion then seq { yield "#" + version; yield! lines } else lines
|
||||||
|
|
||||||
File.WriteAllLines(fullPath, finalLines |> filterNotEmpty |> Seq.toArray)
|
File.WriteAllLines(fullPath, finalLines |> Seq.toArray)
|
||||||
printfn "Processed %s" relativePath
|
printfn "Processed %s" relativePath
|
||||||
|
|
||||||
let processFiles (files: string seq) (extProcessors: IDictionary<string, (string seq -> string seq)>) =
|
let processFiles (files: string seq) (extProcessors: IDictionary<string, (string seq -> string seq)>) =
|
||||||
@@ -184,6 +185,7 @@ let main (argv: string[]) =
|
|||||||
|> replaceRegex @"^(.*?)((?<=^|[;{}()])\s?//(?:\s.*|$))?$" "$1"
|
|> replaceRegex @"^(.*?)((?<=^|[;{}()])\s?//(?:\s.*|$))?$" "$1"
|
||||||
|> replaceRegex @"(?<!\w)(return|throw)(\s.*?)? if (.*?);" "if ($3)$1$2;"
|
|> replaceRegex @"(?<!\w)(return|throw)(\s.*?)? if (.*?);" "if ($3)$1$2;"
|
||||||
)
|
)
|
||||||
|
|> filterNotEmpty
|
||||||
);
|
);
|
||||||
|
|
||||||
".css", (fun (lines: string seq) ->
|
".css", (fun (lines: string seq) ->
|
||||||
@@ -204,6 +206,7 @@ let main (argv: string[]) =
|
|||||||
".html", (fun (lines: string seq) ->
|
".html", (fun (lines: string seq) ->
|
||||||
lines
|
lines
|
||||||
|> Seq.map trimStart
|
|> Seq.map trimStart
|
||||||
|
|> filterNotEmpty
|
||||||
);
|
);
|
||||||
|
|
||||||
".meta", (fun (lines: string seq) ->
|
".meta", (fun (lines: string seq) ->
|
||||||
|
@@ -34,6 +34,7 @@ try{
|
|||||||
|
|
||||||
$contents = [IO.File]::ReadAllText($browserProj)
|
$contents = [IO.File]::ReadAllText($browserProj)
|
||||||
$contents = $contents -Replace '(?<=<HintPath>\.\.\\packages\\CefSharp\.Common\.)(.*?)(?=\\)', $sharpVersion
|
$contents = $contents -Replace '(?<=<HintPath>\.\.\\packages\\CefSharp\.Common\.)(.*?)(?=\\)', $sharpVersion
|
||||||
|
$contents = $contents -Replace '(?<=<Reference Include="CefSharp, Version=)(\d+)', $sharpVersion.Split(".")[0]
|
||||||
$contents = $contents -Replace '(?<=<Reference Include="CefSharp\.BrowserSubprocess\.Core, Version=)(\d+)', $sharpVersion.Split(".")[0]
|
$contents = $contents -Replace '(?<=<Reference Include="CefSharp\.BrowserSubprocess\.Core, Version=)(\d+)', $sharpVersion.Split(".")[0]
|
||||||
|
|
||||||
[IO.File]::WriteAllText($browserProj, $contents)
|
[IO.File]::WriteAllText($browserProj, $contents)
|
||||||
|
File diff suppressed because it is too large
Load Diff
11
Resources/Scripts/imports/markup/offline.html
Normal file
11
Resources/Scripts/imports/markup/offline.html
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<div id="tweetduck-conn-issues" class="Layer NotificationListLayer">
|
||||||
|
<ul class="NotificationList">
|
||||||
|
<li class="Notification Notification--red" style="height:63px;">
|
||||||
|
<div class="Notification-inner">
|
||||||
|
<div class="Notification-icon"><span class="Icon Icon--medium Icon--circleError"></span></div>
|
||||||
|
<div class="Notification-content"><div class="Notification-body">Experiencing connection issues</div></div>
|
||||||
|
<button type="button" class="Notification-closeButton" aria-label="Close"><span class="Icon Icon--smallest Icon--close" aria-hidden="true"></span></button>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
670
Resources/Scripts/imports/scripts/browser.features.js
Normal file
670
Resources/Scripts/imports/scripts/browser.features.js
Normal file
@@ -0,0 +1,670 @@
|
|||||||
|
(function(){
|
||||||
|
//
|
||||||
|
// Function: Event callback for a new tweet.
|
||||||
|
//
|
||||||
|
const onNewTweet = (function(){
|
||||||
|
const recentMessages = new Set();
|
||||||
|
const recentTweets = new Set();
|
||||||
|
let recentTweetTimer = null;
|
||||||
|
|
||||||
|
const resetRecentTweets = () => {
|
||||||
|
recentTweetTimer = null;
|
||||||
|
recentTweets.clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
const startRecentTweetTimer = () => {
|
||||||
|
recentTweetTimer && window.clearTimeout(recentTweetTimer);
|
||||||
|
recentTweetTimer = window.setTimeout(resetRecentTweets, 20000);
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkTweetCache = (set, id) => {
|
||||||
|
return true if set.has(id);
|
||||||
|
|
||||||
|
if (set.size > 50){
|
||||||
|
set.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
set.add(id);
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isSensitive = (tweet) => {
|
||||||
|
const main = tweet.getMainTweet && tweet.getMainTweet();
|
||||||
|
return true if main && main.possiblySensitive; // TODO these don't show media badges when hiding sensitive media
|
||||||
|
|
||||||
|
const related = tweet.getRelatedTweet && tweet.getRelatedTweet();
|
||||||
|
return true if related && related.possiblySensitive;
|
||||||
|
|
||||||
|
const quoted = tweet.quotedTweet;
|
||||||
|
return true if quoted && quoted.possiblySensitive;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const fixMedia = (html, media) => {
|
||||||
|
return html.find("a[data-media-entity-id='" + media.mediaId + "'], .media-item").first().removeClass("is-zoomable").css("background-image", 'url("' + media.small() + '")');
|
||||||
|
};
|
||||||
|
|
||||||
|
return function(column, tweet){
|
||||||
|
if (tweet instanceof TD.services.TwitterConversation || tweet instanceof TD.services.TwitterConversationMessageEvent){
|
||||||
|
return if checkTweetCache(recentMessages, tweet.id);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return if checkTweetCache(recentTweets, tweet.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
startRecentTweetTimer();
|
||||||
|
|
||||||
|
if (column.model.getHasNotification()){
|
||||||
|
const sensitive = isSensitive(tweet);
|
||||||
|
const previews = $TDX.notificationMediaPreviews && (!sensitive || TD.settings.getDisplaySensitiveMedia());
|
||||||
|
// TODO new cards don't have either previews or links
|
||||||
|
|
||||||
|
const html = $(tweet.render({
|
||||||
|
withFooter: false,
|
||||||
|
withTweetActions: false,
|
||||||
|
withMediaPreview: true,
|
||||||
|
isMediaPreviewOff: !previews,
|
||||||
|
isMediaPreviewSmall: previews,
|
||||||
|
isMediaPreviewLarge: false,
|
||||||
|
isMediaPreviewCompact: false,
|
||||||
|
isMediaPreviewInQuoted: previews,
|
||||||
|
thumbSizeClass: "media-size-medium",
|
||||||
|
mediaPreviewSize: "medium"
|
||||||
|
}));
|
||||||
|
|
||||||
|
html.find("footer").last().remove(); // apparently withTweetActions breaks for certain tweets, nice
|
||||||
|
html.find(".js-quote-detail").removeClass("is-actionable margin-b--8"); // prevent quoted tweets from changing the cursor and reduce bottom margin
|
||||||
|
|
||||||
|
if (previews){
|
||||||
|
html.find(".reverse-image-search").remove();
|
||||||
|
|
||||||
|
const container = html.find(".js-media");
|
||||||
|
|
||||||
|
for(let media of tweet.getMedia()){
|
||||||
|
fixMedia(container, media);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tweet.quotedTweet){
|
||||||
|
for(let media of tweet.quotedTweet.getMedia()){
|
||||||
|
fixMedia(container, media).addClass("media-size-medium");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (tweet instanceof TD.services.TwitterActionOnTweet){
|
||||||
|
html.find(".js-media").remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
html.find("a[data-full-url]").each(function(){ // bypass t.co on all links and fix tooltips
|
||||||
|
this.href = this.getAttribute("data-full-url");
|
||||||
|
this.removeAttribute("title");
|
||||||
|
});
|
||||||
|
|
||||||
|
html.find("a[href='#']").each(function(){ // remove <a> tags around links that don't lead anywhere (such as account names the tweet replied to)
|
||||||
|
this.outerHTML = this.innerHTML;
|
||||||
|
});
|
||||||
|
|
||||||
|
html.find("p.link-complex-target").filter(function(){
|
||||||
|
return $(this).text() === "Show this thread";
|
||||||
|
}).first().each(function(){
|
||||||
|
this.id = "tduck-show-thread";
|
||||||
|
|
||||||
|
const moveBefore = html.find(".tweet-body > .js-media, .tweet-body > .js-media-preview-container, .quoted-tweet");
|
||||||
|
|
||||||
|
if (moveBefore){
|
||||||
|
$(this).css("margin-top", "5px").removeClass("margin-b--5").parent("span").detach().insertBefore(moveBefore);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (tweet.quotedTweet){
|
||||||
|
html.find("p.txt-mute").filter(function(){
|
||||||
|
return $(this).text() === "Show this thread";
|
||||||
|
}).first().remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
const type = tweet.getChirpType();
|
||||||
|
|
||||||
|
if (type === "follow"){
|
||||||
|
html.find(".js-user-actions-menu").parent().remove();
|
||||||
|
html.find(".account-bio").removeClass("padding-t--5").css("padding-top", "2px");
|
||||||
|
}
|
||||||
|
else if ((type.startsWith("favorite") || type.startsWith("retweet")) && tweet.isAboutYou()){
|
||||||
|
html.children().first().addClass("td-notification-padded");
|
||||||
|
}
|
||||||
|
else if (type.includes("list_member")){
|
||||||
|
html.children().first().addClass("td-notification-padded td-notification-padded-alt");
|
||||||
|
html.find(".activity-header").css("margin-top", "2px");
|
||||||
|
html.find(".avatar").first().css("margin-bottom", "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sensitive){
|
||||||
|
html.find(".media-badge").each(function(){
|
||||||
|
$(this)[0].lastChild.textContent += " (possibly sensitive)";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const source = tweet.getRelatedTweet();
|
||||||
|
const duration = source ? source.text.length + (source.quotedTweet ? source.quotedTweet.text.length : 0) : tweet.text.length;
|
||||||
|
|
||||||
|
const chirpId = source ? source.id : "";
|
||||||
|
const tweetUrl = source ? source.getChirpURL() : "";
|
||||||
|
const quoteUrl = source && source.quotedTweet ? source.quotedTweet.getChirpURL() : "";
|
||||||
|
|
||||||
|
$TD.onTweetPopup(column.model.privateState.apiid, chirpId, window.TDGF_getColumnName(column), html.html(), duration, tweetUrl, quoteUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (column.model.getHasSound()){
|
||||||
|
$TD.onTweetSound();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Enable popup notifications.
|
||||||
|
//
|
||||||
|
execSafe(function hookDesktopNotifications(){
|
||||||
|
ensurePropertyExists(TD, "controller", "notifications");
|
||||||
|
|
||||||
|
TD.controller.notifications.hasNotifications = function(){
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
TD.controller.notifications.isPermissionGranted = function(){
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
$.subscribe("/notifications/new", function(obj){
|
||||||
|
for(let index = obj.items.length - 1; index >= 0; index--){
|
||||||
|
onNewTweet(obj.column, obj.items[index]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Fix DM notifications not showing if the conversation is open.
|
||||||
|
//
|
||||||
|
if (checkPropertyExists(TD, "vo", "Column", "prototype", "mergeMissingChirps")){
|
||||||
|
TD.vo.Column.prototype.mergeMissingChirps = prependToFunction(TD.vo.Column.prototype.mergeMissingChirps, function(e){
|
||||||
|
const model = this.model;
|
||||||
|
|
||||||
|
if (model && model.state && model.state.type === "privateMe" && !this.notificationsDisabled && e.poller.feed.managed){
|
||||||
|
const unread = [];
|
||||||
|
|
||||||
|
for(let chirp of e.chirps){
|
||||||
|
if (Array.isArray(chirp.messages)){
|
||||||
|
Array.prototype.push.apply(unread, chirp.messages.filter(message => message.read === false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unread.length > 0){
|
||||||
|
if (checkPropertyExists(TD, "util", "chirpReverseColumnSort")){
|
||||||
|
unread.sort(TD.util.chirpReverseColumnSort);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(let message of unread){
|
||||||
|
onNewTweet(this, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO sound notifications are borked as well
|
||||||
|
// TODO figure out what to do with missed notifications at startup
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Mute sound notifications.
|
||||||
|
//
|
||||||
|
HTMLAudioElement.prototype.play = prependToFunction(HTMLAudioElement.prototype.play, function(){
|
||||||
|
return $TDX.muteNotifications;
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Add additional link information to context menu.
|
||||||
|
//
|
||||||
|
execSafe(function setupLinkContextMenu(){
|
||||||
|
$(document.body).delegate("a", "contextmenu", function(){
|
||||||
|
const me = $(this)[0];
|
||||||
|
|
||||||
|
if (me.classList.contains("js-media-image-link")){
|
||||||
|
const hovered = getHoveredTweet();
|
||||||
|
return if !hovered;
|
||||||
|
|
||||||
|
const tweet = hovered.obj.hasMedia() ? hovered.obj : hovered.obj.quotedTweet;
|
||||||
|
const media = tweet.getMedia().find(media => media.mediaId === me.getAttribute("data-media-entity-id"));
|
||||||
|
|
||||||
|
if ((media.isVideo && media.service === "twitter") || media.isAnimatedGif){
|
||||||
|
$TD.setRightClickedLink("video", media.chooseVideoVariant().url);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
$TD.setRightClickedLink("image", media.large());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (me.classList.contains("js-gif-play")){
|
||||||
|
$TD.setRightClickedLink("video", $(this).closest(".js-media-gif-container").find("video").attr("src"));
|
||||||
|
}
|
||||||
|
else if (me.hasAttribute("data-full-url")){
|
||||||
|
$TD.setRightClickedLink("link", me.getAttribute("data-full-url"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Add tweet-related options to context menu.
|
||||||
|
//
|
||||||
|
execSafe(function setupTweetContextMenu(){
|
||||||
|
ensurePropertyExists(TD, "controller", "columnManager", "get");
|
||||||
|
ensurePropertyExists(TD, "services", "ChirpBase", "TWEET");
|
||||||
|
ensurePropertyExists(TD, "services", "TwitterActionFollow");
|
||||||
|
|
||||||
|
const processMedia = function(chirp){
|
||||||
|
return chirp.getMedia().filter(item => !item.isAnimatedGif).map(item => item.entity.media_url_https + ":small").join(";");
|
||||||
|
};
|
||||||
|
|
||||||
|
app.delegate("section.js-column", "contextmenu", function(){
|
||||||
|
const hovered = getHoveredTweet();
|
||||||
|
return if !hovered;
|
||||||
|
|
||||||
|
const tweet = hovered.obj;
|
||||||
|
const quote = tweet.quotedTweet;
|
||||||
|
|
||||||
|
if (tweet.chirpType === TD.services.ChirpBase.TWEET){
|
||||||
|
const tweetUrl = tweet.getChirpURL();
|
||||||
|
const quoteUrl = quote && quote.getChirpURL();
|
||||||
|
|
||||||
|
const chirpAuthors = quote ? [ tweet.getMainUser().screenName, quote.getMainUser().screenName ].join(";") : tweet.getMainUser().screenName;
|
||||||
|
const chirpImages = tweet.hasImage() ? processMedia(tweet) : quote && quote.hasImage() ? processMedia(quote) : "";
|
||||||
|
|
||||||
|
$TD.setRightClickedChirp(tweetUrl || "", quoteUrl || "", chirpAuthors, chirpImages);
|
||||||
|
}
|
||||||
|
else if (tweet instanceof TD.services.TwitterActionFollow){
|
||||||
|
$TD.setRightClickedLink("link", tweet.following.getProfileURL());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Expand shortened links on hover or display tooltip.
|
||||||
|
//
|
||||||
|
execSafe(function setupLinkExpansionOrTooltip(){
|
||||||
|
let prevMouseX = -1, prevMouseY = -1;
|
||||||
|
let tooltipTimer, tooltipDisplayed;
|
||||||
|
|
||||||
|
$(document.body).delegate("a[data-full-url]", {
|
||||||
|
mouseenter: function(){
|
||||||
|
const me = $(this);
|
||||||
|
const text = me.text();
|
||||||
|
return if text.charCodeAt(text.length - 1) !== 8230 && text.charCodeAt(0) !== 8230; // horizontal ellipsis
|
||||||
|
|
||||||
|
if ($TDX.expandLinksOnHover){
|
||||||
|
tooltipTimer = window.setTimeout(function(){
|
||||||
|
me.attr("td-prev-text", text);
|
||||||
|
me.text(me.attr("data-full-url").replace(/^https?:\/\/(www\.)?/, ""));
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
me.removeAttr("title");
|
||||||
|
|
||||||
|
tooltipTimer = window.setTimeout(function(){
|
||||||
|
$TD.displayTooltip(me.attr("data-full-url"));
|
||||||
|
tooltipDisplayed = true;
|
||||||
|
}, 400);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mouseleave: function(){
|
||||||
|
const me = $(this)[0];
|
||||||
|
|
||||||
|
if (me.hasAttribute("td-prev-text")){
|
||||||
|
me.innerText = me.getAttribute("td-prev-text");
|
||||||
|
}
|
||||||
|
|
||||||
|
window.clearTimeout(tooltipTimer);
|
||||||
|
|
||||||
|
if (tooltipDisplayed){
|
||||||
|
tooltipDisplayed = false;
|
||||||
|
$TD.displayTooltip(null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mousemove: function(e){
|
||||||
|
if (tooltipDisplayed && (prevMouseX !== e.clientX || prevMouseY !== e.clientY)){
|
||||||
|
$TD.displayTooltip($(this).attr("data-full-url"));
|
||||||
|
prevMouseX = e.clientX;
|
||||||
|
prevMouseY = e.clientY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Support for extra mouse buttons.
|
||||||
|
//
|
||||||
|
execSafe(function supportExtraMouseButtons(){
|
||||||
|
const tryClickSelector = function(selector, parent){
|
||||||
|
return $(selector, parent).click().length;
|
||||||
|
};
|
||||||
|
|
||||||
|
const tryCloseModal1 = function(){
|
||||||
|
const modal = $("#open-modal");
|
||||||
|
return modal.is(":visible") && tryClickSelector("a.mdl-dismiss", modal);
|
||||||
|
};
|
||||||
|
|
||||||
|
const tryCloseModal2 = function(){
|
||||||
|
const modal = $(".js-modals-container");
|
||||||
|
return modal.length && tryClickSelector("a.mdl-dismiss", modal);
|
||||||
|
};
|
||||||
|
|
||||||
|
const tryCloseHighlightedColumn = function(){
|
||||||
|
const column = getHoveredColumn();
|
||||||
|
return false if !column;
|
||||||
|
|
||||||
|
const ele = $(column.ele);
|
||||||
|
return ((ele.is(".is-shifted-2") && tryClickSelector(".js-tweet-social-proof-back", ele)) || (ele.is(".is-shifted-1") && tryClickSelector(".js-column-back", ele)));
|
||||||
|
};
|
||||||
|
|
||||||
|
window.TDGF_onMouseClickExtra = function(button){
|
||||||
|
if (button === 1){ // back button
|
||||||
|
tryClickSelector(".is-shifted-2 .js-tweet-social-proof-back", ".js-modal-panel") ||
|
||||||
|
tryClickSelector(".is-shifted-1 .js-column-back", ".js-modal-panel") ||
|
||||||
|
tryCloseModal1() ||
|
||||||
|
tryCloseModal2() ||
|
||||||
|
tryClickSelector(".js-inline-compose-close") ||
|
||||||
|
tryCloseHighlightedColumn() ||
|
||||||
|
tryClickSelector(".js-app-content.is-open .js-drawer-close:visible") ||
|
||||||
|
tryClickSelector(".is-shifted-2 .js-tweet-social-proof-back, .is-shifted-2 .js-dm-participants-back") ||
|
||||||
|
$(".is-shifted-1 .js-column-back").click();
|
||||||
|
}
|
||||||
|
else if (button === 2){ // forward button
|
||||||
|
const hovered = getHoveredTweet();
|
||||||
|
|
||||||
|
if (hovered){
|
||||||
|
$(hovered.ele).children().first().click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Allow drag & drop behavior for dropping links on columns to open their detail view.
|
||||||
|
//
|
||||||
|
execSafe(function supportDragDropOverColumns(){
|
||||||
|
const regexTweet = /^https?:\/\/twitter\.com\/[A-Za-z0-9_]+\/status\/(\d+)\/?\??/;
|
||||||
|
const regexAccount = /^https?:\/\/twitter\.com\/(?!signup$|tos$|privacy$|search$|search-)([^/?]+)\/?$/;
|
||||||
|
|
||||||
|
let dragType = false;
|
||||||
|
|
||||||
|
const events = {
|
||||||
|
dragover: function(e){
|
||||||
|
e.originalEvent.dataTransfer.dropEffect = dragType ? "all" : "none";
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
},
|
||||||
|
|
||||||
|
drop: function(e){
|
||||||
|
const url = e.originalEvent.dataTransfer.getData("URL");
|
||||||
|
|
||||||
|
if (dragType === "tweet"){
|
||||||
|
const match = regexTweet.exec(url);
|
||||||
|
|
||||||
|
if (match.length === 2){
|
||||||
|
const column = TD.controller.columnManager.get($(this).attr("data-column"));
|
||||||
|
|
||||||
|
if (column){
|
||||||
|
TD.controller.clients.getPreferredClient().show(match[1], function(chirp){
|
||||||
|
TD.ui.updates.showDetailView(column, chirp, column.findChirp(chirp) || chirp);
|
||||||
|
$(document).trigger("uiGridClearSelection");
|
||||||
|
}, function(){
|
||||||
|
alert("error|Could not retrieve the requested tweet.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (dragType === "account"){
|
||||||
|
const match = regexAccount.exec(url);
|
||||||
|
|
||||||
|
if (match.length === 2){
|
||||||
|
$(document).trigger("uiShowProfile", { id: match[1] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectors = {
|
||||||
|
tweet: "section.js-column",
|
||||||
|
account: app
|
||||||
|
};
|
||||||
|
|
||||||
|
window.TDGF_onGlobalDragStart = function(type, data){
|
||||||
|
if (dragType){
|
||||||
|
app.undelegate(selectors[dragType], events);
|
||||||
|
dragType = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === "link"){
|
||||||
|
dragType = regexTweet.test(data) ? "tweet" : regexAccount.test(data) ? "account": null;
|
||||||
|
app.delegate(selectors[dragType], events);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Make middle click on tweet reply icon open the compose drawer, retweet icon trigger a quote, and favorite icon open a 'Like from accounts...' modal.
|
||||||
|
//
|
||||||
|
execSafe(function supportMiddleClickTweetActions(){
|
||||||
|
app.delegate(".tweet-action,.tweet-detail-action", "auxclick", function(e){
|
||||||
|
return if e.which !== 2;
|
||||||
|
|
||||||
|
const column = TD.controller.columnManager.get($(this).closest("section.js-column").attr("data-column"));
|
||||||
|
return if !column;
|
||||||
|
|
||||||
|
const ele = $(this).closest("article");
|
||||||
|
const tweet = column.findChirp(ele.attr("data-tweet-id")) || column.findChirp(ele.attr("data-key"));
|
||||||
|
return if !tweet;
|
||||||
|
|
||||||
|
switch($(this).attr("rel")){
|
||||||
|
case "reply":
|
||||||
|
const main = tweet.getMainTweet();
|
||||||
|
|
||||||
|
$(document).trigger("uiDockedComposeTweet", {
|
||||||
|
type: "reply",
|
||||||
|
from: [ tweet.account.getKey() ],
|
||||||
|
inReplyTo: {
|
||||||
|
id: tweet.id,
|
||||||
|
htmlText: main.htmlText,
|
||||||
|
user: {
|
||||||
|
screenName: main.user.screenName,
|
||||||
|
name: main.user.name,
|
||||||
|
profileImageURL: main.user.profileImageURL
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mentions: tweet.getReplyUsers(),
|
||||||
|
element: ele
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "favorite":
|
||||||
|
$(document).trigger("uiShowFavoriteFromOptions", { tweet });
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "retweet":
|
||||||
|
TD.controller.stats.quoteTweet();
|
||||||
|
|
||||||
|
$(document).trigger("uiComposeTweet", {
|
||||||
|
type: "tweet",
|
||||||
|
from: [ tweet.account.getKey() ],
|
||||||
|
quotedTweet: tweet.getMainTweet(),
|
||||||
|
element: ele // triggers reply-account plugin
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Add a pin icon to make tweet compose drawer stay open.
|
||||||
|
//
|
||||||
|
execSafe(function setupStayOpenPin(){
|
||||||
|
$(document).on("tduckOldComposerActive", function(e){
|
||||||
|
const ele = $(`#import "markup/pin.html"`).appendTo(".js-docked-compose .js-compose-header");
|
||||||
|
|
||||||
|
ele.click(function(){
|
||||||
|
if (TD.settings.getComposeStayOpen()){
|
||||||
|
ele.css("transform", "rotate(0deg)");
|
||||||
|
TD.settings.setComposeStayOpen(false);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
ele.css("transform", "rotate(90deg)");
|
||||||
|
TD.settings.setComposeStayOpen(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (TD.settings.getComposeStayOpen()){
|
||||||
|
ele.css("transform", "rotate(90deg)");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Make submitting search queries while holding Ctrl or middle-clicking the search icon open the search externally.
|
||||||
|
//
|
||||||
|
onAppReady.push(function setupSearchTriggerHook(){
|
||||||
|
const openSearchExternally = function(event, input){
|
||||||
|
$TD.openBrowser("https://twitter.com/search/?q=" + encodeURIComponent(input.val() || ""));
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
input.val("").blur();
|
||||||
|
app.click(); // unfocus everything
|
||||||
|
};
|
||||||
|
|
||||||
|
$$(".js-app-search-input").keydown(function(e){
|
||||||
|
(e.ctrlKey && e.keyCode === 13) && openSearchExternally(e, $(this)); // enter
|
||||||
|
});
|
||||||
|
|
||||||
|
$$(".js-perform-search").on("click auxclick", function(e){
|
||||||
|
(e.ctrlKey || e.button === 1) && openSearchExternally(e, $(".js-app-search-input:visible"));
|
||||||
|
}).each(function(){
|
||||||
|
window.TDGF_prioritizeNewestEvent($(this)[0], "click");
|
||||||
|
});
|
||||||
|
|
||||||
|
$$("[data-action='show-search']").on("click auxclick", function(e){
|
||||||
|
(e.ctrlKey || e.button === 1) && openSearchExternally(e, $());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Setup video player hooks.
|
||||||
|
//
|
||||||
|
execSafe(function setupVideoPlayer(){
|
||||||
|
const getGifLink = function(ele){
|
||||||
|
return ele.attr("src") || ele.children("source[video-src]").first().attr("video-src");
|
||||||
|
};
|
||||||
|
|
||||||
|
const getVideoTweetLink = function(obj){
|
||||||
|
let parent = obj.closest(".js-tweet").first();
|
||||||
|
let link = (parent.hasClass("tweet-detail") ? parent.find("a[rel='url']") : parent.find("time").first().children("a")).first();
|
||||||
|
return link.attr("href");
|
||||||
|
};
|
||||||
|
|
||||||
|
const getUsername = function(tweet){
|
||||||
|
return tweet && (tweet.quotedTweet || tweet).getMainUser().screenName;
|
||||||
|
};
|
||||||
|
|
||||||
|
app.delegate(".js-gif-play", {
|
||||||
|
click: function(e){
|
||||||
|
let src = !e.ctrlKey && getGifLink($(this).closest(".js-media-gif-container").find("video"));
|
||||||
|
let tweet = getVideoTweetLink($(this));
|
||||||
|
|
||||||
|
if (src){
|
||||||
|
let hovered = getHoveredTweet();
|
||||||
|
window.TDGF_playVideo(src, tweet, getUsername(hovered && hovered.obj));
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
$TD.openBrowser(tweet);
|
||||||
|
}
|
||||||
|
|
||||||
|
e.stopPropagation();
|
||||||
|
},
|
||||||
|
|
||||||
|
mousedown: function(e){
|
||||||
|
if (e.button === 1){
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mouseup: function(e){
|
||||||
|
if (e.button === 1){
|
||||||
|
$TD.openBrowser(getVideoTweetLink($(this)));
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
window.TDGF_injectMustache("status/media_thumb.mustache", "append", "is-gif", " is-paused");
|
||||||
|
|
||||||
|
TD.mustaches["media/native_video.mustache"] = '<div class="js-media-gif-container media-item nbfc is-video" style="background-image:url({{imageSrc}})"><video class="js-media-gif media-item-gif full-width block {{#isPossiblySensitive}}is-invisible{{/isPossiblySensitive}}" loop src="{{videoUrl}}"></video><a class="js-gif-play pin-all is-actionable">{{> media/video_overlay}}</a></div>';
|
||||||
|
|
||||||
|
ensurePropertyExists(TD, "components", "MediaGallery", "prototype", "_loadTweet");
|
||||||
|
ensurePropertyExists(TD, "components", "BaseModal", "prototype", "setAndShowContainer");
|
||||||
|
ensurePropertyExists(TD, "ui", "Column", "prototype", "playGifIfNotManuallyPaused");
|
||||||
|
|
||||||
|
let cancelModal = false;
|
||||||
|
|
||||||
|
TD.components.MediaGallery.prototype._loadTweet = appendToFunction(TD.components.MediaGallery.prototype._loadTweet, function(){
|
||||||
|
const media = this.chirp.getMedia().find(media => media.mediaId === this.clickedMediaEntityId);
|
||||||
|
|
||||||
|
if (media && media.isVideo && media.service === "twitter"){
|
||||||
|
window.TDGF_playVideo(media.chooseVideoVariant().url, this.chirp.getChirpURL(), getUsername(this.chirp));
|
||||||
|
cancelModal = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
TD.components.BaseModal.prototype.setAndShowContainer = prependToFunction(TD.components.BaseModal.prototype.setAndShowContainer, function(){
|
||||||
|
if (cancelModal){
|
||||||
|
cancelModal = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
TD.ui.Column.prototype.playGifIfNotManuallyPaused = function(){};
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Detect and notify about connection issues.
|
||||||
|
//
|
||||||
|
(function(){
|
||||||
|
const onConnectionError = function(){
|
||||||
|
return if $("#tweetduck-conn-issues").length;
|
||||||
|
|
||||||
|
const ele = $(`#import "markup/offline.html"`).appendTo(document.body);
|
||||||
|
|
||||||
|
ele.find("button").click(function(){
|
||||||
|
ele.fadeOut(200);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onConnectionFine = function(){
|
||||||
|
const ele = $("#tweetduck-conn-issues");
|
||||||
|
|
||||||
|
ele.fadeOut(200, function(){
|
||||||
|
ele.remove();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("offline", onConnectionError);
|
||||||
|
window.addEventListener("online", onConnectionFine);
|
||||||
|
})();
|
279
Resources/Scripts/imports/scripts/browser.globals.js
Normal file
279
Resources/Scripts/imports/scripts/browser.globals.js
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
//
|
||||||
|
// Functions: Responds to updating $TDX properties.
|
||||||
|
//
|
||||||
|
(function(){
|
||||||
|
let callbacks = [];
|
||||||
|
|
||||||
|
window.TDGF_registerPropertyUpdateCallback = function(callback){
|
||||||
|
callbacks.push(callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.TDGF_onPropertiesUpdated = function(){
|
||||||
|
callbacks.forEach(func => func($TDX));
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Function: Injects custom HTML into mustache templates.
|
||||||
|
//
|
||||||
|
window.TDGF_injectMustache = function(name, operation, search, custom){
|
||||||
|
let replacement;
|
||||||
|
|
||||||
|
switch(operation){
|
||||||
|
case "replace": replacement = custom; break;
|
||||||
|
case "append": replacement = search + custom; break;
|
||||||
|
case "prepend": replacement = custom + search; break;
|
||||||
|
default: throw "Invalid mustache injection operation. Only 'replace', 'append', 'prepend' are supported.";
|
||||||
|
}
|
||||||
|
|
||||||
|
const prev = TD.mustaches && TD.mustaches[name];
|
||||||
|
|
||||||
|
if (!prev){
|
||||||
|
crashDebug("Mustache injection is referencing an invalid mustache: " + name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TD.mustaches[name] = prev.replace(search, replacement);
|
||||||
|
|
||||||
|
if (prev === TD.mustaches[name]){
|
||||||
|
crashDebug("Mustache injection had no effect: " + name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Function: Pushes the newest jQuery event to the beginning of the event handler list.
|
||||||
|
//
|
||||||
|
window.TDGF_prioritizeNewestEvent = function(element, event){
|
||||||
|
const events = $._data(element, "events");
|
||||||
|
|
||||||
|
const handlers = events[event];
|
||||||
|
const newHandler = handlers[handlers.length - 1];
|
||||||
|
|
||||||
|
for(let index = handlers.length - 1; index > 0; index--){
|
||||||
|
handlers[index] = handlers[index - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
handlers[0] = newHandler;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Function: Returns a display name for a column object.
|
||||||
|
//
|
||||||
|
window.TDGF_getColumnName = (function(){
|
||||||
|
const titles = {
|
||||||
|
"icon-home": "Home",
|
||||||
|
"icon-mention": "Mentions",
|
||||||
|
"icon-message": "Messages",
|
||||||
|
"icon-notifications": "Notifications",
|
||||||
|
"icon-follow": "Followers",
|
||||||
|
"icon-activity": "Activity",
|
||||||
|
"icon-favorite": "Likes",
|
||||||
|
"icon-user": "User",
|
||||||
|
"icon-search": "Search",
|
||||||
|
"icon-list": "List",
|
||||||
|
"icon-custom-timeline": "Timeline",
|
||||||
|
"icon-dataminr": "Dataminr",
|
||||||
|
"icon-play-video": "Live video",
|
||||||
|
"icon-schedule": "Scheduled"
|
||||||
|
};
|
||||||
|
|
||||||
|
return function(column){
|
||||||
|
return titles[column._tduck_icon] || "";
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Function: Adds a search column with the specified query.
|
||||||
|
//
|
||||||
|
onAppReady.push(() => execSafe(function setupSearchFunction(){
|
||||||
|
const context = $._data(document, "events")["uiSearchInputSubmit"][0].handler.context;
|
||||||
|
|
||||||
|
window.TDGF_performSearch = function(query){
|
||||||
|
context.performSearch({ query, tduckResetInput: true });
|
||||||
|
};
|
||||||
|
}, function(){
|
||||||
|
window.TDGF_performSearch = function(){
|
||||||
|
alert("error|This feature is not available due to an internal error.");
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
|
||||||
|
//
|
||||||
|
// Function: Plays sound notification.
|
||||||
|
//
|
||||||
|
window.TDGF_playSoundNotification = function(){
|
||||||
|
document.getElementById("update-sound").play();
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Function: Plays video using the internal player.
|
||||||
|
//
|
||||||
|
window.TDGF_playVideo = function(videoUrl, tweetUrl, username){
|
||||||
|
return if !videoUrl;
|
||||||
|
|
||||||
|
$TD.playVideo(videoUrl, tweetUrl || videoUrl, username || null, function(){
|
||||||
|
$('<div id="td-video-player-overlay" class="ovl" style="display:block"></div>').on("click contextmenu", function(){
|
||||||
|
$TD.stopVideo();
|
||||||
|
}).appendTo(app);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Function: Shows tweet detail, used in notification context menu.
|
||||||
|
//
|
||||||
|
execSafe(function setupShowTweetDetail(){
|
||||||
|
ensurePropertyExists(TD, "ui", "updates", "showDetailView");
|
||||||
|
ensurePropertyExists(TD, "controller", "columnManager", "showColumn");
|
||||||
|
ensurePropertyExists(TD, "controller", "columnManager", "getByApiid");
|
||||||
|
ensurePropertyExists(TD, "controller", "clients", "getPreferredClient");
|
||||||
|
|
||||||
|
const showTweetDetailInternal = function(column, chirp){
|
||||||
|
TD.ui.updates.showDetailView(column, chirp, column.findChirp(chirp) || chirp);
|
||||||
|
TD.controller.columnManager.showColumn(column.model.privateState.key);
|
||||||
|
|
||||||
|
$(document).trigger("uiGridClearSelection");
|
||||||
|
};
|
||||||
|
|
||||||
|
window.TDGF_showTweetDetail = function(columnId, chirpId, fallbackUrl){
|
||||||
|
if (!TD.ready){
|
||||||
|
onAppReady.push(function(){
|
||||||
|
window.TDGF_showTweetDetail(columnId, chirpId, fallbackUrl);
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const column = TD.controller.columnManager.getByApiid(columnId);
|
||||||
|
|
||||||
|
if (!column){
|
||||||
|
if (confirm("error|The column which contained the tweet no longer exists. Would you like to open the tweet in your browser instead?")){
|
||||||
|
$TD.openBrowser(fallbackUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const chirp = column.findMostInterestingChirp(chirpId);
|
||||||
|
|
||||||
|
if (chirp){
|
||||||
|
showTweetDetailInternal(column, chirp);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
TD.controller.clients.getPreferredClient().show(chirpId, function(chirp){
|
||||||
|
showTweetDetailInternal(column, chirp);
|
||||||
|
}, function(){
|
||||||
|
if (confirm("error|Could not retrieve the requested tweet. Would you like to open the tweet in your browser instead?")){
|
||||||
|
$TD.openBrowser(fallbackUrl);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, function(){
|
||||||
|
window.TDGF_showTweetDetail = function(){
|
||||||
|
alert("error|This feature is not available due to an internal error.");
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Function: Screenshots tweet to clipboard.
|
||||||
|
//
|
||||||
|
execSafe(function setupTweetScreenshot(){
|
||||||
|
window.TDGF_triggerScreenshot = function(){
|
||||||
|
const hovered = getHoveredTweet();
|
||||||
|
return if !hovered;
|
||||||
|
|
||||||
|
const columnWidth = $(hovered.column.ele).width();
|
||||||
|
const tweet = hovered.wrap || hovered.obj;
|
||||||
|
|
||||||
|
const html = $(tweet.render({
|
||||||
|
withFooter: false,
|
||||||
|
withTweetActions: false,
|
||||||
|
isInConvo: false,
|
||||||
|
isFavorite: false,
|
||||||
|
isRetweeted: false, // keeps retweet mark above tweet
|
||||||
|
isPossiblySensitive: false,
|
||||||
|
mediaPreviewSize: hovered.column.obj.getMediaPreviewSize()
|
||||||
|
}));
|
||||||
|
|
||||||
|
html.find("footer").last().remove(); // apparently withTweetActions breaks for certain tweets, nice
|
||||||
|
html.find(".td-screenshot-remove").remove();
|
||||||
|
|
||||||
|
html.find("p.link-complex-target,p.txt-mute").filter(function(){
|
||||||
|
return $(this).text() === "Show this thread";
|
||||||
|
}).remove();
|
||||||
|
|
||||||
|
html.addClass($(document.documentElement).attr("class"));
|
||||||
|
html.addClass($(document.body).attr("class"));
|
||||||
|
|
||||||
|
html.css("background-color", getClassStyleProperty("stream-item", "background-color"));
|
||||||
|
html.css("border", "none");
|
||||||
|
|
||||||
|
for(let selector of [ ".js-quote-detail", ".js-media-preview-container", ".js-media" ]){
|
||||||
|
const ele = html.find(selector);
|
||||||
|
|
||||||
|
if (ele.length){
|
||||||
|
ele[0].style.setProperty("margin-bottom", "2px", "important");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const gif = html.find(".js-media-gif-container");
|
||||||
|
|
||||||
|
if (gif.length){
|
||||||
|
gif.css("background-image", 'url("'+tweet.getMedia()[0].small()+'")');
|
||||||
|
}
|
||||||
|
|
||||||
|
const type = tweet.getChirpType();
|
||||||
|
|
||||||
|
if ((type.startsWith("favorite") || type.startsWith("retweet")) && tweet.isAboutYou()){
|
||||||
|
html.addClass("td-notification-padded");
|
||||||
|
}
|
||||||
|
|
||||||
|
$TD.screenshotTweet(html[0].outerHTML, columnWidth);
|
||||||
|
};
|
||||||
|
}, function(){
|
||||||
|
window.TDGF_triggerScreenshot = function(){
|
||||||
|
alert("error|This feature is not available due to an internal error.");
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Function: Apply ROT13 to input selection.
|
||||||
|
//
|
||||||
|
window.TDGF_applyROT13 = function(){
|
||||||
|
const ele = document.activeElement;
|
||||||
|
return if !ele || !ele.value;
|
||||||
|
|
||||||
|
const selection = ele.value.substring(ele.selectionStart, ele.selectionEnd);
|
||||||
|
return if !selection;
|
||||||
|
|
||||||
|
document.execCommand("insertText", false, selection.replace(/[a-zA-Z]/g, function(chr){
|
||||||
|
const code = chr.charCodeAt(0);
|
||||||
|
const start = code <= 90 ? 65 : 97;
|
||||||
|
return String.fromCharCode(start + (code - start + 13) % 26);
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Function: Reloads all columns.
|
||||||
|
//
|
||||||
|
if (checkPropertyExists(TD, "controller", "columnManager", "getAll")){
|
||||||
|
window.TDGF_reloadColumns = function(){
|
||||||
|
Object.values(TD.controller.columnManager.getAll()).forEach(column => column.reloadTweets());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
window.TDGF_reloadColumns = function(){};
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Function: Reloads the website with memory cleanup.
|
||||||
|
//
|
||||||
|
window.TDGF_reload = function(){
|
||||||
|
window.gc && window.gc();
|
||||||
|
window.location.reload();
|
||||||
|
|
||||||
|
window.TDGF_reload = function(){}; // redefine to prevent reloading multiple times
|
||||||
|
};
|
468
Resources/Scripts/imports/scripts/browser.tweaks.js
Normal file
468
Resources/Scripts/imports/scripts/browser.tweaks.js
Normal file
@@ -0,0 +1,468 @@
|
|||||||
|
//
|
||||||
|
// Block: Paste images when tweeting.
|
||||||
|
//
|
||||||
|
onAppReady.push(function supportImagePaste(){
|
||||||
|
const uploader = $._data(document, "events")["uiComposeAddImageClick"][0].handler.context;
|
||||||
|
|
||||||
|
app.delegate(".js-compose-text,.js-reply-tweetbox,.td-detect-image-paste", "paste", function(e){
|
||||||
|
for(let item of e.originalEvent.clipboardData.items){
|
||||||
|
if (item.type.startsWith("image/")){
|
||||||
|
if (!$(this).closest(".rpl").find(".js-reply-popout").click().length){ // popout direct messages
|
||||||
|
return if $(".js-add-image-button").is(".is-disabled"); // tweetdeck does not check upload count properly
|
||||||
|
}
|
||||||
|
|
||||||
|
uploader.addFilesToUpload([ item.getAsFile() ]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Allow changing first day of week in date picker.
|
||||||
|
//
|
||||||
|
if (checkPropertyExists($, "tools", "dateinput", "conf", "firstDay")){
|
||||||
|
$.tools.dateinput.conf.firstDay = $TDX.firstDayOfWeek;
|
||||||
|
|
||||||
|
onAppReady.push(function setupDatePickerFirstDayCallback(){
|
||||||
|
window.TDGF_registerPropertyUpdateCallback(function($TDX){
|
||||||
|
$.tools.dateinput.conf.firstDay = $TDX.firstDayOfWeek;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Override language used for translations.
|
||||||
|
//
|
||||||
|
if (checkPropertyExists(TD, "languages", "getSystemLanguageCode")){
|
||||||
|
const prevFunc = TD.languages.getSystemLanguageCode;
|
||||||
|
|
||||||
|
TD.languages.getSystemLanguageCode = function(returnShortCode){
|
||||||
|
return returnShortCode ? ($TDX.translationTarget || "en") : prevFunc.apply(this, arguments);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Add missing languages for Bing Translator (Bengali, Icelandic, Tagalog, Tamil, Telugu, Urdu).
|
||||||
|
//
|
||||||
|
execSafe(function addMissingTranslationLanguages(){
|
||||||
|
ensurePropertyExists(TD, "languages", "getSupportedTranslationSourceLanguages");
|
||||||
|
|
||||||
|
const newCodes = [ "bn", "is", "tl", "ta", "te", "ur" ];
|
||||||
|
const codeSet = new Set(TD.languages.getSupportedTranslationSourceLanguages());
|
||||||
|
|
||||||
|
for(const lang of newCodes){
|
||||||
|
codeSet.add(lang);
|
||||||
|
}
|
||||||
|
|
||||||
|
const codeList = [...codeSet];
|
||||||
|
|
||||||
|
TD.languages.getSupportedTranslationSourceLanguages = function(){
|
||||||
|
return codeList;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Bypass t.co when clicking/dragging links, media, and in user profiles.
|
||||||
|
//
|
||||||
|
execSafe(function setupShortenerBypass(){
|
||||||
|
$(document.body).delegate("a[data-full-url]", "click auxclick", function(e){
|
||||||
|
// event.which seems to be borked in auxclick
|
||||||
|
// tweet links open directly in the column
|
||||||
|
if ((e.button === 0 || e.button === 1) && $(this).attr("rel") !== "tweet"){
|
||||||
|
$TD.openBrowser($(this).attr("data-full-url"));
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document.body).delegate("a[data-full-url]", "dragstart", function(e){
|
||||||
|
const url = $(this).attr("data-full-url");
|
||||||
|
const data = e.originalEvent.dataTransfer;
|
||||||
|
|
||||||
|
data.clearData();
|
||||||
|
data.setData("text/uri-list", url);
|
||||||
|
data.setData("text/plain", url);
|
||||||
|
data.setData("text/html", `<a href="${url}">${url}</a>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (checkPropertyExists(TD, "services", "TwitterStatus", "prototype", "_generateHTMLText")){
|
||||||
|
TD.services.TwitterStatus.prototype._generateHTMLText = prependToFunction(TD.services.TwitterStatus.prototype._generateHTMLText, function(){
|
||||||
|
const card = this.card;
|
||||||
|
const entities = this.entities;
|
||||||
|
return if !(card && entities);
|
||||||
|
|
||||||
|
const urls = entities.urls;
|
||||||
|
return if !(urls && urls.length);
|
||||||
|
|
||||||
|
const shortUrl = card.url;
|
||||||
|
const urlObj = entities.urls.find(obj => obj.url === shortUrl && obj.expanded_url);
|
||||||
|
|
||||||
|
if (urlObj){
|
||||||
|
const expandedUrl = urlObj.expanded_url;
|
||||||
|
card.url = expandedUrl;
|
||||||
|
|
||||||
|
const values = card.binding_values;
|
||||||
|
|
||||||
|
if (values && values.card_url){
|
||||||
|
values.card_url.string_value = expandedUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkPropertyExists(TD, "services", "TwitterMedia", "prototype", "fromMediaEntity")){
|
||||||
|
const prevFunc = TD.services.TwitterMedia.prototype.fromMediaEntity;
|
||||||
|
|
||||||
|
TD.services.TwitterMedia.prototype.fromMediaEntity = function(){
|
||||||
|
const obj = prevFunc.apply(this, arguments);
|
||||||
|
const e = arguments[0];
|
||||||
|
|
||||||
|
if (e.expanded_url){
|
||||||
|
if (obj.url === obj.shortUrl){
|
||||||
|
obj.shortUrl = e.expanded_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.url = e.expanded_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkPropertyExists(TD, "services", "TwitterUser", "prototype", "fromJSONObject")){
|
||||||
|
const prevFunc = TD.services.TwitterUser.prototype.fromJSONObject;
|
||||||
|
|
||||||
|
TD.services.TwitterUser.prototype.fromJSONObject = function(){
|
||||||
|
const obj = prevFunc.apply(this, arguments);
|
||||||
|
const e = arguments[0].entities;
|
||||||
|
|
||||||
|
if (e && e.url && e.url.urls && e.url.urls.length && e.url.urls[0].expanded_url){
|
||||||
|
obj.url = e.url.urls[0].expanded_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Fix youtu.be previews not showing up for https links.
|
||||||
|
//
|
||||||
|
execSafe(function fixYouTubePreviews(){
|
||||||
|
ensurePropertyExists(TD, "services", "TwitterMedia");
|
||||||
|
|
||||||
|
const media = TD.services.TwitterMedia;
|
||||||
|
|
||||||
|
ensurePropertyExists(media, "YOUTUBE_TINY_RE");
|
||||||
|
ensurePropertyExists(media, "YOUTUBE_LONG_RE");
|
||||||
|
ensurePropertyExists(media, "YOUTUBE_RE");
|
||||||
|
ensurePropertyExists(media, "SERVICES", "youtube");
|
||||||
|
|
||||||
|
media.YOUTUBE_TINY_RE = new RegExp(media.YOUTUBE_TINY_RE.source.replace("http:", "https?:"));
|
||||||
|
media.YOUTUBE_RE = new RegExp(media.YOUTUBE_LONG_RE.source + "|" + media.YOUTUBE_TINY_RE.source);
|
||||||
|
media.SERVICES["youtube"] = media.YOUTUBE_RE;
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Refocus the textbox after switching accounts.
|
||||||
|
//
|
||||||
|
onAppReady.push(function setupAccountSwitchRefocus(){
|
||||||
|
const refocusInput = function(){
|
||||||
|
document.querySelector(".js-docked-compose .js-compose-text").focus();
|
||||||
|
};
|
||||||
|
|
||||||
|
const accountItemClickEvent = function(e){
|
||||||
|
setTimeout(refocusInput, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
$(document).on("tduckOldComposerActive", function(e){
|
||||||
|
$$(".js-account-list", ".js-docked-compose").delegate(".js-account-item", "click", accountItemClickEvent);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Fix docked composer not re-focusing after Alt+Tab & image upload.
|
||||||
|
//
|
||||||
|
onAppReady.push(function fixDockedComposerRefocus(){
|
||||||
|
$(document).on("tduckOldComposerActive", function(e){
|
||||||
|
const ele = $$(".js-compose-text", ".js-docked-compose");
|
||||||
|
const node = ele[0];
|
||||||
|
|
||||||
|
let cancelBlur = false;
|
||||||
|
|
||||||
|
ele.on("blur", function(e){
|
||||||
|
cancelBlur = true;
|
||||||
|
setTimeout(function(){ cancelBlur = false; }, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.TDGF_prioritizeNewestEvent(node, "blur");
|
||||||
|
|
||||||
|
node.blur = prependToFunction(node.blur, function(){
|
||||||
|
return cancelBlur;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
ensureEventExists(document, "uiComposeImageAdded");
|
||||||
|
|
||||||
|
$(document).on("uiComposeImageAdded", function(){
|
||||||
|
document.querySelector(".js-docked-compose .js-compose-text").focus();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Fix DM reply input box not getting focused after opening a conversation.
|
||||||
|
//
|
||||||
|
if (checkPropertyExists(TD, "components", "ConversationDetailView", "prototype", "showChirp")){
|
||||||
|
TD.components.ConversationDetailView.prototype.showChirp = appendToFunction(TD.components.ConversationDetailView.prototype.showChirp, function(){
|
||||||
|
return if !$TDX.focusDmInput;
|
||||||
|
|
||||||
|
setTimeout(function(){
|
||||||
|
document.querySelector(".js-reply-tweetbox").focus();
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Hold Shift to restore cleared column.
|
||||||
|
//
|
||||||
|
execSafe(function supportShiftToClearColumn(){
|
||||||
|
ensurePropertyExists(TD, "vo", "Column", "prototype", "clear");
|
||||||
|
|
||||||
|
let holdingShift = false;
|
||||||
|
|
||||||
|
const updateShiftState = (pressed) => {
|
||||||
|
if (pressed != holdingShift){
|
||||||
|
holdingShift = pressed;
|
||||||
|
$("button[data-action='clear']").children("span").text(holdingShift ? "Restore" : "Clear");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetActiveFocus = () => {
|
||||||
|
document.activeElement.blur();
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener("keydown", function(e){
|
||||||
|
if (e.shiftKey && (document.activeElement === null || !("value" in document.activeElement))){
|
||||||
|
updateShiftState(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener("keyup", function(e){
|
||||||
|
if (!e.shiftKey){
|
||||||
|
updateShiftState(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
TD.vo.Column.prototype.clear = prependToFunction(TD.vo.Column.prototype.clear, function(){
|
||||||
|
window.setTimeout(resetActiveFocus, 0); // unfocuses the Clear button, otherwise it steals keyboard input
|
||||||
|
|
||||||
|
if (holdingShift){
|
||||||
|
this.model.setClearedTimestamp(0);
|
||||||
|
this.reloadTweets();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Make temporary search column appear as the first one and clear the input box.
|
||||||
|
//
|
||||||
|
execSafe(function setupSearchColumnHook(){
|
||||||
|
ensurePropertyExists(TD, "controller", "columnManager", "_columnOrder");
|
||||||
|
ensurePropertyExists(TD, "controller", "columnManager", "move");
|
||||||
|
|
||||||
|
$(document).on("uiSearchNoTemporaryColumn", function(e, data){
|
||||||
|
if (data.query && data.searchScope !== "users" && !data.columnKey){
|
||||||
|
if ($TDX.openSearchInFirstColumn){
|
||||||
|
const order = TD.controller.columnManager._columnOrder;
|
||||||
|
|
||||||
|
if (order.length > 1){
|
||||||
|
const columnKey = order[order.length - 1];
|
||||||
|
|
||||||
|
order.splice(order.length - 1, 1);
|
||||||
|
order.splice(1, 0, columnKey);
|
||||||
|
TD.controller.columnManager.move(columnKey, "left");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!("tduckResetInput" in data)){
|
||||||
|
$(".js-app-search-input").val("");
|
||||||
|
$(".js-perform-search").blur();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Reorder search results to move accounts above hashtags.
|
||||||
|
//
|
||||||
|
onAppReady.push(function reorderSearchResults(){
|
||||||
|
const container = $(".js-search-in-popover");
|
||||||
|
const hashtags = $$(".js-typeahead-topic-list", container);
|
||||||
|
|
||||||
|
$$(".js-typeahead-user-list", container).insertBefore(hashtags);
|
||||||
|
hashtags.addClass("list-divider");
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Revert Like/Follow dialogs being closed after clicking an action.
|
||||||
|
//
|
||||||
|
execSafe(function setupLikeFollowDialogRevert(){
|
||||||
|
const prevSetTimeout = window.setTimeout;
|
||||||
|
|
||||||
|
const overrideState = function(){
|
||||||
|
return if !$TDX.keepLikeFollowDialogsOpen;
|
||||||
|
|
||||||
|
window.setTimeout = function(func, timeout){
|
||||||
|
return timeout !== 500 && prevSetTimeout.apply(this, arguments);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const restoreState = function(context, key){
|
||||||
|
window.setTimeout = prevSetTimeout;
|
||||||
|
|
||||||
|
if ($TDX.keepLikeFollowDialogsOpen && key in context.state){
|
||||||
|
context.state[key] = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$(document).on("uiShowFavoriteFromOptions", function(){
|
||||||
|
$(".js-btn-fav", ".js-modal-inner").each(function(){
|
||||||
|
let event = $._data(this, "events").click[0];
|
||||||
|
let handler = event.handler;
|
||||||
|
|
||||||
|
event.handler = function(){
|
||||||
|
overrideState();
|
||||||
|
handler.apply(this, arguments);
|
||||||
|
restoreState($._data(document, "events").dataFavoriteState[0].handler.context, "stopSubsequentLikes");
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on("uiShowFollowFromOptions", function(){
|
||||||
|
$(".js-component", ".js-modal-inner").each(function(){
|
||||||
|
let event = $._data(this, "events").click[0];
|
||||||
|
let handler = event.handler;
|
||||||
|
let context = handler.context;
|
||||||
|
|
||||||
|
event.handler = function(){
|
||||||
|
overrideState();
|
||||||
|
handler.apply(this, arguments);
|
||||||
|
restoreState(context, "stopSubsequentFollows");
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Fix broken horizontal scrolling of column container when holding Shift.
|
||||||
|
//
|
||||||
|
if (checkPropertyExists(TD, "ui", "columns", "setupColumnScrollListeners")){
|
||||||
|
TD.ui.columns.setupColumnScrollListeners = appendToFunction(TD.ui.columns.setupColumnScrollListeners, function(column){
|
||||||
|
const ele = document.querySelector(".js-column[data-column='" + column.model.getKey() + "']");
|
||||||
|
return if ele == null;
|
||||||
|
|
||||||
|
$(ele).off("onmousewheel").on("mousewheel", ".scroll-v", function(e){
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
});
|
||||||
|
|
||||||
|
window.TDGF_prioritizeNewestEvent(ele, "mousewheel");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Fix DM image previews and GIF thumbnails not loading due to new URLs.
|
||||||
|
//
|
||||||
|
if (checkPropertyExists(TD, "services", "TwitterMedia", "prototype", "getTwitterPreviewUrl")){
|
||||||
|
const prevFunc = TD.services.TwitterMedia.prototype.getTwitterPreviewUrl;
|
||||||
|
|
||||||
|
TD.services.TwitterMedia.prototype.getTwitterPreviewUrl = function(){
|
||||||
|
const url = prevFunc.apply(this, arguments);
|
||||||
|
|
||||||
|
if (url.startsWith("https://ton.twitter.com/1.1/ton/data/dm/") || url.startsWith("https://pbs.twimg.com/tweet_video_thumb/")){
|
||||||
|
const format = url.match(/\?.*format=(\w+)/);
|
||||||
|
|
||||||
|
if (format && format.length === 2){
|
||||||
|
const fix = `.${format[1]}?`;
|
||||||
|
|
||||||
|
if (!url.includes(fix)){
|
||||||
|
return url.replace("?", fix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return url;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Fix DMs not being marked as read when replying to them.
|
||||||
|
//
|
||||||
|
execSafe(function markRepliedDMsAsRead(){
|
||||||
|
ensurePropertyExists(TD, "controller", "clients", "getClient");
|
||||||
|
ensurePropertyExists(TD, "services", "Conversations", "prototype", "getConversation");
|
||||||
|
|
||||||
|
$(document).on("dataDmSent", function(e, data){
|
||||||
|
const client = TD.controller.clients.getClient(data.request.accountKey);
|
||||||
|
return if !client;
|
||||||
|
|
||||||
|
const conversation = client.conversations.getConversation(data.request.conversationId);
|
||||||
|
return if !conversation;
|
||||||
|
|
||||||
|
conversation.markAsRead();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Limit amount of loaded DMs to avoid massive lag from re-opening them several times.
|
||||||
|
//
|
||||||
|
if (checkPropertyExists(TD, "services", "TwitterConversation", "prototype", "renderThread")){
|
||||||
|
const prevFunc = TD.services.TwitterConversation.prototype.renderThread;
|
||||||
|
|
||||||
|
TD.services.TwitterConversation.prototype.renderThread = function(){
|
||||||
|
const prevMessages = this.messages;
|
||||||
|
|
||||||
|
this.messages = prevMessages.slice(0, 100);
|
||||||
|
const result = prevFunc.apply(this, arguments);
|
||||||
|
this.messages = prevMessages;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Fix scheduled tweets not showing up sometimes.
|
||||||
|
//
|
||||||
|
execSafe(function fixScheduledTweets(){
|
||||||
|
ensurePropertyExists(TD, "controller", "columnManager", "getAll");
|
||||||
|
ensureEventExists(document, "dataTweetSent");
|
||||||
|
|
||||||
|
$(document).on("dataTweetSent", function(e, data){
|
||||||
|
if (data.response.state && data.response.state === "scheduled"){
|
||||||
|
const column = Object.values(TD.controller.columnManager.getAll()).find(column => column.model.state.type === "scheduled");
|
||||||
|
return if !column;
|
||||||
|
|
||||||
|
setTimeout(function(){
|
||||||
|
column.reloadTweets();
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Let's make retweets lowercase again.
|
||||||
|
//
|
||||||
|
window.TDGF_injectMustache("status/tweet_single.mustache", "replace", "{{_i}} Retweeted{{/i}}", "{{_i}} retweeted{{/i}}");
|
||||||
|
|
||||||
|
if (checkPropertyExists(TD, "services", "TwitterActionRetweet", "prototype", "generateText")){
|
||||||
|
TD.services.TwitterActionRetweet.prototype.generateText = appendToFunction(TD.services.TwitterActionRetweet.prototype.generateText, function(){
|
||||||
|
this.text = this.text.replace(" Retweeted", " retweeted");
|
||||||
|
this.htmlText = this.htmlText.replace(" Retweeted", " retweeted");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkPropertyExists(TD, "services", "TwitterActionRetweetedInteraction", "prototype", "generateText")){
|
||||||
|
TD.services.TwitterActionRetweetedInteraction.prototype.generateText = appendToFunction(TD.services.TwitterActionRetweetedInteraction.prototype.generateText, function(){
|
||||||
|
this.htmlText = this.htmlText.replace(" Retweeted", " retweeted").replace(" Retweet", " retweet");
|
||||||
|
});
|
||||||
|
}
|
@@ -1,14 +1,31 @@
|
|||||||
(function($TDP){
|
(function(){
|
||||||
|
if (!("$TDP" in window)){
|
||||||
|
console.error("Missing $TDP");
|
||||||
|
}
|
||||||
|
|
||||||
|
const validatePluginObject = function(pluginObject){
|
||||||
|
if (!("$token" in pluginObject)){
|
||||||
|
throw "Invalid plugin object.";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Setup a simple JavaScript object configuration loader.
|
// Block: Setup a simple JavaScript object configuration loader.
|
||||||
//
|
//
|
||||||
window.TDPF_loadConfigurationFile = function(pluginObject, fileNameUser, fileNameDefault, onSuccess, onFailure){
|
window.TDPF_loadConfigurationFile = function(pluginObject, fileNameUser, fileNameDefault, onSuccess, onFailure){
|
||||||
|
validatePluginObject(pluginObject);
|
||||||
|
|
||||||
let identifier = pluginObject.$id;
|
let identifier = pluginObject.$id;
|
||||||
let token = pluginObject.$token;
|
let token = pluginObject.$token;
|
||||||
|
|
||||||
$TDP.checkFileExists(token, fileNameUser).then(exists => {
|
$TDP.checkFileExists(token, fileNameUser).then(exists => {
|
||||||
let fileName = exists ? fileNameUser : fileNameDefault;
|
let fileName = exists ? fileNameUser : fileNameDefault;
|
||||||
|
|
||||||
|
if (fileName === null){
|
||||||
|
onSuccess && onSuccess({});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
(exists ? $TDP.readFile(token, fileName, true) : $TDP.readFileRoot(token, fileName)).then(contents => {
|
(exists ? $TDP.readFile(token, fileName, true) : $TDP.readFileRoot(token, fileName)).then(contents => {
|
||||||
let obj;
|
let obj;
|
||||||
|
|
||||||
@@ -39,6 +56,8 @@
|
|||||||
// Block: Setup a function to add/remove custom CSS.
|
// Block: Setup a function to add/remove custom CSS.
|
||||||
//
|
//
|
||||||
window.TDPF_createCustomStyle = function(pluginObject){
|
window.TDPF_createCustomStyle = function(pluginObject){
|
||||||
|
validatePluginObject(pluginObject);
|
||||||
|
|
||||||
let element = document.createElement("style");
|
let element = document.createElement("style");
|
||||||
element.id = "plugin-" + pluginObject.$id + "-"+Math.random().toString(36).substring(2, 7);
|
element.id = "plugin-" + pluginObject.$id + "-"+Math.random().toString(36).substring(2, 7);
|
||||||
document.head.appendChild(element);
|
document.head.appendChild(element);
|
||||||
@@ -49,4 +68,105 @@
|
|||||||
element: element
|
element: element
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
})($TDP);
|
|
||||||
|
//
|
||||||
|
// Block: Setup a function to mimic a Storage object that will be saved in the plugin.
|
||||||
|
//
|
||||||
|
window.TDPF_createStorage = function(pluginObject, onReady){
|
||||||
|
validatePluginObject(pluginObject);
|
||||||
|
|
||||||
|
if ("$storage" in pluginObject){
|
||||||
|
if (pluginObject.$storage !== null){ // set to null while the file is still loading
|
||||||
|
onReady(pluginObject.$storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Storage{
|
||||||
|
get length(){
|
||||||
|
return Object.keys(this).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
key(index){
|
||||||
|
return Object.keys(this)[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
getItem(key){
|
||||||
|
return this[key] || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
setItem(key, value){
|
||||||
|
this[key] = value;
|
||||||
|
updateFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
removeItem(key){
|
||||||
|
delete this[key];
|
||||||
|
updateFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
clear(){
|
||||||
|
for(key of Object.keys(this)){
|
||||||
|
delete this[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
replace(obj, silent){
|
||||||
|
for(let key of Object.keys(this)){
|
||||||
|
delete this[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
for(let key in obj){
|
||||||
|
this[key] = obj[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!silent){
|
||||||
|
updateFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var storage = new Proxy(new Storage(), {
|
||||||
|
get: function(obj, prop, receiver){
|
||||||
|
const value = obj[prop];
|
||||||
|
return typeof value === "function" ? value.bind(obj) : value;
|
||||||
|
},
|
||||||
|
|
||||||
|
set: function(obj, prop, value){
|
||||||
|
obj.setItem(prop, value);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteProperty: function(obj, prop){
|
||||||
|
obj.removeItem(prop);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
enumerate: function(obj){
|
||||||
|
return Object.keys(obj);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var delay = -1;
|
||||||
|
|
||||||
|
const updateFile = function(){
|
||||||
|
window.clearTimeout(delay);
|
||||||
|
|
||||||
|
delay = window.setTimeout(function(){
|
||||||
|
$TDP.writeFile(pluginObject.$token, ".storage", JSON.stringify(storage));
|
||||||
|
}, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
pluginObject.$storage = null;
|
||||||
|
|
||||||
|
window.TDPF_loadConfigurationFile(pluginObject, ".storage", null, function(obj){
|
||||||
|
storage.replace(obj, true);
|
||||||
|
onReady(pluginObject.$storage = storage);
|
||||||
|
}, function(){
|
||||||
|
onReady(pluginObject.$storage = storage);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
@@ -1,10 +1,11 @@
|
|||||||
(function($, $TD){
|
(function(){
|
||||||
$(document).one("TD.ready", function(){
|
$(document).one("TD.ready", function(){
|
||||||
let css = $(`<style>#import "styles/introduction.css"</style>`).appendTo(document.head);
|
const css = $(`<style>#import "styles/introduction.css"</style>`).appendTo(document.head);
|
||||||
let ele = $(`#import "markup/introduction.html"`).appendTo(".js-app");
|
const ele = $(`#import "markup/introduction.html"`).appendTo(".js-app");
|
||||||
|
|
||||||
let tdUser = null;
|
let tdUser = null;
|
||||||
let loadTweetDuckUser = (onSuccess, onError) => {
|
|
||||||
|
const loadTweetDuckUser = (onSuccess, onError) => {
|
||||||
if (tdUser !== null){
|
if (tdUser !== null){
|
||||||
onSuccess(tdUser);
|
onSuccess(tdUser);
|
||||||
}
|
}
|
||||||
@@ -28,8 +29,8 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
ele.find("button, a.mdl-dismiss").click(function(){
|
ele.find("button, a.mdl-dismiss").click(function(){
|
||||||
let showGuide = $(this)[0].hasAttribute("data-guide");
|
const showGuide = $(this)[0].hasAttribute("data-guide");
|
||||||
let allowDataCollection = $("#td-anonymous-data").is(":checked");
|
const allowDataCollection = $("#td-anonymous-data").is(":checked");
|
||||||
|
|
||||||
ele.fadeOut(200, function(){
|
ele.fadeOut(200, function(){
|
||||||
$TD.onIntroductionClosed(showGuide, allowDataCollection);
|
$TD.onIntroductionClosed(showGuide, allowDataCollection);
|
||||||
@@ -38,4 +39,4 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
})($, $TD);
|
})();
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
(function($TD, $TDX){
|
(function(){
|
||||||
//
|
//
|
||||||
// Variable: Collection of all <a> tags.
|
// Variable: Collection of all <a> tags.
|
||||||
//
|
//
|
||||||
@@ -19,13 +19,13 @@
|
|||||||
(function(){
|
(function(){
|
||||||
const onLinkClick = function(e){
|
const onLinkClick = function(e){
|
||||||
if (e.button === 0 || e.button === 1){
|
if (e.button === 0 || e.button === 1){
|
||||||
let ele = e.currentTarget;
|
const ele = e.currentTarget;
|
||||||
|
|
||||||
$TD.openBrowser(ele.href);
|
$TD.openBrowser(ele.href);
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
if ($TDX.skipOnLinkClick){
|
if ($TDX.skipOnLinkClick){
|
||||||
let parentClasses = ele.parentNode.classList;
|
const parentClasses = ele.parentNode.classList;
|
||||||
|
|
||||||
if (parentClasses.contains("js-tweet-text") || parentClasses.contains("js-quoted-tweet-text") || parentClasses.contains("js-timestamp")){
|
if (parentClasses.contains("js-tweet-text") || parentClasses.contains("js-quoted-tweet-text") || parentClasses.contains("js-timestamp")){
|
||||||
$TD.loadNextNotification();
|
$TD.loadNextNotification();
|
||||||
@@ -46,12 +46,12 @@
|
|||||||
let tooltipTimer, tooltipDisplayed;
|
let tooltipTimer, tooltipDisplayed;
|
||||||
|
|
||||||
addEventListener(links, "mouseenter", function(e){
|
addEventListener(links, "mouseenter", function(e){
|
||||||
let me = e.currentTarget;
|
const me = e.currentTarget;
|
||||||
|
|
||||||
let url = me.getAttribute("data-full-url");
|
const url = me.getAttribute("data-full-url");
|
||||||
return if !url;
|
return if !url;
|
||||||
|
|
||||||
let text = me.textContent;
|
const text = me.textContent;
|
||||||
return if text.charCodeAt(text.length-1) !== 8230 && text.charCodeAt(0) !== 8230; // horizontal ellipsis
|
return if text.charCodeAt(text.length-1) !== 8230 && text.charCodeAt(0) !== 8230; // horizontal ellipsis
|
||||||
|
|
||||||
if ($TDX.expandLinksOnHover){
|
if ($TDX.expandLinksOnHover){
|
||||||
@@ -72,7 +72,7 @@
|
|||||||
return if !e.currentTarget.hasAttribute("data-full-url");
|
return if !e.currentTarget.hasAttribute("data-full-url");
|
||||||
|
|
||||||
if ($TDX.expandLinksOnHover){
|
if ($TDX.expandLinksOnHover){
|
||||||
let prevText = e.currentTarget.getAttribute("td-prev-text");
|
const prevText = e.currentTarget.getAttribute("td-prev-text");
|
||||||
|
|
||||||
if (prevText){
|
if (prevText){
|
||||||
e.currentTarget.innerHTML = prevText;
|
e.currentTarget.innerHTML = prevText;
|
||||||
@@ -89,7 +89,7 @@
|
|||||||
|
|
||||||
addEventListener(links, "mousemove", function(e){
|
addEventListener(links, "mousemove", function(e){
|
||||||
if (tooltipDisplayed && (prevMouseX !== e.clientX || prevMouseY !== e.clientY)){
|
if (tooltipDisplayed && (prevMouseX !== e.clientX || prevMouseY !== e.clientY)){
|
||||||
let url = e.currentTarget.getAttribute("data-full-url");
|
const url = e.currentTarget.getAttribute("data-full-url");
|
||||||
return if !url;
|
return if !url;
|
||||||
|
|
||||||
$TD.displayTooltip(url);
|
$TD.displayTooltip(url);
|
||||||
@@ -99,10 +99,46 @@
|
|||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Work around broken smooth scrolling.
|
||||||
|
//
|
||||||
|
(function(){;
|
||||||
|
let targetY = 0;
|
||||||
|
let delay = -1;
|
||||||
|
let scrolling = false;
|
||||||
|
|
||||||
|
window.TDGF_scrollSmoothly = function(delta){
|
||||||
|
targetY += delta;
|
||||||
|
|
||||||
|
if (targetY < 0){
|
||||||
|
targetY = 0;
|
||||||
|
}
|
||||||
|
else if (targetY > document.body.offsetHeight - window.innerHeight){
|
||||||
|
targetY = document.body.offsetHeight - window.innerHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
const prevY = window.scrollY;
|
||||||
|
window.scrollTo({ top: targetY, left: window.scrollX, behavior: "smooth" });
|
||||||
|
scrolling = true;
|
||||||
|
|
||||||
|
const diff = Math.abs(targetY - prevY);
|
||||||
|
const time = 420 * (Math.log(diff + 510) - 6);
|
||||||
|
|
||||||
|
clearTimeout(delay);
|
||||||
|
delay = setTimeout(function(){ scrolling = false; }, time);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("scroll", function(){
|
||||||
|
if (!scrolling){
|
||||||
|
targetY = window.scrollY;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Work around clipboard HTML formatting.
|
// Block: Work around clipboard HTML formatting.
|
||||||
//
|
//
|
||||||
document.addEventListener("copy", function(e){
|
document.addEventListener("copy", function(){
|
||||||
window.setTimeout($TD.fixClipboard, 0);
|
window.setTimeout($TD.fixClipboard, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -110,7 +146,7 @@
|
|||||||
// Block: Setup a handler for 'Show this thread'.
|
// Block: Setup a handler for 'Show this thread'.
|
||||||
//
|
//
|
||||||
(function(){
|
(function(){
|
||||||
let btn = document.getElementById("tduck-show-thread");
|
const btn = document.getElementById("tduck-show-thread");
|
||||||
return if !btn;
|
return if !btn;
|
||||||
|
|
||||||
btn.addEventListener("click", function(){
|
btn.addEventListener("click", function(){
|
||||||
@@ -145,4 +181,4 @@
|
|||||||
// Block: Force a reset of scroll position on every load.
|
// Block: Force a reset of scroll position on every load.
|
||||||
//
|
//
|
||||||
history.scrollRestoration = "manual";
|
history.scrollRestoration = "manual";
|
||||||
})($TD, $TDX);
|
})();
|
||||||
|
@@ -1,13 +1,13 @@
|
|||||||
(function($TD){
|
(function($TD){
|
||||||
let ele = document.getElementsByTagName("article")[0];
|
const ele = document.getElementsByTagName("article")[0];
|
||||||
ele.style.width = "{width}px";
|
ele.style.width = "{width}px";
|
||||||
|
|
||||||
ele.style.position = "absolute";
|
ele.style.position = "absolute";
|
||||||
let contentHeight = ele.offsetHeight;
|
const contentHeight = ele.offsetHeight;
|
||||||
ele.style.position = "static";
|
ele.style.position = "static";
|
||||||
|
|
||||||
let avatar = ele.querySelector(".tweet-avatar");
|
const avatar = ele.querySelector(".tweet-avatar");
|
||||||
let avatarBottom = avatar ? avatar.getBoundingClientRect().bottom : 0;
|
const avatarBottom = avatar ? avatar.getBoundingClientRect().bottom : 0;
|
||||||
|
|
||||||
$TD.setHeight(Math.floor(Math.max(contentHeight, avatarBottom + 9))).then(() => {
|
$TD.setHeight(Math.floor(Math.max(contentHeight, avatarBottom + 9))).then(() => {
|
||||||
let framesLeft = {frames}; // basic render is done in 1 frame, large media take longer
|
let framesLeft = {frames}; // basic render is done in 1 frame, large media take longer
|
||||||
|
@@ -44,8 +44,8 @@
|
|||||||
border-radius: 0 !important;
|
border-radius: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
html, body {
|
||||||
overflow: hidden !important;
|
overflow-y: auto !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-canvas, div[tweetduck-login-wrapper], body.ResponsiveLayout > div.PageContainer > div.Section {
|
.page-canvas, div[tweetduck-login-wrapper], body.ResponsiveLayout > div.PageContainer > div.Section {
|
||||||
@@ -68,22 +68,18 @@ button[type='submit'] {
|
|||||||
/* General per-page styling */
|
/* General per-page styling */
|
||||||
/****************************/
|
/****************************/
|
||||||
|
|
||||||
html[mobile][login] div[tweetduck-login-wrapper] {
|
html[login] div[tweetduck-login-wrapper] {
|
||||||
/* vertically center page & fix colors */
|
/* vertically center page & fix colors */
|
||||||
margin-top: calc(50vh - 200px);
|
margin-top: calc(50vh - 237px);
|
||||||
padding: 26px 1.1vw;
|
padding: 26px 1.1vw;
|
||||||
|
max-width: 450px;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
html[mobile][login] #tweetduck-helper:hover {
|
html[login] #tweetduck-helper:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
html[desktop][login] #tweetduck-helper {
|
html[logout] {
|
||||||
margin-top: 15px !important;
|
overflow: hidden !important;
|
||||||
font-weight: bold !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
html[mobile][logout] div[role="button"] {
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.3) !important;
|
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
(function(){
|
(function(){
|
||||||
const isLogin = location.pathname === "/login";
|
const isLogin = location.pathname === "/login";
|
||||||
const isLogout = location.pathname === "/logout";
|
const isLogout = location.pathname === "/logout";
|
||||||
const isMobile = location.host === "mobile.twitter.com";
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Function: Inject custom CSS into the page.
|
// Function: Inject custom CSS into the page.
|
||||||
@@ -12,7 +11,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let link = document.createElement("link");
|
const link = document.createElement("link");
|
||||||
link.rel = "stylesheet";
|
link.rel = "stylesheet";
|
||||||
link.href = "https://abs.twimg.com/tduck/css";
|
link.href = "https://abs.twimg.com/tduck/css";
|
||||||
|
|
||||||
@@ -24,13 +23,6 @@
|
|||||||
else if (isLogout){
|
else if (isLogout){
|
||||||
document.documentElement.setAttribute("logout", "");
|
document.documentElement.setAttribute("logout", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isMobile){
|
|
||||||
document.documentElement.setAttribute("mobile", "");
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
document.documentElement.setAttribute("desktop", "");
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
setTimeout(injectCSS, 1);
|
setTimeout(injectCSS, 1);
|
||||||
@@ -40,7 +32,7 @@
|
|||||||
//
|
//
|
||||||
const triggerWhenExists = function(query, callback){
|
const triggerWhenExists = function(query, callback){
|
||||||
let id = window.setInterval(function(){
|
let id = window.setInterval(function(){
|
||||||
let ele = document.querySelector(query);
|
const ele = document.querySelector(query);
|
||||||
|
|
||||||
if (ele && callback(ele)){
|
if (ele && callback(ele)){
|
||||||
window.clearInterval(id);
|
window.clearInterval(id);
|
||||||
@@ -53,9 +45,8 @@
|
|||||||
//
|
//
|
||||||
if (isLogin){
|
if (isLogin){
|
||||||
document.addEventListener("DOMContentLoaded", function(){
|
document.addEventListener("DOMContentLoaded", function(){
|
||||||
if (isMobile){
|
|
||||||
triggerWhenExists("main h1", function(heading){
|
triggerWhenExists("main h1", function(heading){
|
||||||
heading.parentNode.setAttribute("tweetduck-login-wrapper", "");
|
heading.parentNode.parentNode.setAttribute("tweetduck-login-wrapper", "");
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -86,37 +77,12 @@
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
else{
|
|
||||||
const openLinkExternally = function(e){
|
|
||||||
let href = e.currentTarget.getAttribute("href");
|
|
||||||
$TD.openBrowser(href[0] === '/' ? location.origin+href : href);
|
|
||||||
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
};
|
|
||||||
|
|
||||||
for(let link of document.getElementsByTagName("A")){
|
|
||||||
link.addEventListener("click", openLinkExternally);
|
|
||||||
}
|
|
||||||
|
|
||||||
let texts = document.querySelector(".page-canvas > div:last-child");
|
|
||||||
|
|
||||||
if (texts){
|
|
||||||
texts.insertAdjacentHTML("beforeend", `<p id="tweetduck-helper">Used the TweetDuck app before? <a href="#">Import your profile »</a></p>`);
|
|
||||||
|
|
||||||
texts.querySelector("#tweetduck-helper > a").addEventListener("click", function(){
|
|
||||||
$TD.openProfileImport();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Hide cookie crap.
|
// Block: Hide cookie crap.
|
||||||
//
|
//
|
||||||
if (isMobile){
|
|
||||||
document.addEventListener("DOMContentLoaded", function(){
|
document.addEventListener("DOMContentLoaded", function(){
|
||||||
triggerWhenExists("a[href^='https://help.twitter.com/rules-and-policies/twitter-cookies']", function(cookie){
|
triggerWhenExists("a[href^='https://help.twitter.com/rules-and-policies/twitter-cookies']", function(cookie){
|
||||||
while(!!cookie){
|
while(!!cookie){
|
||||||
@@ -132,5 +98,4 @@
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
|
||||||
})();
|
})();
|
||||||
|
@@ -1,4 +1,9 @@
|
|||||||
(function($TDU){
|
(function(){
|
||||||
|
if (!("$TDU" in window)){
|
||||||
|
console.error("Missing $TDU");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Function: Creates the update notification element. Removes the old one if already exists.
|
// Function: Creates the update notification element. Removes the old one if already exists.
|
||||||
//
|
//
|
||||||
@@ -32,7 +37,7 @@
|
|||||||
|
|
||||||
// notification
|
// notification
|
||||||
let ele = document.getElementById("tweetduck-update");
|
let ele = document.getElementById("tweetduck-update");
|
||||||
let existed = !!ele;
|
const existed = !!ele;
|
||||||
|
|
||||||
if (existed){
|
if (existed){
|
||||||
ele.remove();
|
ele.remove();
|
||||||
@@ -136,10 +141,11 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
try{
|
try{
|
||||||
throw false if !($._data(document, "events").TD.some(obj => obj.namespace === "ready"));
|
throw "Missing jQuery or TD.ready event" if !($._data(document, "events").TD.some(obj => obj.namespace === "ready"));
|
||||||
|
|
||||||
$(document).one("TD.ready", triggerCheck);
|
$(document).one("TD.ready", triggerCheck);
|
||||||
}catch(err){
|
}catch(err){
|
||||||
|
console.warn(err);
|
||||||
setTimeout(triggerCheck, 500);
|
setTimeout(triggerCheck, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,4 +153,4 @@
|
|||||||
// Block: Setup global functions.
|
// Block: Setup global functions.
|
||||||
//
|
//
|
||||||
window.TDUF_displayNotification = displayNotification;
|
window.TDUF_displayNotification = displayNotification;
|
||||||
})($TDU);
|
})();
|
||||||
|
149
TweetDuck.csproj
149
TweetDuck.csproj
@@ -1,10 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="packages\CefSharp.WinForms.86.0.241\build\CefSharp.WinForms.props" Condition="Exists('packages\CefSharp.WinForms.86.0.241\build\CefSharp.WinForms.props')" />
|
||||||
|
<Import Project="packages\CefSharp.Common.86.0.241\build\CefSharp.Common.props" Condition="Exists('packages\CefSharp.Common.86.0.241\build\CefSharp.Common.props')" />
|
||||||
|
<Import Project="packages\cef.redist.x86.86.0.24\build\cef.redist.x86.props" Condition="Exists('packages\cef.redist.x86.86.0.24\build\cef.redist.x86.props')" />
|
||||||
|
<Import Project="packages\cef.redist.x64.86.0.24\build\cef.redist.x64.props" Condition="Exists('packages\cef.redist.x64.86.0.24\build\cef.redist.x64.props')" />
|
||||||
<Import Project="packages\Microsoft.Net.Compilers.3.0.0\build\Microsoft.Net.Compilers.props" Condition="Exists('packages\Microsoft.Net.Compilers.3.0.0\build\Microsoft.Net.Compilers.props')" />
|
<Import Project="packages\Microsoft.Net.Compilers.3.0.0\build\Microsoft.Net.Compilers.props" Condition="Exists('packages\Microsoft.Net.Compilers.3.0.0\build\Microsoft.Net.Compilers.props')" />
|
||||||
<Import Project="packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.props" Condition="Exists('packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.props')" />
|
|
||||||
<Import Project="packages\CefSharp.Common.67.0.0\build\CefSharp.Common.props" Condition="Exists('packages\CefSharp.Common.67.0.0\build\CefSharp.Common.props')" />
|
|
||||||
<Import Project="packages\cef.redist.x86.3.3396.1786\build\cef.redist.x86.props" Condition="Exists('packages\cef.redist.x86.3.3396.1786\build\cef.redist.x86.props')" />
|
|
||||||
<Import Project="packages\cef.redist.x64.3.3396.1786\build\cef.redist.x64.props" Condition="Exists('packages\cef.redist.x64.3.3396.1786\build\cef.redist.x64.props')" />
|
|
||||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
@@ -53,15 +53,71 @@
|
|||||||
<Reference Include="System.Windows.Forms" />
|
<Reference Include="System.Windows.Forms" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Version.cs" />
|
<Compile Include="Management\LockManager.cs" />
|
||||||
<Compile Include="Configuration\Arguments.cs" />
|
<Compile Include="Application\SystemHandler.cs" />
|
||||||
<Compile Include="Configuration\ConfigManager.cs" />
|
|
||||||
<Compile Include="Configuration\SystemConfig.cs" />
|
|
||||||
<Compile Include="Configuration\UserConfig.cs" />
|
|
||||||
<Compile Include="Browser\Adapters\CefScriptExecutor.cs" />
|
<Compile Include="Browser\Adapters\CefScriptExecutor.cs" />
|
||||||
<Compile Include="Browser\Bridge\PropertyBridge.cs" />
|
<Compile Include="Browser\Bridge\PropertyBridge.cs" />
|
||||||
|
<Compile Include="Browser\Bridge\TweetDeckBridge.cs" />
|
||||||
<Compile Include="Browser\Bridge\UpdateBridge.cs" />
|
<Compile Include="Browser\Bridge\UpdateBridge.cs" />
|
||||||
|
<Compile Include="Browser\Data\ContextInfo.cs" />
|
||||||
|
<Compile Include="Browser\Data\ResourceHandlers.cs" />
|
||||||
|
<Compile Include="Browser\Data\ResourceLink.cs" />
|
||||||
|
<Compile Include="Browser\Data\WindowState.cs" />
|
||||||
|
<Compile Include="Browser\Handling\ContextMenuBase.cs" />
|
||||||
|
<Compile Include="Browser\Handling\ContextMenuBrowser.cs" />
|
||||||
|
<Compile Include="Browser\Handling\ContextMenuGuide.cs" />
|
||||||
|
<Compile Include="Browser\Handling\ContextMenuNotification.cs" />
|
||||||
|
<Compile Include="Browser\Handling\DragHandlerBrowser.cs" />
|
||||||
|
<Compile Include="Browser\Handling\Filters\ResponseFilterBase.cs" />
|
||||||
|
<Compile Include="Browser\Handling\Filters\ResponseFilterVendor.cs" />
|
||||||
|
<Compile Include="Browser\Handling\General\BrowserProcessHandler.cs" />
|
||||||
|
<Compile Include="Browser\Handling\General\FileDialogHandler.cs" />
|
||||||
|
<Compile Include="Browser\Handling\General\JavaScriptDialogHandler.cs" />
|
||||||
|
<Compile Include="Browser\Handling\General\LifeSpanHandler.cs" />
|
||||||
|
<Compile Include="Browser\Handling\KeyboardHandlerBase.cs" />
|
||||||
|
<Compile Include="Browser\Handling\KeyboardHandlerBrowser.cs" />
|
||||||
|
<Compile Include="Browser\Handling\KeyboardHandlerNotification.cs" />
|
||||||
|
<Compile Include="Browser\Handling\RequestHandlerBase.cs" />
|
||||||
|
<Compile Include="Browser\Handling\RequestHandlerBrowser.cs" />
|
||||||
|
<Compile Include="Browser\Handling\ResourceHandlerNotification.cs" />
|
||||||
|
<Compile Include="Browser\Handling\ResourceRequestHandler.cs" />
|
||||||
|
<Compile Include="Browser\Handling\ResourceRequestHandlerBase.cs" />
|
||||||
|
<Compile Include="Browser\Handling\ResourceRequestHandlerBrowser.cs" />
|
||||||
|
<Compile Include="Browser\Notification\Screenshot\ScreenshotBridge.cs" />
|
||||||
|
<Compile Include="Browser\Notification\Screenshot\TweetScreenshotManager.cs" />
|
||||||
|
<Compile Include="Browser\Notification\SoundNotification.cs" />
|
||||||
|
<Compile Include="Browser\TweetDeckBrowser.cs" />
|
||||||
|
<Compile Include="Configuration\Arguments.cs" />
|
||||||
|
<Compile Include="Configuration\ConfigManager.cs" />
|
||||||
|
<Compile Include="Configuration\PluginConfig.cs" />
|
||||||
|
<Compile Include="Configuration\SystemConfig.cs" />
|
||||||
|
<Compile Include="Configuration\UserConfig.cs" />
|
||||||
<Compile Include="Controls\ControlExtensions.cs" />
|
<Compile Include="Controls\ControlExtensions.cs" />
|
||||||
|
<Compile Include="Management\Analytics\AnalyticsFile.cs" />
|
||||||
|
<Compile Include="Management\Analytics\AnalyticsManager.cs" />
|
||||||
|
<Compile Include="Management\Analytics\AnalyticsReport.cs" />
|
||||||
|
<Compile Include="Management\Analytics\AnalyticsReportGenerator.cs" />
|
||||||
|
<Compile Include="Management\BrowserCache.cs" />
|
||||||
|
<Compile Include="Management\ClipboardManager.cs" />
|
||||||
|
<Compile Include="Management\FormManager.cs" />
|
||||||
|
<Compile Include="Management\ProfileManager.cs" />
|
||||||
|
<Compile Include="Management\VideoPlayer.cs" />
|
||||||
|
<Compile Include="Plugins\PluginDispatcher.cs" />
|
||||||
|
<Compile Include="Plugins\PluginSchemeFactory.cs" />
|
||||||
|
<Compile Include="Program.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="Reporter.cs" />
|
||||||
|
<Compile Include="Resources\ScriptLoader.cs" />
|
||||||
|
<Compile Include="Resources\ScriptLoaderDebug.cs" />
|
||||||
|
<Compile Include="Updates\UpdateCheckClient.cs" />
|
||||||
|
<Compile Include="Updates\UpdateInstaller.cs" />
|
||||||
|
<Compile Include="Utils\BrowserUtils.cs" />
|
||||||
|
<Compile Include="Utils\NativeMethods.cs" />
|
||||||
|
<Compile Include="Utils\TwitterUtils.cs" />
|
||||||
|
<Compile Include="Utils\WindowsUtils.cs" />
|
||||||
|
<Compile Include="Version.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
<Compile Include="Controls\FlatButton.cs">
|
<Compile Include="Controls\FlatButton.cs">
|
||||||
<SubType>Component</SubType>
|
<SubType>Component</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -74,30 +130,12 @@
|
|||||||
<Compile Include="Controls\NumericUpDownEx.cs">
|
<Compile Include="Controls\NumericUpDownEx.cs">
|
||||||
<SubType>Component</SubType>
|
<SubType>Component</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Management\ClipboardManager.cs" />
|
|
||||||
<Compile Include="Management\FormManager.cs" />
|
|
||||||
<Compile Include="Browser\Handling\ContextMenuGuide.cs" />
|
|
||||||
<Compile Include="Browser\Handling\DragHandlerBrowser.cs" />
|
|
||||||
<Compile Include="Browser\Handling\Filters\ResponseFilterBase.cs" />
|
|
||||||
<Compile Include="Browser\Handling\Filters\ResponseFilterVendor.cs" />
|
|
||||||
<Compile Include="Browser\Handling\General\BrowserProcessHandler.cs" />
|
|
||||||
<Compile Include="Browser\Handling\ContextMenuBase.cs" />
|
|
||||||
<Compile Include="Browser\Handling\ContextMenuBrowser.cs" />
|
|
||||||
<Compile Include="Browser\FormBrowser.cs">
|
<Compile Include="Browser\FormBrowser.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Browser\FormBrowser.Designer.cs">
|
<Compile Include="Browser\FormBrowser.Designer.cs">
|
||||||
<DependentUpon>FormBrowser.cs</DependentUpon>
|
<DependentUpon>FormBrowser.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Browser\Handling\General\FileDialogHandler.cs" />
|
|
||||||
<Compile Include="Browser\Handling\KeyboardHandlerBase.cs" />
|
|
||||||
<Compile Include="Browser\Handling\KeyboardHandlerBrowser.cs" />
|
|
||||||
<Compile Include="Browser\Handling\KeyboardHandlerNotification.cs" />
|
|
||||||
<Compile Include="Browser\Handling\RequestHandlerBase.cs" />
|
|
||||||
<Compile Include="Browser\Handling\RequestHandlerBrowser.cs" />
|
|
||||||
<Compile Include="Browser\Handling\ResourceHandlerFactory.cs" />
|
|
||||||
<Compile Include="Browser\Handling\ResourceHandlerNotification.cs" />
|
|
||||||
<Compile Include="Browser\Data\ContextInfo.cs" />
|
|
||||||
<Compile Include="Browser\Notification\Example\FormNotificationExample.cs">
|
<Compile Include="Browser\Notification\Example\FormNotificationExample.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -113,20 +151,12 @@
|
|||||||
<Compile Include="Browser\Notification\FormNotificationBase.Designer.cs">
|
<Compile Include="Browser\Notification\FormNotificationBase.Designer.cs">
|
||||||
<DependentUpon>FormNotificationBase.cs</DependentUpon>
|
<DependentUpon>FormNotificationBase.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Browser\Handling\ContextMenuNotification.cs" />
|
|
||||||
<Compile Include="Browser\Handling\General\JavaScriptDialogHandler.cs" />
|
|
||||||
<Compile Include="Browser\Handling\General\LifeSpanHandler.cs" />
|
|
||||||
<Compile Include="Browser\Notification\FormNotificationTweet.cs">
|
<Compile Include="Browser\Notification\FormNotificationTweet.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Browser\Notification\FormNotificationTweet.Designer.cs">
|
<Compile Include="Browser\Notification\FormNotificationTweet.Designer.cs">
|
||||||
<DependentUpon>FormNotificationTweet.cs</DependentUpon>
|
<DependentUpon>FormNotificationTweet.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Browser\Notification\SoundNotification.cs" />
|
|
||||||
<Compile Include="Management\Analytics\AnalyticsFile.cs" />
|
|
||||||
<Compile Include="Management\Analytics\AnalyticsManager.cs" />
|
|
||||||
<Compile Include="Management\Analytics\AnalyticsReport.cs" />
|
|
||||||
<Compile Include="Management\Analytics\AnalyticsReportGenerator.cs" />
|
|
||||||
<Compile Include="Dialogs\FormAbout.cs">
|
<Compile Include="Dialogs\FormAbout.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -151,7 +181,6 @@
|
|||||||
<Compile Include="Dialogs\FormPlugins.Designer.cs">
|
<Compile Include="Dialogs\FormPlugins.Designer.cs">
|
||||||
<DependentUpon>FormPlugins.cs</DependentUpon>
|
<DependentUpon>FormPlugins.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Management\VideoPlayer.cs" />
|
|
||||||
<Compile Include="Dialogs\Settings\DialogSettingsAnalytics.cs">
|
<Compile Include="Dialogs\Settings\DialogSettingsAnalytics.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -206,9 +235,6 @@
|
|||||||
<Compile Include="Dialogs\Settings\TabSettingsTray.Designer.cs">
|
<Compile Include="Dialogs\Settings\TabSettingsTray.Designer.cs">
|
||||||
<DependentUpon>TabSettingsTray.cs</DependentUpon>
|
<DependentUpon>TabSettingsTray.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Browser\TweetDeckBrowser.cs" />
|
|
||||||
<Compile Include="Utils\TwitterUtils.cs" />
|
|
||||||
<Compile Include="Management\ProfileManager.cs" />
|
|
||||||
<Compile Include="Dialogs\Settings\TabSettingsAdvanced.cs">
|
<Compile Include="Dialogs\Settings\TabSettingsAdvanced.cs">
|
||||||
<SubType>UserControl</SubType>
|
<SubType>UserControl</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -233,41 +259,29 @@
|
|||||||
<Compile Include="Dialogs\Settings\TabSettingsNotifications.Designer.cs">
|
<Compile Include="Dialogs\Settings\TabSettingsNotifications.Designer.cs">
|
||||||
<DependentUpon>TabSettingsNotifications.cs</DependentUpon>
|
<DependentUpon>TabSettingsNotifications.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Browser\Notification\Screenshot\ScreenshotBridge.cs" />
|
|
||||||
<Compile Include="Browser\Notification\Screenshot\FormNotificationScreenshotable.cs">
|
<Compile Include="Browser\Notification\Screenshot\FormNotificationScreenshotable.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Browser\Notification\Screenshot\TweetScreenshotManager.cs" />
|
|
||||||
<Compile Include="Browser\Data\ResourceLink.cs" />
|
|
||||||
<Compile Include="Browser\Data\WindowState.cs" />
|
|
||||||
<Compile Include="Utils\WindowsUtils.cs" />
|
|
||||||
<Compile Include="Browser\Bridge\TweetDeckBridge.cs" />
|
|
||||||
<Compile Include="Dialogs\FormSettings.cs">
|
<Compile Include="Dialogs\FormSettings.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Dialogs\FormSettings.Designer.cs">
|
<Compile Include="Dialogs\FormSettings.Designer.cs">
|
||||||
<DependentUpon>FormSettings.cs</DependentUpon>
|
<DependentUpon>FormSettings.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Application\LockHandler.cs" />
|
|
||||||
<Compile Include="Application\SystemHandler.cs" />
|
|
||||||
<Compile Include="Plugins\PluginControl.cs">
|
<Compile Include="Plugins\PluginControl.cs">
|
||||||
<SubType>UserControl</SubType>
|
<SubType>UserControl</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Plugins\PluginControl.Designer.cs">
|
<Compile Include="Plugins\PluginControl.Designer.cs">
|
||||||
<DependentUpon>PluginControl.cs</DependentUpon>
|
<DependentUpon>PluginControl.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Plugins\PluginDispatcher.cs" />
|
|
||||||
<Compile Include="Controls\FlowLayoutPanelNoHScroll.cs">
|
<Compile Include="Controls\FlowLayoutPanelNoHScroll.cs">
|
||||||
<SubType>Component</SubType>
|
<SubType>Component</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Configuration\PluginConfig.cs" />
|
|
||||||
<Compile Include="Properties\Resources.Designer.cs">
|
<Compile Include="Properties\Resources.Designer.cs">
|
||||||
<AutoGen>True</AutoGen>
|
<AutoGen>True</AutoGen>
|
||||||
<DesignTime>True</DesignTime>
|
<DesignTime>True</DesignTime>
|
||||||
<DependentUpon>Resources.resx</DependentUpon>
|
<DependentUpon>Resources.resx</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Reporter.cs" />
|
|
||||||
<Compile Include="Resources\ScriptLoaderDebug.cs" />
|
|
||||||
<Compile Include="Updates\FormUpdateDownload.cs">
|
<Compile Include="Updates\FormUpdateDownload.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -280,13 +294,6 @@
|
|||||||
<Compile Include="Browser\TrayIcon.Designer.cs">
|
<Compile Include="Browser\TrayIcon.Designer.cs">
|
||||||
<DependentUpon>TrayIcon.cs</DependentUpon>
|
<DependentUpon>TrayIcon.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Management\BrowserCache.cs" />
|
|
||||||
<Compile Include="Utils\BrowserUtils.cs" />
|
|
||||||
<Compile Include="Utils\NativeMethods.cs" />
|
|
||||||
<Compile Include="Updates\UpdateCheckClient.cs" />
|
|
||||||
<Compile Include="Program.cs" />
|
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
|
||||||
<Compile Include="Resources\ScriptLoader.cs" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
|
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
|
||||||
@@ -314,12 +321,12 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
<None Include="Resources\Images\avatar.png" />
|
<None Include="Resources\Images\avatar.png" />
|
||||||
<None Include="Resources\Images\icon.ico" />
|
|
||||||
<None Include="Resources\Images\icon-tray-muted.ico" />
|
|
||||||
<None Include="Resources\Images\icon-muted.ico" />
|
<None Include="Resources\Images\icon-muted.ico" />
|
||||||
<None Include="Resources\Images\icon-small.ico" />
|
<None Include="Resources\Images\icon-small.ico" />
|
||||||
|
<None Include="Resources\Images\icon-tray-muted.ico" />
|
||||||
<None Include="Resources\Images\icon-tray-new.ico" />
|
<None Include="Resources\Images\icon-tray-new.ico" />
|
||||||
<None Include="Resources\Images\icon-tray.ico" />
|
<None Include="Resources\Images\icon-tray.ico" />
|
||||||
|
<None Include="Resources\Images\icon.ico" />
|
||||||
<None Include="Resources\Images\spinner.apng" />
|
<None Include="Resources\Images\spinner.apng" />
|
||||||
<None Include="Resources\Plugins\.debug\.meta" />
|
<None Include="Resources\Plugins\.debug\.meta" />
|
||||||
<None Include="Resources\Plugins\.debug\browser.js" />
|
<None Include="Resources\Plugins\.debug\browser.js" />
|
||||||
@@ -346,6 +353,11 @@
|
|||||||
<None Include="Resources\PostCefUpdate.ps1" />
|
<None Include="Resources\PostCefUpdate.ps1" />
|
||||||
<None Include="Resources\Scripts\code.js" />
|
<None Include="Resources\Scripts\code.js" />
|
||||||
<None Include="Resources\Scripts\imports\markup\introduction.html" />
|
<None Include="Resources\Scripts\imports\markup\introduction.html" />
|
||||||
|
<None Include="Resources\Scripts\imports\markup\offline.html" />
|
||||||
|
<None Include="Resources\Scripts\imports\markup\pin.html" />
|
||||||
|
<None Include="Resources\Scripts\imports\scripts\browser.features.js" />
|
||||||
|
<None Include="Resources\Scripts\imports\scripts\browser.globals.js" />
|
||||||
|
<None Include="Resources\Scripts\imports\scripts\browser.tweaks.js" />
|
||||||
<None Include="Resources\Scripts\imports\scripts\plugins.base.js" />
|
<None Include="Resources\Scripts\imports\scripts\plugins.base.js" />
|
||||||
<None Include="Resources\Scripts\imports\styles\introduction.css" />
|
<None Include="Resources\Scripts\imports\styles\introduction.css" />
|
||||||
<None Include="Resources\Scripts\imports\styles\twitter.base.css" />
|
<None Include="Resources\Scripts\imports\styles\twitter.base.css" />
|
||||||
@@ -360,6 +372,7 @@
|
|||||||
<None Include="Resources\Scripts\screenshot.js" />
|
<None Include="Resources\Scripts\screenshot.js" />
|
||||||
<None Include="Resources\Scripts\styles\browser.css" />
|
<None Include="Resources\Scripts\styles\browser.css" />
|
||||||
<None Include="Resources\Scripts\styles\notification.css" />
|
<None Include="Resources\Scripts\styles\notification.css" />
|
||||||
|
<None Include="Resources\Scripts\styles\twitter.css" />
|
||||||
<None Include="Resources\Scripts\twitter.js" />
|
<None Include="Resources\Scripts\twitter.js" />
|
||||||
<None Include="Resources\Scripts\update.js" />
|
<None Include="Resources\Scripts\update.js" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -413,14 +426,14 @@ IF EXIST "$(ProjectDir)bld\post_build.exe" (
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Error Condition="!Exists('packages\cef.redist.x64.3.3396.1786\build\cef.redist.x64.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x64.3.3396.1786\build\cef.redist.x64.props'))" />
|
|
||||||
<Error Condition="!Exists('packages\cef.redist.x86.3.3396.1786\build\cef.redist.x86.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x86.3.3396.1786\build\cef.redist.x86.props'))" />
|
|
||||||
<Error Condition="!Exists('packages\CefSharp.Common.67.0.0\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.67.0.0\build\CefSharp.Common.props'))" />
|
|
||||||
<Error Condition="!Exists('packages\CefSharp.Common.67.0.0\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.67.0.0\build\CefSharp.Common.targets'))" />
|
|
||||||
<Error Condition="!Exists('packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.props'))" />
|
|
||||||
<Error Condition="!Exists('packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.targets'))" />
|
|
||||||
<Error Condition="!Exists('packages\Microsoft.Net.Compilers.3.0.0\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.Net.Compilers.3.0.0\build\Microsoft.Net.Compilers.props'))" />
|
<Error Condition="!Exists('packages\Microsoft.Net.Compilers.3.0.0\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.Net.Compilers.3.0.0\build\Microsoft.Net.Compilers.props'))" />
|
||||||
|
<Error Condition="!Exists('packages\cef.redist.x64.86.0.24\build\cef.redist.x64.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x64.86.0.24\build\cef.redist.x64.props'))" />
|
||||||
|
<Error Condition="!Exists('packages\cef.redist.x86.86.0.24\build\cef.redist.x86.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x86.86.0.24\build\cef.redist.x86.props'))" />
|
||||||
|
<Error Condition="!Exists('packages\CefSharp.Common.86.0.241\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.86.0.241\build\CefSharp.Common.props'))" />
|
||||||
|
<Error Condition="!Exists('packages\CefSharp.Common.86.0.241\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.86.0.241\build\CefSharp.Common.targets'))" />
|
||||||
|
<Error Condition="!Exists('packages\CefSharp.WinForms.86.0.241\build\CefSharp.WinForms.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.86.0.241\build\CefSharp.WinForms.props'))" />
|
||||||
|
<Error Condition="!Exists('packages\CefSharp.WinForms.86.0.241\build\CefSharp.WinForms.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.86.0.241\build\CefSharp.WinForms.targets'))" />
|
||||||
</Target>
|
</Target>
|
||||||
<Import Project="packages\CefSharp.Common.67.0.0\build\CefSharp.Common.targets" Condition="Exists('packages\CefSharp.Common.67.0.0\build\CefSharp.Common.targets')" />
|
<Import Project="packages\CefSharp.Common.86.0.241\build\CefSharp.Common.targets" Condition="Exists('packages\CefSharp.Common.86.0.241\build\CefSharp.Common.targets')" />
|
||||||
<Import Project="packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.targets" Condition="Exists('packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.targets')" />
|
<Import Project="packages\CefSharp.WinForms.86.0.241\build\CefSharp.WinForms.targets" Condition="Exists('packages\CefSharp.WinForms.86.0.241\build\CefSharp.WinForms.targets')" />
|
||||||
</Project>
|
</Project>
|
@@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetLib.Core.Features.Updates;
|
using TweetLib.Core.Systems.Updates;
|
||||||
|
|
||||||
namespace TweetDuck.Updates{
|
namespace TweetDuck.Updates{
|
||||||
sealed partial class FormUpdateDownload : Form{
|
sealed partial class FormUpdateDownload : Form{
|
||||||
|
@@ -6,7 +6,7 @@ using System.Text;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Web.Script.Serialization;
|
using System.Web.Script.Serialization;
|
||||||
using TweetDuck.Utils;
|
using TweetDuck.Utils;
|
||||||
using TweetLib.Core.Features.Updates;
|
using TweetLib.Core.Systems.Updates;
|
||||||
using TweetLib.Core.Utils;
|
using TweetLib.Core.Utils;
|
||||||
using JsonObject = System.Collections.Generic.IDictionary<string, object>;
|
using JsonObject = System.Collections.Generic.IDictionary<string, object>;
|
||||||
|
|
||||||
|
37
Updates/UpdateInstaller.cs
Normal file
37
Updates/UpdateInstaller.cs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using TweetDuck.Configuration;
|
||||||
|
using TweetLib.Core.Utils;
|
||||||
|
|
||||||
|
namespace TweetDuck.Updates{
|
||||||
|
sealed class UpdateInstaller{
|
||||||
|
public string Path { get; }
|
||||||
|
|
||||||
|
public UpdateInstaller(string path){
|
||||||
|
this.Path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Launch(){
|
||||||
|
// ProgramPath has a trailing backslash
|
||||||
|
string arguments = "/SP- /SILENT /FORCECLOSEAPPLICATIONS /UPDATEPATH=\"" + Program.ProgramPath + "\" /RUNARGS=\"" + Arguments.GetCurrentForInstallerCmd() + "\"" + (Program.IsPortable ? " /PORTABLE=1" : "");
|
||||||
|
bool runElevated = !Program.IsPortable || !FileUtils.CheckFolderWritePermission(Program.ProgramPath);
|
||||||
|
|
||||||
|
try{
|
||||||
|
using(Process.Start(new ProcessStartInfo{
|
||||||
|
FileName = Path,
|
||||||
|
Arguments = arguments,
|
||||||
|
Verb = runElevated ? "runas" : string.Empty,
|
||||||
|
ErrorDialog = true
|
||||||
|
})){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}catch(Win32Exception e) when (e.NativeErrorCode == 0x000004C7){ // operation canceled by the user
|
||||||
|
return false;
|
||||||
|
}catch(Exception e){
|
||||||
|
Program.Reporter.HandleException("Update Installer Error", "Could not launch update installer.", true, e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -9,6 +9,7 @@ using TweetDuck.Browser;
|
|||||||
using TweetDuck.Configuration;
|
using TweetDuck.Configuration;
|
||||||
using TweetDuck.Dialogs;
|
using TweetDuck.Dialogs;
|
||||||
using TweetDuck.Management;
|
using TweetDuck.Management;
|
||||||
|
using TweetLib.Core;
|
||||||
using TweetLib.Core.Features.Twitter;
|
using TweetLib.Core.Features.Twitter;
|
||||||
|
|
||||||
namespace TweetDuck.Utils{
|
namespace TweetDuck.Utils{
|
||||||
@@ -27,17 +28,7 @@ namespace TweetDuck.Utils{
|
|||||||
args["disable-gpu-compositing"] = "1";
|
args["disable-gpu-compositing"] = "1";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Config.EnableSmoothScrolling){
|
if (!Config.EnableSmoothScrolling){
|
||||||
args["disable-threaded-scrolling"] = "1";
|
|
||||||
|
|
||||||
if (args.TryGetValue("disable-features", out string disabledFeatures)){
|
|
||||||
args["disable-features"] = "TouchpadAndWheelScrollLatching," + disabledFeatures;
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
args["disable-features"] = "TouchpadAndWheelScrollLatching";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
args["disable-smooth-scrolling"] = "1";
|
args["disable-smooth-scrolling"] = "1";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,6 +71,11 @@ namespace TweetDuck.Utils{
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void RegisterJsBridge(this IWebBrowser browserControl, string name, object bridge){
|
||||||
|
CefSharpSettings.LegacyJavascriptBindingEnabled = true;
|
||||||
|
browserControl.JavascriptObjectRepository.Register(name, bridge, isAsync: true, BindingOptions.DefaultBinder);
|
||||||
|
}
|
||||||
|
|
||||||
public static void OpenDevToolsCustom(this IWebBrowser browser){
|
public static void OpenDevToolsCustom(this IWebBrowser browser){
|
||||||
var info = new WindowInfo();
|
var info = new WindowInfo();
|
||||||
info.SetAsPopup(IntPtr.Zero, "Dev Tools");
|
info.SetAsPopup(IntPtr.Zero, "Dev Tools");
|
||||||
@@ -105,7 +101,7 @@ namespace TweetDuck.Utils{
|
|||||||
string browserPath = Config.BrowserPath;
|
string browserPath = Config.BrowserPath;
|
||||||
|
|
||||||
if (browserPath == null || !File.Exists(browserPath)){
|
if (browserPath == null || !File.Exists(browserPath)){
|
||||||
WindowsUtils.OpenAssociatedProgram(url);
|
App.SystemHandler.OpenAssociatedProgram(url);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
string quotedUrl = '"' + url + '"';
|
string quotedUrl = '"' + url + '"';
|
||||||
|
@@ -9,6 +9,7 @@ using CefSharp;
|
|||||||
using TweetDuck.Browser.Data;
|
using TweetDuck.Browser.Data;
|
||||||
using TweetDuck.Dialogs;
|
using TweetDuck.Dialogs;
|
||||||
using TweetDuck.Management;
|
using TweetDuck.Management;
|
||||||
|
using TweetLib.Core;
|
||||||
using TweetLib.Core.Features.Twitter;
|
using TweetLib.Core.Features.Twitter;
|
||||||
using TweetLib.Core.Utils;
|
using TweetLib.Core.Utils;
|
||||||
using Cookie = CefSharp.Cookie;
|
using Cookie = CefSharp.Cookie;
|
||||||
@@ -18,7 +19,7 @@ namespace TweetDuck.Utils{
|
|||||||
public static readonly Color BackgroundColor = Color.FromArgb(28, 99, 153);
|
public static readonly Color BackgroundColor = Color.FromArgb(28, 99, 153);
|
||||||
public const string BackgroundColorOverride = "setTimeout(function f(){let h=document.head;if(!h){setTimeout(f,5);return;}let e=document.createElement('style');e.innerHTML='body,body::before{background:#1c6399!important;margin:0}';h.appendChild(e);},1)";
|
public const string BackgroundColorOverride = "setTimeout(function f(){let h=document.head;if(!h){setTimeout(f,5);return;}let e=document.createElement('style');e.innerHTML='body,body::before{background:#1c6399!important;margin:0}';h.appendChild(e);},1)";
|
||||||
|
|
||||||
public static readonly ResourceLink LoadingSpinner = new ResourceLink("https://ton.twimg.com/tduck/spinner", ResourceHandler.FromByteArray(Properties.Resources.spinner, "image/apng"));
|
public static readonly ResourceLink LoadingSpinner = new ResourceLink("https://ton.twimg.com/tduck/spinner", ResourceHandlers.ForBytes(Properties.Resources.spinner, "image/apng"));
|
||||||
|
|
||||||
public static readonly string[] DictionaryWords = {
|
public static readonly string[] DictionaryWords = {
|
||||||
"tweetdeck", "TweetDeck", "tweetduck", "TweetDuck", "TD"
|
"tweetdeck", "TweetDeck", "tweetduck", "TweetDuck", "TD"
|
||||||
@@ -44,7 +45,7 @@ namespace TweetDuck.Utils{
|
|||||||
string ext = Path.GetExtension(path);
|
string ext = Path.GetExtension(path);
|
||||||
|
|
||||||
if (ImageUrl.ValidExtensions.Contains(ext)){
|
if (ImageUrl.ValidExtensions.Contains(ext)){
|
||||||
WindowsUtils.OpenAssociatedProgram(path);
|
App.SystemHandler.OpenAssociatedProgram(path);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
FormMessage.Error("Image Download", "Invalid file extension " + ext, FormMessage.OK);
|
FormMessage.Error("Image Download", "Invalid file extension " + ext, FormMessage.OK);
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
@@ -18,24 +16,6 @@ namespace TweetDuck.Utils{
|
|||||||
return ver.Major == major && ver.Minor == minor;
|
return ver.Major == major && ver.Minor == minor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool OpenAssociatedProgram(string file, string arguments = "", bool runElevated = false){
|
|
||||||
try{
|
|
||||||
using(Process.Start(new ProcessStartInfo{
|
|
||||||
FileName = file,
|
|
||||||
Arguments = arguments,
|
|
||||||
Verb = runElevated ? "runas" : string.Empty,
|
|
||||||
ErrorDialog = true
|
|
||||||
})){
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}catch(Win32Exception e) when (e.NativeErrorCode == 0x000004C7){ // operation canceled by the user
|
|
||||||
return false;
|
|
||||||
}catch(Exception e){
|
|
||||||
Program.Reporter.HandleException("Error Opening Program", "Could not open the associated program for " + file, true, e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool TrySleepUntil(Func<bool> test, int timeoutMillis, int timeStepMillis){
|
public static bool TrySleepUntil(Func<bool> test, int timeoutMillis, int timeStepMillis){
|
||||||
for(int waited = 0; waited < timeoutMillis; waited += timeStepMillis){
|
for(int waited = 0; waited < timeoutMillis; waited += timeStepMillis){
|
||||||
if (test()){
|
if (test()){
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Version = TweetDuck.Version;
|
using TweetDuck;
|
||||||
|
|
||||||
[assembly: AssemblyVersion(Version.Tag)]
|
[assembly: AssemblyVersion(Version.Tag)]
|
||||||
[assembly: AssemblyFileVersion(Version.Tag)]
|
[assembly: AssemblyFileVersion(Version.Tag)]
|
||||||
|
|
||||||
namespace TweetDuck{
|
namespace TweetDuck{
|
||||||
internal static class Version{
|
internal static class Version{
|
||||||
public const string Tag = "1.18.5";
|
public const string Tag = "1.20";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
@@ -65,6 +65,7 @@ Type: filesandordirs; Name: "{localappdata}\{#MyAppName}\GPUCache"
|
|||||||
[InstallDelete]
|
[InstallDelete]
|
||||||
Type: files; Name: "{app}\CEFSHARP-LICENSE.txt"
|
Type: files; Name: "{app}\CEFSHARP-LICENSE.txt"
|
||||||
Type: files; Name: "{app}\LICENSE.txt"
|
Type: files; Name: "{app}\LICENSE.txt"
|
||||||
|
Type: files; Name: "{app}\natives_blob.bin"
|
||||||
Type: filesandordirs; Name: "{app}\scripts"
|
Type: filesandordirs; Name: "{app}\scripts"
|
||||||
Type: filesandordirs; Name: "{app}\plugins\official"
|
Type: filesandordirs; Name: "{app}\plugins\official"
|
||||||
|
|
||||||
|
@@ -5,7 +5,6 @@ namespace TweetLib.Core{
|
|||||||
public sealed class App{
|
public sealed class App{
|
||||||
#pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.
|
#pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.
|
||||||
public static IAppErrorHandler ErrorHandler { get; private set; }
|
public static IAppErrorHandler ErrorHandler { get; private set; }
|
||||||
public static IAppLockHandler LockHandler { get; private set; }
|
|
||||||
public static IAppSystemHandler SystemHandler { get; private set; }
|
public static IAppSystemHandler SystemHandler { get; private set; }
|
||||||
public static IAppResourceHandler ResourceHandler { get; private set; }
|
public static IAppResourceHandler ResourceHandler { get; private set; }
|
||||||
#pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.
|
#pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.
|
||||||
@@ -14,7 +13,6 @@ namespace TweetLib.Core{
|
|||||||
|
|
||||||
public sealed class Builder{
|
public sealed class Builder{
|
||||||
public IAppErrorHandler? ErrorHandler { get; set; }
|
public IAppErrorHandler? ErrorHandler { get; set; }
|
||||||
public IAppLockHandler? LockHandler { get; set; }
|
|
||||||
public IAppSystemHandler? SystemHandler { get; set; }
|
public IAppSystemHandler? SystemHandler { get; set; }
|
||||||
public IAppResourceHandler? ResourceHandler { get; set; }
|
public IAppResourceHandler? ResourceHandler { get; set; }
|
||||||
|
|
||||||
@@ -22,7 +20,6 @@ namespace TweetLib.Core{
|
|||||||
|
|
||||||
internal void Initialize(){
|
internal void Initialize(){
|
||||||
App.ErrorHandler = Validate(ErrorHandler, nameof(ErrorHandler))!;
|
App.ErrorHandler = Validate(ErrorHandler, nameof(ErrorHandler))!;
|
||||||
App.LockHandler = Validate(LockHandler, nameof(LockHandler))!;
|
|
||||||
App.SystemHandler = Validate(SystemHandler, nameof(SystemHandler))!;
|
App.SystemHandler = Validate(SystemHandler, nameof(SystemHandler))!;
|
||||||
App.ResourceHandler = Validate(ResourceHandler, nameof(ResourceHandler))!;
|
App.ResourceHandler = Validate(ResourceHandler, nameof(ResourceHandler))!;
|
||||||
}
|
}
|
||||||
|
@@ -1,162 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.IO;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace TweetLib.Core.Application.Helpers{
|
|
||||||
public sealed class LockManager{
|
|
||||||
private const int RetryDelay = 250;
|
|
||||||
|
|
||||||
public enum Result{
|
|
||||||
Success, HasProcess, Fail
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly string file;
|
|
||||||
private FileStream? lockStream;
|
|
||||||
private Process? lockingProcess;
|
|
||||||
|
|
||||||
public LockManager(string file){
|
|
||||||
this.file = file;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lock file
|
|
||||||
|
|
||||||
private bool ReleaseLockFileStream(){
|
|
||||||
if (lockStream != null){
|
|
||||||
lockStream.Dispose();
|
|
||||||
lockStream = null;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Result TryCreateLockFile(){
|
|
||||||
void CreateLockFileStream(){
|
|
||||||
lockStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read);
|
|
||||||
lockStream.Write(BitConverter.GetBytes(CurrentProcessID), 0, sizeof(int));
|
|
||||||
lockStream.Flush(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
try{
|
|
||||||
CreateLockFileStream();
|
|
||||||
return Result.Success;
|
|
||||||
}catch(DirectoryNotFoundException){
|
|
||||||
try{
|
|
||||||
CreateLockFileStream();
|
|
||||||
return Result.Success;
|
|
||||||
}catch{
|
|
||||||
ReleaseLockFileStream();
|
|
||||||
return Result.Fail;
|
|
||||||
}
|
|
||||||
}catch(IOException){
|
|
||||||
return Result.HasProcess;
|
|
||||||
}catch{
|
|
||||||
ReleaseLockFileStream();
|
|
||||||
return Result.Fail;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lock management
|
|
||||||
|
|
||||||
public Result Lock(){
|
|
||||||
if (lockStream != null){
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result initialResult = TryCreateLockFile();
|
|
||||||
|
|
||||||
if (initialResult == Result.HasProcess){
|
|
||||||
try{
|
|
||||||
int pid;
|
|
||||||
|
|
||||||
using(FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)){
|
|
||||||
byte[] bytes = new byte[sizeof(int)];
|
|
||||||
fileStream.Read(bytes, 0, bytes.Length);
|
|
||||||
pid = BitConverter.ToInt32(bytes, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
try{
|
|
||||||
Process foundProcess = Process.GetProcessById(pid);
|
|
||||||
|
|
||||||
if (MatchesCurrentProcess(foundProcess)){
|
|
||||||
lockingProcess = foundProcess;
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
foundProcess.Close();
|
|
||||||
}
|
|
||||||
}catch{
|
|
||||||
// GetProcessById throws ArgumentException if the process is missing
|
|
||||||
// Process.MainModule can throw exceptions in some cases
|
|
||||||
}
|
|
||||||
|
|
||||||
return lockingProcess == null ? Result.Fail : Result.HasProcess;
|
|
||||||
}catch{
|
|
||||||
return Result.Fail;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return initialResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result LockWait(int timeout){
|
|
||||||
for(int elapsed = 0; elapsed < timeout; elapsed += RetryDelay){
|
|
||||||
Result result = Lock();
|
|
||||||
|
|
||||||
if (result == Result.HasProcess){
|
|
||||||
Thread.Sleep(RetryDelay);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Unlock(){
|
|
||||||
if (ReleaseLockFileStream()){
|
|
||||||
try{
|
|
||||||
File.Delete(file);
|
|
||||||
}catch(Exception e){
|
|
||||||
App.ErrorHandler.Log(e.ToString());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Locking process
|
|
||||||
|
|
||||||
public bool RestoreLockingProcess(){
|
|
||||||
return lockingProcess != null && App.LockHandler.RestoreProcess(lockingProcess);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool CloseLockingProcess(){
|
|
||||||
if (lockingProcess != null && App.LockHandler.CloseProcess(lockingProcess)){
|
|
||||||
lockingProcess = null;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Utilities
|
|
||||||
|
|
||||||
private static int CurrentProcessID{
|
|
||||||
get{
|
|
||||||
using Process me = Process.GetCurrentProcess();
|
|
||||||
return me.Id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[SuppressMessage("ReSharper", "PossibleNullReferenceException")]
|
|
||||||
private static bool MatchesCurrentProcess(Process process){
|
|
||||||
using Process current = Process.GetCurrentProcess();
|
|
||||||
return current.MainModule.FileVersionInfo.InternalName == process.MainModule.FileVersionInfo.InternalName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,8 +0,0 @@
|
|||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace TweetLib.Core.Application{
|
|
||||||
public interface IAppLockHandler{
|
|
||||||
bool RestoreProcess(Process process);
|
|
||||||
bool CloseProcess(Process process);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,5 +1,6 @@
|
|||||||
namespace TweetLib.Core.Application{
|
namespace TweetLib.Core.Application{
|
||||||
public interface IAppSystemHandler{
|
public interface IAppSystemHandler{
|
||||||
|
void OpenAssociatedProgram(string path);
|
||||||
void OpenFileExplorer(string path);
|
void OpenFileExplorer(string path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
8
lib/TweetLib.Core/Browser/IResourceProvider.cs
Normal file
8
lib/TweetLib.Core/Browser/IResourceProvider.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using System.Net;
|
||||||
|
|
||||||
|
namespace TweetLib.Core.Browser{
|
||||||
|
public interface IResourceProvider<T>{
|
||||||
|
T Status(HttpStatusCode code, string message);
|
||||||
|
T File(byte[] bytes, string extension);
|
||||||
|
}
|
||||||
|
}
|
@@ -2,7 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using TweetLib.Core.Features.Configuration;
|
using TweetLib.Core.Systems.Configuration;
|
||||||
|
|
||||||
namespace TweetLib.Core.Features.Plugins.Config{
|
namespace TweetLib.Core.Features.Plugins.Config{
|
||||||
public sealed class PluginConfigInstance<T> : IConfigInstance<T> where T : BaseConfig, IPluginConfig{
|
public sealed class PluginConfigInstance<T> : IConfigInstance<T> where T : BaseConfig, IPluginConfig{
|
||||||
|
@@ -47,7 +47,7 @@ namespace TweetLib.Core.Features.Plugins{
|
|||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Plugin? GetPluginFromToken(int token){
|
internal Plugin? GetPluginFromToken(int token){
|
||||||
return tokens.TryGetValue(token, out Plugin plugin) ? plugin : null;
|
return tokens.TryGetValue(token, out Plugin plugin) ? plugin : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -23,7 +23,7 @@ namespace TweetLib.Core.Features.Plugins{
|
|||||||
private readonly string pluginFolder;
|
private readonly string pluginFolder;
|
||||||
private readonly string pluginDataFolder;
|
private readonly string pluginDataFolder;
|
||||||
|
|
||||||
private readonly PluginBridge bridge;
|
internal readonly PluginBridge bridge;
|
||||||
private IScriptExecutor? browserExecutor;
|
private IScriptExecutor? browserExecutor;
|
||||||
|
|
||||||
private readonly HashSet<Plugin> plugins = new HashSet<Plugin>();
|
private readonly HashSet<Plugin> plugins = new HashSet<Plugin>();
|
||||||
|
69
lib/TweetLib.Core/Features/Plugins/PluginSchemeHandler.cs
Normal file
69
lib/TweetLib.Core/Features/Plugins/PluginSchemeHandler.cs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using TweetLib.Core.Browser;
|
||||||
|
using TweetLib.Core.Features.Plugins.Enums;
|
||||||
|
|
||||||
|
namespace TweetLib.Core.Features.Plugins{
|
||||||
|
public sealed class PluginSchemeHandler<T> where T : class{
|
||||||
|
public const string Name = "tdp";
|
||||||
|
|
||||||
|
private readonly IResourceProvider<T> resourceProvider;
|
||||||
|
private PluginBridge? bridge = null;
|
||||||
|
|
||||||
|
public PluginSchemeHandler(IResourceProvider<T> resourceProvider){
|
||||||
|
this.resourceProvider = resourceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Setup(PluginManager plugins){
|
||||||
|
if (this.bridge != null){
|
||||||
|
throw new InvalidOperationException("Plugin scheme handler is already setup.");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.bridge = plugins.bridge;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T? Process(string url){
|
||||||
|
if (!Uri.TryCreate(url, UriKind.Absolute, out var uri) || uri.Scheme != Name || !int.TryParse(uri.Authority, out var identifier)){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var segments = uri.Segments.Select(segment => segment.TrimEnd('/')).Where(segment => !string.IsNullOrEmpty(segment)).ToArray();
|
||||||
|
|
||||||
|
if (segments.Length > 0){
|
||||||
|
var handler = segments[0] switch{
|
||||||
|
"root" => DoReadRootFile(identifier, segments),
|
||||||
|
_ => null
|
||||||
|
};
|
||||||
|
|
||||||
|
if (handler != null){
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourceProvider.Status(HttpStatusCode.BadRequest, "Bad URL path: " + uri.AbsolutePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private T? DoReadRootFile(int identifier, string[] segments){
|
||||||
|
string path = string.Join("/", segments, 1, segments.Length - 1);
|
||||||
|
|
||||||
|
Plugin? plugin = bridge?.GetPluginFromToken(identifier);
|
||||||
|
string fullPath = plugin == null ? string.Empty : plugin.GetFullPathIfSafe(PluginFolder.Root, path);
|
||||||
|
|
||||||
|
if (fullPath.Length == 0){
|
||||||
|
return resourceProvider.Status(HttpStatusCode.Forbidden, "File path has to be relative to the plugin root folder.");
|
||||||
|
}
|
||||||
|
|
||||||
|
try{
|
||||||
|
return resourceProvider.File(File.ReadAllBytes(fullPath), Path.GetExtension(path));
|
||||||
|
}catch(FileNotFoundException){
|
||||||
|
return resourceProvider.Status(HttpStatusCode.NotFound, "File not found.");
|
||||||
|
}catch(DirectoryNotFoundException){
|
||||||
|
return resourceProvider.Status(HttpStatusCode.NotFound, "Directory not found.");
|
||||||
|
}catch(Exception e){
|
||||||
|
return resourceProvider.Status(HttpStatusCode.InternalServerError, e.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace TweetLib.Core.Features.Configuration{
|
namespace TweetLib.Core.Systems.Configuration{
|
||||||
public abstract class BaseConfig{
|
public abstract class BaseConfig{
|
||||||
private readonly IConfigManager configManager;
|
private readonly IConfigManager configManager;
|
||||||
|
|
@@ -2,7 +2,7 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using TweetLib.Core.Serialization;
|
using TweetLib.Core.Serialization;
|
||||||
|
|
||||||
namespace TweetLib.Core.Features.Configuration{
|
namespace TweetLib.Core.Systems.Configuration{
|
||||||
public sealed class FileConfigInstance<T> : IConfigInstance<T> where T : BaseConfig{
|
public sealed class FileConfigInstance<T> : IConfigInstance<T> where T : BaseConfig{
|
||||||
public T Instance { get; }
|
public T Instance { get; }
|
||||||
public FileSerializer<T> Serializer { get; }
|
public FileSerializer<T> Serializer { get; }
|
@@ -1,4 +1,4 @@
|
|||||||
namespace TweetLib.Core.Features.Configuration{
|
namespace TweetLib.Core.Systems.Configuration{
|
||||||
public interface IConfigInstance<out T>{
|
public interface IConfigInstance<out T>{
|
||||||
T Instance { get; }
|
T Instance { get; }
|
||||||
|
|
@@ -1,4 +1,4 @@
|
|||||||
namespace TweetLib.Core.Features.Configuration{
|
namespace TweetLib.Core.Systems.Configuration{
|
||||||
public interface IConfigManager{
|
public interface IConfigManager{
|
||||||
void TriggerProgramRestartRequested();
|
void TriggerProgramRestartRequested();
|
||||||
IConfigInstance<BaseConfig> GetInstanceInfo(BaseConfig instance);
|
IConfigInstance<BaseConfig> GetInstanceInfo(BaseConfig instance);
|
125
lib/TweetLib.Core/Systems/Startup/LockFile.cs
Normal file
125
lib/TweetLib.Core/Systems/Startup/LockFile.cs
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
using TweetLib.Core.Utils;
|
||||||
|
|
||||||
|
namespace TweetLib.Core.Systems.Startup{
|
||||||
|
public sealed class LockFile{
|
||||||
|
private static int CurrentProcessID{
|
||||||
|
get{
|
||||||
|
using Process me = Process.GetCurrentProcess();
|
||||||
|
return me.Id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly string path;
|
||||||
|
private FileStream? stream;
|
||||||
|
|
||||||
|
public LockFile(string path){
|
||||||
|
this.path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateLockFileStream(){
|
||||||
|
stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read);
|
||||||
|
stream.Write(BitConverter.GetBytes(CurrentProcessID), 0, sizeof(int));
|
||||||
|
stream.Flush(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ReleaseLockFileStream(){
|
||||||
|
if (stream != null){
|
||||||
|
stream.Dispose();
|
||||||
|
stream = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public LockResult Lock(){
|
||||||
|
if (stream != null){
|
||||||
|
return LockResult.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
try{
|
||||||
|
CreateLockFileStream();
|
||||||
|
return LockResult.Success;
|
||||||
|
}catch(DirectoryNotFoundException){
|
||||||
|
try{
|
||||||
|
FileUtils.CreateDirectoryForFile(path);
|
||||||
|
CreateLockFileStream();
|
||||||
|
return LockResult.Success;
|
||||||
|
}catch(Exception e){
|
||||||
|
ReleaseLockFileStream();
|
||||||
|
return new LockResult.Fail(e);
|
||||||
|
}
|
||||||
|
}catch(IOException e){
|
||||||
|
return DetermineLockingProcessOrFail(e);
|
||||||
|
}catch(Exception e){
|
||||||
|
ReleaseLockFileStream();
|
||||||
|
return new LockResult.Fail(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public LockResult LockWait(int timeout, int retryDelay){
|
||||||
|
for(int elapsed = 0; elapsed < timeout; elapsed += retryDelay){
|
||||||
|
var result = Lock();
|
||||||
|
|
||||||
|
if (result is LockResult.HasProcess info){
|
||||||
|
info.Dispose();
|
||||||
|
Thread.Sleep(retryDelay);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Unlock(){
|
||||||
|
if (ReleaseLockFileStream()){
|
||||||
|
try{
|
||||||
|
File.Delete(path);
|
||||||
|
}catch(Exception e){
|
||||||
|
App.ErrorHandler.Log(e.ToString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private LockResult DetermineLockingProcessOrFail(Exception originalException){
|
||||||
|
try{
|
||||||
|
int pid;
|
||||||
|
|
||||||
|
using(var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)){
|
||||||
|
byte[] bytes = new byte[sizeof(int)];
|
||||||
|
fileStream.Read(bytes, 0, bytes.Length);
|
||||||
|
pid = BitConverter.ToInt32(bytes, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
try{
|
||||||
|
var foundProcess = Process.GetProcessById(pid);
|
||||||
|
using var currentProcess = Process.GetCurrentProcess();
|
||||||
|
|
||||||
|
if (currentProcess.MainModule.FileVersionInfo.InternalName == foundProcess.MainModule.FileVersionInfo.InternalName){
|
||||||
|
return new LockResult.HasProcess(foundProcess);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
foundProcess.Close();
|
||||||
|
}
|
||||||
|
}catch{
|
||||||
|
// GetProcessById throws ArgumentException if the process is missing
|
||||||
|
// Process.MainModule can throw exceptions in some cases
|
||||||
|
}
|
||||||
|
|
||||||
|
return new LockResult.Fail(originalException);
|
||||||
|
}catch(Exception e){
|
||||||
|
return new LockResult.Fail(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
38
lib/TweetLib.Core/Systems/Startup/LockResult.cs
Normal file
38
lib/TweetLib.Core/Systems/Startup/LockResult.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace TweetLib.Core.Systems.Startup{
|
||||||
|
public class LockResult{
|
||||||
|
private readonly string name;
|
||||||
|
|
||||||
|
private LockResult(string name){
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString(){
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LockResult Success { get; } = new LockResult("Success");
|
||||||
|
|
||||||
|
public sealed class Fail : LockResult{
|
||||||
|
public Exception Exception { get; }
|
||||||
|
|
||||||
|
public Fail(Exception exception) : base("Fail"){
|
||||||
|
this.Exception = exception;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class HasProcess : LockResult, IDisposable{
|
||||||
|
public Process Process { get; }
|
||||||
|
|
||||||
|
public HasProcess(Process process) : base("HasProcess"){
|
||||||
|
this.Process = process;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose(){
|
||||||
|
Process.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +1,6 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace TweetLib.Core.Features.Updates{
|
namespace TweetLib.Core.Systems.Updates{
|
||||||
public interface IUpdateCheckClient{
|
public interface IUpdateCheckClient{
|
||||||
bool CanCheck { get; }
|
bool CanCheck { get; }
|
||||||
Task<UpdateInfo> Check();
|
Task<UpdateInfo> Check();
|
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using TweetLib.Core.Data;
|
using TweetLib.Core.Data;
|
||||||
|
|
||||||
namespace TweetLib.Core.Features.Updates{
|
namespace TweetLib.Core.Systems.Updates{
|
||||||
public sealed class UpdateCheckEventArgs : EventArgs{
|
public sealed class UpdateCheckEventArgs : EventArgs{
|
||||||
public int EventId { get; }
|
public int EventId { get; }
|
||||||
public Result<UpdateInfo> Result { get; }
|
public Result<UpdateInfo> Result { get; }
|
@@ -1,4 +1,4 @@
|
|||||||
namespace TweetLib.Core.Features.Updates{
|
namespace TweetLib.Core.Systems.Updates{
|
||||||
public enum UpdateDownloadStatus{
|
public enum UpdateDownloadStatus{
|
||||||
None = 0,
|
None = 0,
|
||||||
InProgress,
|
InProgress,
|
@@ -5,7 +5,7 @@ using System.Timers;
|
|||||||
using TweetLib.Core.Data;
|
using TweetLib.Core.Data;
|
||||||
using Timer = System.Timers.Timer;
|
using Timer = System.Timers.Timer;
|
||||||
|
|
||||||
namespace TweetLib.Core.Features.Updates{
|
namespace TweetLib.Core.Systems.Updates{
|
||||||
public sealed class UpdateHandler : IDisposable{
|
public sealed class UpdateHandler : IDisposable{
|
||||||
public const int CheckCodeUpdatesDisabled = -1;
|
public const int CheckCodeUpdatesDisabled = -1;
|
||||||
|
|
@@ -3,7 +3,7 @@ using System.IO;
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using TweetLib.Core.Utils;
|
using TweetLib.Core.Utils;
|
||||||
|
|
||||||
namespace TweetLib.Core.Features.Updates{
|
namespace TweetLib.Core.Systems.Updates{
|
||||||
public sealed class UpdateInfo{
|
public sealed class UpdateInfo{
|
||||||
public string VersionTag { get; }
|
public string VersionTag { get; }
|
||||||
public string ReleaseNotes { get; }
|
public string ReleaseNotes { get; }
|
@@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="cef.redist.x64" version="3.3396.1786" targetFramework="net452" />
|
<package id="cef.redist.x64" version="86.0.24" targetFramework="net472" />
|
||||||
<package id="cef.redist.x86" version="3.3396.1786" targetFramework="net452" />
|
<package id="cef.redist.x86" version="86.0.24" targetFramework="net472" />
|
||||||
<package id="CefSharp.Common" version="67.0.0" targetFramework="net452" />
|
<package id="CefSharp.Common" version="86.0.241" targetFramework="net472" />
|
||||||
<package id="CefSharp.WinForms" version="67.0.0" targetFramework="net452" />
|
<package id="CefSharp.WinForms" version="86.0.241" targetFramework="net472" />
|
||||||
<package id="Microsoft.Net.Compilers" version="3.0.0" targetFramework="net472" developmentDependency="true" />
|
<package id="Microsoft.Net.Compilers" version="3.0.0" targetFramework="net472" developmentDependency="true" />
|
||||||
</packages>
|
</packages>
|
@@ -22,11 +22,11 @@ namespace TweetDuck.Browser{
|
|||||||
Task.Factory.StartNew(() => KillWhenHung(parentId), TaskCreationOptions.LongRunning);
|
Task.Factory.StartNew(() => KillWhenHung(parentId), TaskCreationOptions.LongRunning);
|
||||||
|
|
||||||
if (FindArg(typePrefix) == "renderer"){
|
if (FindArg(typePrefix) == "renderer"){
|
||||||
using SubProcess subProcess = new SubProcess(args);
|
using SubProcess subProcess = new SubProcess(null, args);
|
||||||
return subProcess.Run();
|
return subProcess.Run();
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
return SubProcess.ExecuteProcess();
|
return SubProcess.ExecuteProcess(args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<Import Project="..\packages\Microsoft.Net.Compilers.3.0.0\build\Microsoft.Net.Compilers.props" Condition="Exists('..\packages\Microsoft.Net.Compilers.3.0.0\build\Microsoft.Net.Compilers.props')" />
|
<Import Project="..\packages\Microsoft.Net.Compilers.3.0.0\build\Microsoft.Net.Compilers.props" Condition="Exists('..\packages\Microsoft.Net.Compilers.3.0.0\build\Microsoft.Net.Compilers.props')" />
|
||||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
@@ -29,9 +29,13 @@
|
|||||||
<StartupObject />
|
<StartupObject />
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="CefSharp.BrowserSubprocess.Core, Version=67.0.0.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=x86">
|
<Reference Include="CefSharp, Version=86.0.0.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=x86">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<HintPath>..\packages\CefSharp.Common.67.0.0\CefSharp\x86\CefSharp.BrowserSubprocess.Core.dll</HintPath>
|
<HintPath>..\packages\CefSharp.Common.86.0.241\CefSharp\x86\CefSharp.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="CefSharp.BrowserSubprocess.Core, Version=86.0.0.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=x86">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\packages\CefSharp.Common.86.0.241\CefSharp\x86\CefSharp.BrowserSubprocess.Core.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
Reference in New Issue
Block a user