mirror of
https://github.com/chylex/TweetDuck.git
synced 2025-05-04 17:34:07 +02:00
Merge remote-tracking branch 'refs/remotes/origin/master' into cefsharp_67
This commit is contained in:
commit
cacc3aa24c
Plugins
Properties
Resources
TweetDuck.csprojTweetDuck.slnlib
tests/Data
@ -26,9 +26,8 @@ public static bool IncludesDisabledPlugins(this PluginEnvironment environment){
|
||||
|
||||
public static string GetScriptIdentifier(this PluginEnvironment environment){
|
||||
switch(environment){
|
||||
case PluginEnvironment.None: return "root:plugins";
|
||||
case PluginEnvironment.Browser: return "root:plugins.browser";
|
||||
case PluginEnvironment.Notification: return "root:plugins.notification";
|
||||
case PluginEnvironment.Browser: return "root:plugins:browser";
|
||||
case PluginEnvironment.Notification: return "root:plugins:notification";
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ namespace TweetDuck.Plugins{
|
||||
sealed class PluginManager{
|
||||
private static IReadOnlyDictionary<PluginEnvironment, string> LoadSetupScripts(){
|
||||
return PluginEnvironmentExtensions.Map(
|
||||
ScriptLoader.LoadResource("plugins.js"),
|
||||
null,
|
||||
ScriptLoader.LoadResource("plugins.browser.js"),
|
||||
ScriptLoader.LoadResource("plugins.notification.js")
|
||||
);
|
||||
@ -158,7 +158,6 @@ private void ExecutePlugins(IFrame frame, PluginEnvironment environment){
|
||||
}
|
||||
|
||||
ScriptLoader.ExecuteScript(frame, PluginSetupScripts[environment], environment.GetScriptIdentifier());
|
||||
ScriptLoader.ExecuteScript(frame, PluginSetupScripts[PluginEnvironment.None], PluginEnvironment.None.GetScriptIdentifier());
|
||||
|
||||
bool includeDisabled = environment.IncludesDisabledPlugins();
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
using System.Runtime.InteropServices;
|
||||
@ -42,6 +42,6 @@
|
||||
[assembly: CLSCompliant(true)]
|
||||
|
||||
#if DEBUG
|
||||
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("TweetTest.System")]
|
||||
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("TweetTest.Unit")]
|
||||
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("UnitTests")]
|
||||
#endif
|
||||
|
@ -32,6 +32,16 @@ enabled(){
|
||||
});
|
||||
};
|
||||
|
||||
// setup
|
||||
|
||||
this.htmlModal = null;
|
||||
|
||||
$TDP.readFileRoot(this.$token, "modal.html").then(contents => {
|
||||
this.htmlModal = contents;
|
||||
}).catch(err => {
|
||||
$TD.alert("error", "Problem loading data for the template plugin: "+err.message);
|
||||
});
|
||||
|
||||
// button
|
||||
|
||||
var buttonHTML = '<button class="manage-templates-btn needsclick btn btn-on-blue full-width txt-left margin-b--12 padding-v--6 padding-h--12"><i class="icon icon-bookmark"></i><span class="label padding-ls">Manage templates</span></button>';
|
||||
@ -45,46 +55,6 @@ enabled(){
|
||||
dockedComposePanel.find(".js-tweet-type-button").first().before(buttonHTML);
|
||||
}
|
||||
|
||||
// css
|
||||
|
||||
this.css = window.TDPF_createCustomStyle(this);
|
||||
this.css.insert(".manage-templates-btn.active { color: #fff; box-shadow: 0 0 2px 3px #50a5e6; outline: 0; }");
|
||||
|
||||
this.css.insert("#templates-modal-wrap { width: 100%; height: 100%; padding: 49px; position: absolute; z-index: 999; box-sizing: border-box; background-color: rgba(0, 0, 0, 0.5); }");
|
||||
this.css.insert("#templates-modal { width: 100%; height: 100%; min-width: 500px; background-color: #fff; display: flex; }");
|
||||
this.css.insert("#templates-modal > div { display: flex; flex-direction: column; }");
|
||||
|
||||
this.css.insert(".templates-modal-bottom { flex: 0 0 auto; padding: 16px; }");
|
||||
this.css.insert("#template-list .templates-modal-bottom { display: flex; justify-content: space-between; }");
|
||||
this.css.insert("#template-editor .templates-modal-bottom { text-align: right; }");
|
||||
|
||||
this.css.insert("#template-list { height: 100%; flex: 1 1 auto; }");
|
||||
this.css.insert("#template-list ul { list-style-type: none; font-size: 24px; color: #222; flex: 1 1 auto; padding: 12px; overflow-y: auto; }");
|
||||
this.css.insert("#template-list li { display: block; width: 100%; padding: 4px 8px; box-sizing: border-box; }");
|
||||
this.css.insert("#template-list li[data-template] { cursor: pointer; }");
|
||||
this.css.insert("#template-list li[data-template]:hover { background-color: #d8d8d8; }");
|
||||
this.css.insert("#template-list li span { white-space: nowrap; }");
|
||||
this.css.insert("#template-list li .icon { opacity: 0.6; margin-left: 4px; padding: 3px; }");
|
||||
this.css.insert("#template-list li .icon:hover { opacity: 1; }");
|
||||
this.css.insert("#template-list li .template-actions { float: right; }");
|
||||
|
||||
this.css.insert("#template-editor { height: 100%; flex: 0 0 auto; width: 25vw; min-width: 225px; max-width: 400px; background-color: #485865; }");
|
||||
this.css.insert(".template-editor-form { flex: 1 1 auto; padding: 12px 16px; font-size: 14px; overflow-y: auto; }");
|
||||
this.css.insert(".template-editor-form .compose-text-title { margin: 24px 0 9px; }");
|
||||
this.css.insert(".template-editor-form .compose-text-title:first-child { margin-top: 0; }");
|
||||
this.css.insert(".template-editor-form input, .template-editor-form textarea { color: #111; background-color: #fff; border: none; border-radius: 0; }");
|
||||
this.css.insert(".template-editor-form input:focus, .template-editor-form textarea:focus { box-shadow: inset 0 1px 3px rgba(17, 17, 17, 0.1), 0 0 8px rgba(80, 165, 230, 0.6); }");
|
||||
this.css.insert(".template-editor-form textarea { height: 146px; font-size: 14px; padding: 10px; resize: none; }");
|
||||
this.css.insert(".template-editor-form .template-editor-tips-button { cursor: pointer; }");
|
||||
this.css.insert(".template-editor-form .template-editor-tips-button .icon { font-size: 12px; vertical-align: -5%; margin-left: 4px; }");
|
||||
this.css.insert(".template-editor-form .template-editor-tips { display: none; }");
|
||||
this.css.insert(".template-editor-form .template-editor-tips p { margin: 10px 0; }");
|
||||
this.css.insert(".template-editor-form .template-editor-tips p:first-child { margin-top: 0; }");
|
||||
this.css.insert(".template-editor-form .template-editor-tips li:nth-child(2n+1) { margin-top: 5px; padding-left: 6px; font-family: monospace; }");
|
||||
this.css.insert(".template-editor-form .template-editor-tips li:nth-child(2n) { margin-top: 1px; padding-left: 14px; opacity: 0.66; }");
|
||||
|
||||
this.css.insert(".invisible { display: none !important; }");
|
||||
|
||||
// template implementation
|
||||
|
||||
var readTemplateTokens = (contents, tokenData) => {
|
||||
@ -258,67 +228,18 @@ enabled(){
|
||||
this.editingTemplate = null;
|
||||
|
||||
var showTemplateModal = () => {
|
||||
$(".manage-templates-btn").addClass("active");
|
||||
$(".js-app-content").prepend(this.htmlModal);
|
||||
|
||||
let html = `
|
||||
<div id="templates-modal-wrap" class="scroll-v scroll-styled-v">
|
||||
<div id="templates-modal">
|
||||
<div id="template-list">
|
||||
<ul></ul>
|
||||
|
||||
<div class="templates-modal-bottom">
|
||||
<button data-action="close" class="Button--secondary"><i class="icon icon-close icon-small padding-rs"></i><span class="label">Close</span></button>
|
||||
<button data-action="new-template" class="Button--primary"><i class="icon icon-plus icon-small padding-rs"></i><span class="label">New Template</span></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="template-editor" class="invisible">
|
||||
<div class="template-editor-form">
|
||||
<div class="compose-text-title">Template Name</div>
|
||||
<input name="template-name" type="text">
|
||||
|
||||
<div class="compose-text-title">Contents</div>
|
||||
<textarea name="template-contents" class="compose-text scroll-v scroll-styled-v scroll-styled-h scroll-alt"></textarea>
|
||||
|
||||
<div class="compose-text-title template-editor-tips-button">Advanced <i class="icon icon-arrow-d"></i></div>
|
||||
<div class="template-editor-tips">
|
||||
<p>You can use the following tokens. All tokens except for <span style="font-family: monospace">{ajax}</span> can only be used once.</p>
|
||||
<ul>
|
||||
<li>{cursor}</li>
|
||||
<li>Location where the cursor is placed</li>
|
||||
<li>{cursor#<selectionlength>}</li>
|
||||
<li>Places cursor and selects a set amount of characters</li>
|
||||
<li>{ajax#<url>}</li>
|
||||
<li>Replaced with the result of a cross-origin ajax request</li>
|
||||
<li>{ajax#<eval>#<url>}</li>
|
||||
<li>Allows parsing the ajax request using <span style="font-family: monospace">$</span> as the placeholder for the result<br>Example: <span style="font-family: monospace">$.substring(0,5)</span></li>
|
||||
</ul>
|
||||
<p>To use special characters in the tweet text, escape them with a backslash:
|
||||
<br><span style="font-family: monospace"> \\{ \\} \\# \\\\</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="templates-modal-bottom">
|
||||
<button data-action="editor-cancel" class="Button--secondary"><i class="icon icon-close icon-small padding-rs"></i><span class="label">Cancel</span></button>
|
||||
<button data-action="editor-confirm" class="Button--primary" style="margin-left:4px"><i class="icon icon-check icon-small padding-rs"></i><span class="label">Confirm</span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
/* TODO possibly implement this later
|
||||
|
||||
/* TODO possibly implement this later
|
||||
|
||||
<li>{paste}</li>
|
||||
<li>Paste text or an image from clipboard</li>
|
||||
<li>{paste#text}</li>
|
||||
<li>Paste only if clipboard has text</li>
|
||||
<li>{paste#image}</li>
|
||||
<li>Paste only if clipboard has an image</li>
|
||||
|
||||
*/
|
||||
<li>{paste}</li>
|
||||
<li>Paste text or an image from clipboard</li>
|
||||
<li>{paste#text}</li>
|
||||
<li>Paste only if clipboard has text</li>
|
||||
<li>{paste#image}</li>
|
||||
<li>Paste only if clipboard has an image</li>
|
||||
|
||||
$(".js-app-content").prepend(html);
|
||||
*/
|
||||
|
||||
let ele = $("#templates-modal-wrap").first();
|
||||
|
||||
@ -409,7 +330,6 @@ enabled(){
|
||||
|
||||
var hideTemplateModal = function(){
|
||||
$("#templates-modal-wrap").remove();
|
||||
$(".manage-templates-btn").removeClass("active");
|
||||
};
|
||||
|
||||
var toggleEditor = function(){
|
||||
@ -468,8 +388,6 @@ ready(){
|
||||
}
|
||||
|
||||
disabled(){
|
||||
this.css.remove();
|
||||
|
||||
$(".manage-templates-btn").remove();
|
||||
$("#templates-modal-wrap").remove();
|
||||
|
||||
|
227
Resources/Plugins/templates/modal.html
Normal file
227
Resources/Plugins/templates/modal.html
Normal file
@ -0,0 +1,227 @@
|
||||
<div id="templates-modal-wrap" class="scroll-v scroll-styled-v">
|
||||
<div id="templates-modal">
|
||||
<div id="template-list">
|
||||
<ul></ul>
|
||||
|
||||
<div class="templates-modal-bottom">
|
||||
<button data-action="close" class="Button--secondary"><i class="icon icon-close icon-small padding-rs"></i><span class="label">Close</span></button>
|
||||
<button data-action="new-template" class="Button--primary"><i class="icon icon-plus icon-small padding-rs"></i><span class="label">New Template</span></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="template-editor" class="invisible">
|
||||
<div class="template-editor-form">
|
||||
<div class="compose-text-title">Template Name</div>
|
||||
<input name="template-name" type="text">
|
||||
|
||||
<div class="compose-text-title">Contents</div>
|
||||
<textarea name="template-contents" class="compose-text scroll-v scroll-styled-v scroll-styled-h scroll-alt"></textarea>
|
||||
|
||||
<div class="compose-text-title template-editor-tips-button">Advanced <i class="icon icon-arrow-d"></i></div>
|
||||
<div class="template-editor-tips">
|
||||
<p>You can use the following tokens. All tokens except for <span style="font-family: monospace">{ajax}</span> can only be used once.</p>
|
||||
<ul>
|
||||
<li>{cursor}</li>
|
||||
<li>Location where the cursor is placed</li>
|
||||
<li>{cursor#<selectionlength>}</li>
|
||||
<li>Places cursor and selects a set amount of characters</li>
|
||||
<li>{ajax#<url>}</li>
|
||||
<li>Replaced with the result of a cross-origin ajax request</li>
|
||||
<li>{ajax#<eval>#<url>}</li>
|
||||
<li>Allows parsing the ajax request using <span style="font-family: monospace">$</span> as the placeholder for the result<br>Example: <span style="font-family: monospace">$.substring(0,5)</span></li>
|
||||
</ul>
|
||||
<p>To use special characters in the tweet text, escape them with a backslash:
|
||||
<br><span style="font-family: monospace"> \{ \} \# \\</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="templates-modal-bottom">
|
||||
<button data-action="editor-cancel" class="Button--secondary"><i class="icon icon-close icon-small padding-rs"></i><span class="label">Cancel</span></button>
|
||||
<button data-action="editor-confirm" class="Button--primary" style="margin-left:4px"><i class="icon icon-check icon-small padding-rs"></i><span class="label">Confirm</span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style type="text/css">
|
||||
|
||||
/* General */
|
||||
|
||||
#templates-modal-wrap {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 49px;
|
||||
position: absolute;
|
||||
z-index: 999;
|
||||
box-sizing: border-box;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
#templates-modal {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-width: 500px;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#templates-modal > div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.templates-modal-bottom {
|
||||
flex: 0 0 auto;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
#template-list .templates-modal-bottom {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
#template-editor .templates-modal-bottom {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.manage-templates-btn {
|
||||
/* modifies the Manage Templates button while open */
|
||||
color: #fff;
|
||||
box-shadow: 0 0 2px 3px #50a5e6;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
/* Template list */
|
||||
|
||||
#template-list {
|
||||
height: 100%;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
#template-list ul {
|
||||
list-style-type: none;
|
||||
font-size: 24px;
|
||||
color: #222;
|
||||
flex: 1 1 auto;
|
||||
padding: 12px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#template-list li {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 4px 8px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#template-list li[data-template] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#template-list li[data-template]:hover {
|
||||
background-color: #d8d8d8;
|
||||
}
|
||||
|
||||
#template-list li span {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#template-list li .icon {
|
||||
opacity: 0.6;
|
||||
margin-left: 4px;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
#template-list li .icon:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#template-list li .template-actions {
|
||||
float: right;
|
||||
}
|
||||
|
||||
/* Template editor */
|
||||
|
||||
#template-editor {
|
||||
height: 100%;
|
||||
flex: 0 0 auto;
|
||||
width: 25vw;
|
||||
min-width: 225px;
|
||||
max-width: 400px;
|
||||
background-color: #485865;
|
||||
}
|
||||
|
||||
#template-editor.invisible {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.template-editor-form {
|
||||
flex: 1 1 auto;
|
||||
padding: 12px 16px;
|
||||
font-size: 14px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.template-editor-form .compose-text-title {
|
||||
margin: 24px 0 9px;
|
||||
}
|
||||
|
||||
.template-editor-form .compose-text-title:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.template-editor-form input, .template-editor-form textarea {
|
||||
color: #111;
|
||||
background-color: #fff;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.template-editor-form input:focus, .template-editor-form textarea:focus {
|
||||
box-shadow: inset 0 1px 3px rgba(17, 17, 17, 0.1), 0 0 8px rgba(80, 165, 230, 0.6);
|
||||
}
|
||||
|
||||
.template-editor-form textarea {
|
||||
height: 146px;
|
||||
font-size: 14px;
|
||||
padding: 10px;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
/* Template tips */
|
||||
|
||||
.template-editor-form .template-editor-tips-button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.template-editor-form .template-editor-tips-button .icon {
|
||||
font-size: 12px;
|
||||
vertical-align: -5%;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.template-editor-form .template-editor-tips {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.template-editor-form .template-editor-tips p {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.template-editor-form .template-editor-tips p:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.template-editor-form .template-editor-tips li:nth-child(2n+1) {
|
||||
margin-top: 5px;
|
||||
padding-left: 6px;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.template-editor-form .template-editor-tips li:nth-child(2n) {
|
||||
margin-top: 1px;
|
||||
padding-left: 14px;
|
||||
opacity: 0.66;
|
||||
}
|
||||
</style>
|
||||
</div>
|
@ -46,7 +46,7 @@ try{
|
||||
function Remove-Empty-Lines{
|
||||
Param([Parameter(Mandatory = $True, Position = 1)] $lines)
|
||||
|
||||
ForEach($line in $lines){
|
||||
foreach($line in $lines){
|
||||
if ($line -ne ''){
|
||||
$line
|
||||
}
|
||||
@ -69,11 +69,19 @@ try{
|
||||
|
||||
function Rewrite-File{
|
||||
Param([Parameter(Mandatory = $True, Position = 1)] $file,
|
||||
[Parameter(Mandatory = $True, Position = 2)] $lines)
|
||||
[Parameter(Mandatory = $True, Position = 2)] $lines,
|
||||
[Parameter(Mandatory = $True, Position = 3)] $imports)
|
||||
|
||||
$lines = Remove-Empty-Lines($lines)
|
||||
$relativePath = $file.FullName.Substring($targetDir.Length)
|
||||
|
||||
foreach($line in $lines){
|
||||
if ($line.Contains('#import ')){
|
||||
$imports.Add($file.FullName)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if ($relativePath.StartsWith("scripts\")){
|
||||
$lines = (,("#" + $version) + $lines)
|
||||
}
|
||||
@ -82,39 +90,66 @@ try{
|
||||
Write-Host "Processed" $relativePath
|
||||
}
|
||||
|
||||
# Post processing
|
||||
# Validation
|
||||
|
||||
Check-Carriage-Return(Join-Path $targetDir "plugins\official\emoji-keyboard\emoji-ordering.txt")
|
||||
|
||||
ForEach($file in Get-ChildItem -Path $targetDir -Filter "*.js" -Exclude "configuration.default.js" -Recurse){
|
||||
# Processing
|
||||
|
||||
$imports = New-Object "System.Collections.Generic.List[string]"
|
||||
|
||||
foreach($file in Get-ChildItem -Path $targetDir -Filter "*.js" -Exclude "configuration.default.js" -Recurse){
|
||||
$lines = [IO.File]::ReadLines($file.FullName)
|
||||
$lines = $lines | % { $_.TrimStart() }
|
||||
$lines = $lines -Replace '^(.*?)((?<=^|[;{}()])\s?//(?:\s.*|$))?$', '$1'
|
||||
$lines = $lines -Replace '(?<!\w)return(\s.*?)? if (.*?);', 'if ($2)return$1;'
|
||||
Rewrite-File $file $lines
|
||||
Rewrite-File $file $lines $imports
|
||||
}
|
||||
|
||||
ForEach($file in Get-ChildItem -Path $targetDir -Filter "*.css" -Recurse){
|
||||
foreach($file in Get-ChildItem -Path $targetDir -Filter "*.css" -Recurse){
|
||||
$lines = [IO.File]::ReadLines($file.FullName)
|
||||
$lines = $lines -Replace '\s*/\*.*?\*/', ''
|
||||
$lines = $lines -Replace '^(\S.*) {$', '$1{'
|
||||
$lines = $lines -Replace '^\s+(.+?):\s*(.+?)(?:\s*(!important))?;$', '$1:$2$3;'
|
||||
$lines = @((Remove-Empty-Lines($lines)) -Join ' ')
|
||||
Rewrite-File $file $lines
|
||||
Rewrite-File $file $lines $imports
|
||||
}
|
||||
|
||||
ForEach($file in Get-ChildItem -Path $targetDir -Filter "*.html" -Recurse){
|
||||
foreach($file in Get-ChildItem -Path $targetDir -Filter "*.html" -Recurse){
|
||||
$lines = [IO.File]::ReadLines($file.FullName)
|
||||
$lines = $lines | % { $_.TrimStart() }
|
||||
Rewrite-File $file $lines
|
||||
Rewrite-File $file $lines $imports
|
||||
}
|
||||
|
||||
ForEach($file in Get-ChildItem -Path (Join-Path $targetDir "plugins") -Filter "*.meta" -Recurse){
|
||||
foreach($file in Get-ChildItem -Path (Join-Path $targetDir "plugins") -Filter "*.meta" -Recurse){
|
||||
$lines = [IO.File]::ReadLines($file.FullName)
|
||||
$lines = $lines -Replace '\{version\}', $version
|
||||
Rewrite-File $file $lines
|
||||
Rewrite-File $file $lines $imports
|
||||
}
|
||||
|
||||
# Imports
|
||||
|
||||
$importFolder = Join-Path $targetDir "scripts\imports"
|
||||
|
||||
foreach($path in $imports){
|
||||
$text = [IO.File]::ReadAllText($path)
|
||||
$text = [Regex]::Replace($text, '#import "(.*)"', {
|
||||
$importPath = Join-Path $importFolder ($args[0].Groups[1].Value.Trim())
|
||||
$importStr = [IO.File]::ReadAllText($importPath).TrimEnd()
|
||||
|
||||
if ($importStr[0] -eq '#'){
|
||||
$importStr = $importStr.Substring($importStr.IndexOf("`n") + 1)
|
||||
}
|
||||
|
||||
return $importStr
|
||||
}, [System.Text.RegularExpressions.RegexOptions]::MultiLine)
|
||||
|
||||
[IO.File]::WriteAllText($path, $text)
|
||||
Write-Host "Resolved" $path.Substring($targetDir.Length)
|
||||
}
|
||||
|
||||
[IO.Directory]::Delete($importFolder, $True)
|
||||
|
||||
# Finished
|
||||
|
||||
$sw.Stop()
|
||||
|
@ -5,23 +5,23 @@
|
||||
window.TDPF_loadConfigurationFile = function(pluginObject, fileNameUser, fileNameDefault, onSuccess, onFailure){
|
||||
var identifier = pluginObject.$id;
|
||||
var token = pluginObject.$token;
|
||||
|
||||
|
||||
$TDP.checkFileExists(token, fileNameUser).then(exists => {
|
||||
var fileName = exists ? fileNameUser : fileNameDefault;
|
||||
|
||||
(exists ? $TDP.readFile(token, fileName, true) : $TDP.readFileRoot(token, fileName)).then(contents => {
|
||||
var obj;
|
||||
|
||||
|
||||
try{
|
||||
obj = eval("("+contents+")");
|
||||
}catch(err){
|
||||
if (!(onFailure && onFailure(err))){
|
||||
$TD.alert("warning", "Problem loading '"+fileName+"' file for '"+identifier+"' plugin, the JavaScript syntax is invalid: "+err.message);
|
||||
}
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
onSuccess && onSuccess(obj);
|
||||
}).catch(err => {
|
||||
if (!(onFailure && onFailure(err))){
|
69
Resources/Scripts/imports/styles/introduction.css
Normal file
69
Resources/Scripts/imports/styles/introduction.css
Normal file
@ -0,0 +1,69 @@
|
||||
#td-introduction-modal {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#td-introduction-modal .mdl {
|
||||
width: 90%;
|
||||
min-width: 515px;
|
||||
max-width: 835px;
|
||||
height: 328px;
|
||||
}
|
||||
|
||||
#td-introduction-modal .mdl-inner {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
#td-introduction-modal .mdl-header-title {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
#td-introduction-modal .mdl-content {
|
||||
padding: 4px 16px 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#td-introduction-modal p {
|
||||
margin: 12px 0;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
#td-introduction-modal p strong {
|
||||
font-weight: normal;
|
||||
text-shadow: 0 0 #000;
|
||||
}
|
||||
|
||||
#td-introduction-modal .main-menu {
|
||||
float: left;
|
||||
width: 187px;
|
||||
height: 124px;
|
||||
margin-right: 12px;
|
||||
box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.33);
|
||||
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALsAAAB8CAMAAAAGozFUAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACZUExURQAAAAAANgAAYQA2YQA2iABhYQBhrDYAADYANjYAYTY2ADY2NjY2YTY2iDZhrDaIiDaIz2EAAGEANmEAYWE2NmE2iGFhNmFhYWGs8og2AIg2Nog2YYiIYYisiIjPrIjPz4jP8qxhAKxhNqzPiKzyrKzy8szMzM+INs/PiM/yrM/yz8/y8tfX1/Dw8PKsYfLPiPLyrPLyz/Ly8nG9GIQAAARDSURBVHja7ZyPd9IwEMdDxbI5cemcWJjbWrWrDi2Q//+PM7m7pOGHfcORlD6v7w1ok2wfwqXkvrs78TTcQzypoR6DZ//dcTA7szM7s8dgX2dCiJxGrG+Kvd+C1w619M+uoZqkGiy7AxvivI+XajURSWVO9AtjQ6UQqTIn18R+PxFSP82SCnuUOYxdfdjMzSmNn9mPMJa9p/QWpH5cZ5rjgqbanNQjOMnGS315nWl+7NGkqrmSqpb6hRuvm+PO++qygtkWKZ2YOW2EGBWr6dK3mTKHmcYeuu3r/fR5UawmUrXjY9tMLRFItez6abPYYd/MffbN4uHT89033UPVInfje1ir+Glbm7msGjKR1mbg/UFv7KFqYzGfwUhq6cbHZle1Jp3QZ65fjMwcize3hbEcu1ZnZkVCb+wBq8L86D4JGl1sm+E9AbMzO7MzO7MzO7Mze586QQqdcav7ggO3juMfF8Whlsj+6hXuzx17x0aWmswO/nBLbPaZ8RqatzdHsO/36In94WOltAOnXSZwoR7A+Qa3H5q/CEH+aA5+OfaQ6ASCYGAuZq3nAi4KjML2oH6T9qxX05+W3T5pt5+WQ5NUpA1s99jMpZt05zpiPxzVpOF1gukSFACPDN3+1p0l/3qbHf1rVBQO9gMFISz7ZnE/Xe6wX3oqXzc7KgoH+4GCEFoXA8VL20CuGprCTG6xkzZwyGZIUTjUDxSEwOybu4regzC3mxLXqm8zVhsoYUXai2aF5qQolL7KQB1QQeDvVWZndmZndmZn9nNn/3WuB9sMszM7szM7sw+WXXvEQgYTRYOyNxA9IEMJiyHZjSijWi1pUOxIDcqWiao6uSgalN0E70BYT7YncJ1CFI0074Uvg55KFI1i715U1SlF0bD3maRSGH4nfbn0VKJo+Pt77qKqTi2KRvlePZObI7PzXozZmZ3ZmZ3Zmf1/YjdO3qjY3xH0rBW8PN9jvDyv3cyR+R7DZvdCNq7pQk8iwfE243l7NUTR9CYSHLNWk8qPRsKcJu9CdJHgmHlXqos9vkhwPLsNuWptpi+R4Hh2G3KF+Vgte3yR4FXfq620Orw9QTleDpLd3M6Tvqed95HMzuzMzuzMzuw9sJep6v7XQSnEXsyBd0AZmN3WVzrAL2Rfvb8t/vK3OgukKM/ddelpkdlraVyLV7LvbZujsLuyG1CkxcURtGlalsQVbaHELfP43YYfeP9WRmGBfPWw7Bpc47dFWnaTsBy7K9ri6r3Q4w47BrSsbx7neeh5NwZTp7ZIy34ik2P3iraYxK22aguyb+c46RfvZGibgZsElfzRc9fB7oq2uJysHXvfZs+u0tDspl4RhqBgOAHGEXhpWu3Ks0VbqF6Lq9piC7vQIGszRSkDs+MfqFMbTkBJWF6almO3RVsocQsfS0yihOxXGgQpW2D4/+728p6A2Zmd2Zmd2c+SfaCHZh/u8QdspaeBZ15I7gAAAABJRU5ErkJggg==);
|
||||
}
|
||||
|
||||
#td-introduction-modal .main-menu + p {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
#td-introduction-modal footer {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
#td-introduction-modal button {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
#td-introduction-modal .anondata {
|
||||
float: left;
|
||||
margin: 5px 7px;
|
||||
}
|
||||
|
||||
#td-introduction-modal .anondata input {
|
||||
vertical-align: -10%;
|
||||
}
|
||||
|
||||
#td-introduction-modal .anondata label {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
}
|
61
Resources/Scripts/imports/styles/twitter.base.css
Normal file
61
Resources/Scripts/imports/styles/twitter.base.css
Normal file
@ -0,0 +1,61 @@
|
||||
/*********************/
|
||||
/* Center everything */
|
||||
/*********************/
|
||||
|
||||
#doc {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
display: table;
|
||||
}
|
||||
|
||||
#page-outer {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#page-container {
|
||||
padding: 0 20px !important;
|
||||
width: 100% !important;
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
|
||||
.page-canvas {
|
||||
margin: 0 auto !important;
|
||||
}
|
||||
|
||||
/*******************/
|
||||
/* General styling */
|
||||
/*******************/
|
||||
|
||||
body {
|
||||
/* remove scrollbar */
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
.page-canvas {
|
||||
/* tweak page shadow */
|
||||
box-shadow: 0 0 150px rgba(255, 255, 255, 0.3) !important;
|
||||
}
|
||||
|
||||
.topbar {
|
||||
/* hide top bar */
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.page-canvas, .buttons, .btn, input {
|
||||
/* sharpen borders */
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
|
||||
input {
|
||||
/* tweak input padding */
|
||||
padding: 5px 8px 4px !important;
|
||||
}
|
||||
|
||||
button[type='submit'] {
|
||||
/* style buttons */
|
||||
border: 1px solid rgba(0, 0, 0, 0.3) !important;
|
||||
border-radius: 0 !important;
|
||||
}
|
38
Resources/Scripts/imports/styles/twitter.logout.css
Normal file
38
Resources/Scripts/imports/styles/twitter.logout.css
Normal file
@ -0,0 +1,38 @@
|
||||
/*****************************/
|
||||
/* Fix min width and margins */
|
||||
/*****************************/
|
||||
|
||||
.page-canvas {
|
||||
width: auto !important;
|
||||
max-width: 888px;
|
||||
}
|
||||
|
||||
.signout-wrapper {
|
||||
width: auto !important;
|
||||
margin: 0 auto !important;
|
||||
}
|
||||
|
||||
.signout {
|
||||
margin: 60px 0 54px !important;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
|
||||
/*******************/
|
||||
/* General styling */
|
||||
/*******************/
|
||||
|
||||
.aside {
|
||||
/* hide elements around dialog */
|
||||
display: none;
|
||||
}
|
||||
|
||||
.buttons button, .buttons a {
|
||||
/* style buttons */
|
||||
display: inline-block;
|
||||
margin: 0 4px !important;
|
||||
border: 1px solid rgba(0, 0, 0, 0.3) !important;
|
||||
border-radius: 0 !important;
|
||||
}
|
126
Resources/Scripts/imports/styles/update.css
Normal file
126
Resources/Scripts/imports/styles/update.css
Normal file
@ -0,0 +1,126 @@
|
||||
#tweetduck-update {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
width: 200px;
|
||||
height: 178px;
|
||||
z-index: 9999;
|
||||
color: #fff;
|
||||
background-color: rgb(32, 94, 138);
|
||||
text-align: center;
|
||||
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.5);
|
||||
transition: transform 400ms cubic-bezier(.02, .01, .47, 1);
|
||||
}
|
||||
|
||||
#tweetduck-update.hidden-below {
|
||||
transform: translateY(178px);
|
||||
}
|
||||
|
||||
#tweetduck-update .tdu-title {
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
margin: 8px 0 2px;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
#tweetduck-update .tdu-info {
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
margin: 3px 0;
|
||||
}
|
||||
|
||||
#tweetduck-update .tdu-showlog {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#tweetduck-update .tdu-buttons button {
|
||||
display: block;
|
||||
margin: 7px auto 0;
|
||||
padding: 4px 10px;
|
||||
width: 80%;
|
||||
height: 30px;
|
||||
border: 0;
|
||||
border-radius: 1px;
|
||||
outline: none;
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
background-color: #419de0;
|
||||
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.5);
|
||||
box-shadow: 1px 1px 1px rgba(17, 17, 17, 0.5) !important;
|
||||
transition: box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
#tweetduck-update .tdu-buttons button:hover {
|
||||
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.75);
|
||||
box-shadow: 1px 1px 1px rgba(17, 17, 17, 0.75), 0 -2px 0 rgba(17, 17, 17, 0.33) inset !important;
|
||||
}
|
||||
|
||||
#tweetduck-update .tdu-buttons button.tdu-btn-ignore, .tdu-buttons button.tdu-btn-later {
|
||||
background-color: #607a8e;
|
||||
color: #dfdfdf;
|
||||
}
|
||||
|
||||
#tweetduck-changelog {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 9998;
|
||||
}
|
||||
|
||||
#tweetduck-changelog-box {
|
||||
position: absolute;
|
||||
width: 60%;
|
||||
height: 75%;
|
||||
max-width: calc(90% - 200px);
|
||||
max-height: 90%;
|
||||
left: calc(50% + 100px);
|
||||
top: 50%;
|
||||
padding: 12px;
|
||||
overflow-y: auto;
|
||||
transform: translateX(-50%) translateY(-50%);
|
||||
font-size: 14px;
|
||||
color: #000;
|
||||
background-color: #fff;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#tweetduck-changelog h2 {
|
||||
margin: 0 0 7px;
|
||||
font-size: 23px;
|
||||
}
|
||||
|
||||
#tweetduck-changelog h2 + br {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#tweetduck-changelog h3 {
|
||||
margin: 0 0 5px 7px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
#tweetduck-changelog p {
|
||||
margin: 8px 8px 0 6px;
|
||||
}
|
||||
|
||||
#tweetduck-changelog p.li {
|
||||
margin: 0 8px 2px 30px;
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
#tweetduck-changelog p.l2 {
|
||||
margin-left: 50px !important;
|
||||
}
|
||||
|
||||
#tweetduck-changelog a {
|
||||
color: #247fbb;
|
||||
}
|
||||
|
||||
#tweetduck-changelog code {
|
||||
padding: 0 4px;
|
||||
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||
color: #24292e;
|
||||
background-color: rgba(27, 31, 35, 0.05);
|
||||
}
|
@ -1,77 +1,6 @@
|
||||
(function($, $TD){
|
||||
$(document).one("TD.ready", function(){
|
||||
let css = $(`
|
||||
<style>
|
||||
#td-introduction-modal {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#td-introduction-modal .mdl {
|
||||
width: 90%;
|
||||
min-width: 515px;
|
||||
max-width: 835px;
|
||||
height: 328px;
|
||||
}
|
||||
|
||||
#td-introduction-modal .mdl-inner {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
#td-introduction-modal .mdl-header-title {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
#td-introduction-modal .mdl-content {
|
||||
padding: 4px 16px 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#td-introduction-modal p {
|
||||
margin: 12px 0;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
#td-introduction-modal p strong {
|
||||
font-weight: normal;
|
||||
text-shadow: 0 0 #000;
|
||||
}
|
||||
|
||||
#td-introduction-modal .main-menu {
|
||||
float: left;
|
||||
width: 187px;
|
||||
height: 124px;
|
||||
margin-right: 12px;
|
||||
box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.33);
|
||||
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALsAAAB8CAMAAAAGozFUAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACZUExURQAAAAAANgAAYQA2YQA2iABhYQBhrDYAADYANjYAYTY2ADY2NjY2YTY2iDZhrDaIiDaIz2EAAGEANmEAYWE2NmE2iGFhNmFhYWGs8og2AIg2Nog2YYiIYYisiIjPrIjPz4jP8qxhAKxhNqzPiKzyrKzy8szMzM+INs/PiM/yrM/yz8/y8tfX1/Dw8PKsYfLPiPLyrPLyz/Ly8nG9GIQAAARDSURBVHja7ZyPd9IwEMdDxbI5cemcWJjbWrWrDi2Q//+PM7m7pOGHfcORlD6v7w1ok2wfwqXkvrs78TTcQzypoR6DZ//dcTA7szM7s8dgX2dCiJxGrG+Kvd+C1w619M+uoZqkGiy7AxvivI+XajURSWVO9AtjQ6UQqTIn18R+PxFSP82SCnuUOYxdfdjMzSmNn9mPMJa9p/QWpH5cZ5rjgqbanNQjOMnGS315nWl+7NGkqrmSqpb6hRuvm+PO++qygtkWKZ2YOW2EGBWr6dK3mTKHmcYeuu3r/fR5UawmUrXjY9tMLRFItez6abPYYd/MffbN4uHT89033UPVInfje1ir+Glbm7msGjKR1mbg/UFv7KFqYzGfwUhq6cbHZle1Jp3QZ65fjMwcize3hbEcu1ZnZkVCb+wBq8L86D4JGl1sm+E9AbMzO7MzO7MzO7Mze586QQqdcav7ggO3juMfF8Whlsj+6hXuzx17x0aWmswO/nBLbPaZ8RqatzdHsO/36In94WOltAOnXSZwoR7A+Qa3H5q/CEH+aA5+OfaQ6ASCYGAuZq3nAi4KjML2oH6T9qxX05+W3T5pt5+WQ5NUpA1s99jMpZt05zpiPxzVpOF1gukSFACPDN3+1p0l/3qbHf1rVBQO9gMFISz7ZnE/Xe6wX3oqXzc7KgoH+4GCEFoXA8VL20CuGprCTG6xkzZwyGZIUTjUDxSEwOybu4regzC3mxLXqm8zVhsoYUXai2aF5qQolL7KQB1QQeDvVWZndmZndmZn9nNn/3WuB9sMszM7szM7sw+WXXvEQgYTRYOyNxA9IEMJiyHZjSijWi1pUOxIDcqWiao6uSgalN0E70BYT7YncJ1CFI0074Uvg55KFI1i715U1SlF0bD3maRSGH4nfbn0VKJo+Pt77qKqTi2KRvlePZObI7PzXozZmZ3ZmZ3Zmf1/YjdO3qjY3xH0rBW8PN9jvDyv3cyR+R7DZvdCNq7pQk8iwfE243l7NUTR9CYSHLNWk8qPRsKcJu9CdJHgmHlXqos9vkhwPLsNuWptpi+R4Hh2G3KF+Vgte3yR4FXfq620Orw9QTleDpLd3M6Tvqed95HMzuzMzuzMzuw9sJep6v7XQSnEXsyBd0AZmN3WVzrAL2Rfvb8t/vK3OgukKM/ddelpkdlraVyLV7LvbZujsLuyG1CkxcURtGlalsQVbaHELfP43YYfeP9WRmGBfPWw7Bpc47dFWnaTsBy7K9ri6r3Q4w47BrSsbx7neeh5NwZTp7ZIy34ik2P3iraYxK22aguyb+c46RfvZGibgZsElfzRc9fB7oq2uJysHXvfZs+u0tDspl4RhqBgOAHGEXhpWu3Ks0VbqF6Lq9piC7vQIGszRSkDs+MfqFMbTkBJWF6almO3RVsocQsfS0yihOxXGgQpW2D4/+728p6A2Zmd2Zmd2c+SfaCHZh/u8QdspaeBZ15I7gAAAABJRU5ErkJggg==);
|
||||
}
|
||||
|
||||
#td-introduction-modal .main-menu + p {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
#td-introduction-modal footer {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
#td-introduction-modal button {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
#td-introduction-modal .anondata {
|
||||
float: left;
|
||||
margin: 5px 7px;
|
||||
}
|
||||
|
||||
#td-introduction-modal .anondata input {
|
||||
vertical-align: -10%;
|
||||
}
|
||||
|
||||
#td-introduction-modal .anondata label {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>`).appendTo(document.head);
|
||||
let css = $(`<style>#import "styles/introduction.css"</style>`).appendTo(document.head);
|
||||
|
||||
let ele = $(`
|
||||
<div id="td-introduction-modal" class="ovl scroll-v scroll-styled-v">
|
||||
|
@ -122,4 +122,6 @@
|
||||
window.TDPF_reloadColumns = window.TDGF_reloadColumns;
|
||||
window.TDPF_prioritizeNewestEvent = window.TDGF_prioritizeNewestEvent;
|
||||
window.TDPF_injectMustache = window.TDGF_injectMustache;
|
||||
|
||||
#import "scripts/plugins.base.js"
|
||||
})();
|
||||
|
@ -14,3 +14,5 @@ window.TD_PLUGINS = {
|
||||
plugin.obj.run();
|
||||
}
|
||||
};
|
||||
|
||||
#import "scripts/plugins.base.js"
|
||||
|
@ -9,33 +9,14 @@
|
||||
}
|
||||
|
||||
var style = document.createElement("style");
|
||||
document.head.appendChild(style);
|
||||
|
||||
let addRule = (rule) => {
|
||||
style.sheet.insertRule(rule, 0);
|
||||
};
|
||||
|
||||
addRule("body { overflow: hidden !important; }"); // remove scrollbar
|
||||
addRule(".page-canvas { box-shadow: 0 0 150px rgba(255, 255, 255, 0.3) !important; }"); // change page box shadow
|
||||
addRule(".topbar { display: none !important; }"); // hide top bar
|
||||
|
||||
addRule(".page-canvas, .buttons, .btn, input { border-radius: 0 !important; }"); // sharpen borders
|
||||
addRule("input { padding: 5px 8px 4px !important; }"); // tweak input padding
|
||||
addRule("button[type='submit'] { border: 1px solid rgba(0, 0, 0, 0.3) !important; border-radius: 0 !important; }"); // style buttons
|
||||
|
||||
addRule("#doc { width: 100%; height: 100%; margin: 0; position: absolute; display: table; }"); // center everything
|
||||
addRule("#page-outer { display: table-cell; vertical-align: middle; }"); // center everything
|
||||
addRule("#page-container { padding: 0 20px !important; width: 100% !important; box-sizing: border-box !important; }"); // center everything
|
||||
addRule(".page-canvas { margin: 0 auto !important; }"); // center everything
|
||||
style.innerText = `#import "styles/twitter.base.css"`;
|
||||
|
||||
if (location.pathname === "/logout"){
|
||||
addRule(".page-canvas { width: auto !important; max-width: 888px; }"); // fix min width
|
||||
addRule(".signout-wrapper { width: auto !important; margin: 0 auto !important; }"); // fix min width and margins
|
||||
addRule(".signout { margin: 60px 0 54px !important; }"); // fix dialog margins
|
||||
addRule(".buttons { padding-bottom: 0 !important; }"); // fix dialog margins
|
||||
addRule(".aside { display: none; }"); // hide elements around logout dialog
|
||||
addRule(".buttons button, .buttons a { display: inline-block; margin: 0 4px !important; border: 1px solid rgba(0, 0, 0, 0.3) !important; border-radius: 0 !important; }"); // style buttons
|
||||
style.innerText += `#import "styles/twitter.logout.css"`;
|
||||
}
|
||||
|
||||
document.head.appendChild(style);
|
||||
};
|
||||
|
||||
setTimeout(injectCSS, 1);
|
||||
|
@ -10,134 +10,7 @@
|
||||
if (!css){
|
||||
css = document.createElement("style");
|
||||
css.id = "tweetduck-update-css";
|
||||
css.innerText = `
|
||||
#tweetduck-update {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
width: 200px;
|
||||
height: 178px;
|
||||
z-index: 9999;
|
||||
color: #fff;
|
||||
background-color: rgb(32, 94, 138);
|
||||
text-align: center;
|
||||
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.5);
|
||||
transition: transform 400ms cubic-bezier(.02, .01, .47, 1);
|
||||
}
|
||||
|
||||
#tweetduck-update.hidden-below {
|
||||
transform: translateY(178px);
|
||||
}
|
||||
|
||||
#tweetduck-update .tdu-title {
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
margin: 8px 0 2px;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
#tweetduck-update .tdu-info {
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
margin: 3px 0;
|
||||
}
|
||||
|
||||
#tweetduck-update .tdu-showlog {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#tweetduck-update .tdu-buttons button {
|
||||
display: block;
|
||||
margin: 7px auto 0;
|
||||
padding: 4px 10px;
|
||||
width: 80%;
|
||||
height: 30px;
|
||||
border: 0;
|
||||
border-radius: 1px;
|
||||
outline: none;
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
background-color: #419de0;
|
||||
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.5);
|
||||
box-shadow: 1px 1px 1px rgba(17, 17, 17, 0.5) !important;
|
||||
transition: box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
#tweetduck-update .tdu-buttons button:hover {
|
||||
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.75);
|
||||
box-shadow: 1px 1px 1px rgba(17, 17, 17, 0.75), 0 -2px 0 rgba(17, 17, 17, 0.33) inset !important;
|
||||
}
|
||||
|
||||
#tweetduck-update .tdu-buttons button.tdu-btn-ignore, .tdu-buttons button.tdu-btn-later {
|
||||
background-color: #607a8e;
|
||||
color: #dfdfdf;
|
||||
}
|
||||
|
||||
#tweetduck-changelog {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 9998;
|
||||
}
|
||||
|
||||
#tweetduck-changelog-box {
|
||||
position: absolute;
|
||||
width: 60%;
|
||||
height: 75%;
|
||||
max-width: calc(90% - 200px);
|
||||
max-height: 90%;
|
||||
left: calc(50% + 100px);
|
||||
top: 50%;
|
||||
padding: 12px;
|
||||
overflow-y: auto;
|
||||
transform: translateX(-50%) translateY(-50%);
|
||||
font-size: 14px;
|
||||
color: #000;
|
||||
background-color: #fff;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#tweetduck-changelog h2 {
|
||||
margin: 0 0 7px;
|
||||
font-size: 23px;
|
||||
}
|
||||
|
||||
#tweetduck-changelog h2 + br {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#tweetduck-changelog h3 {
|
||||
margin: 0 0 5px 7px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
#tweetduck-changelog p {
|
||||
margin: 8px 8px 0 6px;
|
||||
}
|
||||
|
||||
#tweetduck-changelog p.li {
|
||||
margin: 0 8px 2px 30px;
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
#tweetduck-changelog p.l2 {
|
||||
margin-left: 50px !important;
|
||||
}
|
||||
|
||||
#tweetduck-changelog a {
|
||||
color: #247fbb;
|
||||
}
|
||||
|
||||
#tweetduck-changelog code {
|
||||
padding: 0 4px;
|
||||
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||
color: #24292e;
|
||||
background-color: rgba(27, 31, 35, 0.05);
|
||||
}`;
|
||||
|
||||
css.innerText = `#import "styles/update.css"`;
|
||||
document.head.appendChild(css);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
<?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">
|
||||
<Import Project="packages\CefSharp.WinForms.67.0.0-CI2658\build\CefSharp.WinForms.props" Condition="Exists('packages\CefSharp.WinForms.67.0.0-CI2658\build\CefSharp.WinForms.props')" />
|
||||
<Import Project="packages\CefSharp.Common.67.0.0-CI2658\build\CefSharp.Common.props" Condition="Exists('packages\CefSharp.Common.67.0.0-CI2658\build\CefSharp.Common.props')" />
|
||||
@ -363,15 +363,20 @@
|
||||
<None Include="Resources\Plugins\reply-account\configuration.default.js" />
|
||||
<None Include="Resources\Plugins\templates\.meta" />
|
||||
<None Include="Resources\Plugins\templates\browser.js" />
|
||||
<None Include="Resources\Plugins\templates\modal.html" />
|
||||
<None Include="Resources\Plugins\timeline-polls\.meta" />
|
||||
<None Include="Resources\Plugins\timeline-polls\browser.js" />
|
||||
<None Include="Resources\Scripts\code.js" />
|
||||
<None Include="Resources\Scripts\imports\scripts\plugins.base.js" />
|
||||
<None Include="Resources\Scripts\imports\styles\introduction.css" />
|
||||
<None Include="Resources\Scripts\imports\styles\twitter.base.css" />
|
||||
<None Include="Resources\Scripts\imports\styles\twitter.logout.css" />
|
||||
<None Include="Resources\Scripts\imports\styles\update.css" />
|
||||
<None Include="Resources\Scripts\introduction.js" />
|
||||
<None Include="Resources\Scripts\notification.js" />
|
||||
<None Include="Resources\Scripts\pages\error.html" />
|
||||
<None Include="Resources\Scripts\pages\example.html" />
|
||||
<None Include="Resources\Scripts\plugins.browser.js" />
|
||||
<None Include="Resources\Scripts\plugins.js" />
|
||||
<None Include="Resources\Scripts\plugins.notification.js" />
|
||||
<None Include="Resources\Scripts\screenshot.js" />
|
||||
<None Include="Resources\Scripts\styles\browser.css" />
|
||||
|
@ -6,12 +6,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetDuck", "TweetDuck.cspr
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetDuck.Browser", "subprocess\TweetDuck.Browser.csproj", "{B10B0017-819E-4F71-870F-8256B36A26AA}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "tests\UnitTests.csproj", "{A958FA7A-4A2C-42A7-BFA0-159343483F4E}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetDuck.Video", "video\TweetDuck.Video.csproj", "{278B2D11-402D-44B6-B6A1-8FA67DB65565}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetLib.Communication", "lib\TweetLib.Communication\TweetLib.Communication.csproj", "{72473763-4B9D-4FB6-A923-9364B2680F06}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetTest.System", "lib\TweetTest.System\TweetTest.System.csproj", "{A958FA7A-4A2C-42A7-BFA0-159343483F4E}"
|
||||
EndProject
|
||||
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "TweetTest.Unit", "lib\TweetTest.Unit\TweetTest.Unit.fsproj", "{EEE1071A-28FA-48B1-82A1-9CBDC5C3F2C3}"
|
||||
EndProject
|
||||
Global
|
||||
@ -28,10 +28,6 @@ Global
|
||||
{B10B0017-819E-4F71-870F-8256B36A26AA}.Debug|x86.Build.0 = Debug|x86
|
||||
{B10B0017-819E-4F71-870F-8256B36A26AA}.Release|x86.ActiveCfg = Release|x86
|
||||
{B10B0017-819E-4F71-870F-8256B36A26AA}.Release|x86.Build.0 = Release|x86
|
||||
{A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Debug|x86.Build.0 = Debug|x86
|
||||
{A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Release|x86.ActiveCfg = Debug|x86
|
||||
{A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Release|x86.Build.0 = Debug|x86
|
||||
{278B2D11-402D-44B6-B6A1-8FA67DB65565}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{278B2D11-402D-44B6-B6A1-8FA67DB65565}.Debug|x86.Build.0 = Debug|x86
|
||||
{278B2D11-402D-44B6-B6A1-8FA67DB65565}.Release|x86.ActiveCfg = Release|x86
|
||||
@ -40,10 +36,12 @@ Global
|
||||
{72473763-4B9D-4FB6-A923-9364B2680F06}.Debug|x86.Build.0 = Debug|x86
|
||||
{72473763-4B9D-4FB6-A923-9364B2680F06}.Release|x86.ActiveCfg = Release|x86
|
||||
{72473763-4B9D-4FB6-A923-9364B2680F06}.Release|x86.Build.0 = Release|x86
|
||||
{A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Debug|x86.Build.0 = Debug|x86
|
||||
{A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Release|x86.ActiveCfg = Debug|x86
|
||||
{EEE1071A-28FA-48B1-82A1-9CBDC5C3F2C3}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{EEE1071A-28FA-48B1-82A1-9CBDC5C3F2C3}.Debug|x86.Build.0 = Debug|x86
|
||||
{EEE1071A-28FA-48B1-82A1-9CBDC5C3F2C3}.Release|x86.ActiveCfg = Debug|x86
|
||||
{EEE1071A-28FA-48B1-82A1-9CBDC5C3F2C3}.Release|x86.Build.0 = Debug|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -5,9 +5,9 @@
|
||||
using TweetDuck.Configuration;
|
||||
using TweetDuck.Core.Other;
|
||||
|
||||
namespace UnitTests.Configuration{
|
||||
namespace TweetTest.Configuration{
|
||||
[TestClass]
|
||||
public class TestUserConfig : UnitTestIO{
|
||||
public class TestUserConfig : TestIO{
|
||||
private static void WriteTestConfig(string file, bool withBackup){
|
||||
UserConfig cfg = UserConfig.Load(file);
|
||||
cfg.ZoomLevel = 123;
|
@ -4,9 +4,9 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using TweetDuck.Data;
|
||||
|
||||
namespace UnitTests.Data{
|
||||
namespace TweetTest.Data{
|
||||
[TestClass]
|
||||
public class TestCombinedFileStream : UnitTestIO{
|
||||
public class TestCombinedFileStream : TestIO{
|
||||
[TestMethod]
|
||||
public void TestNoFiles(){
|
||||
using(CombinedFileStream cfs = new CombinedFileStream(File.OpenWrite("empty"))){
|
@ -3,9 +3,9 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using TweetDuck.Data.Serialization;
|
||||
|
||||
namespace UnitTests.Data{
|
||||
namespace TweetTest.Data{
|
||||
[TestClass]
|
||||
public class TestFileSerializer : UnitTestIO{
|
||||
public class TestFileSerializer : TestIO{
|
||||
private enum TestEnum{
|
||||
A, B, C, D, E
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
using System.Reflection;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("UnitTests")]
|
||||
[assembly: AssemblyTitle("TweetTest.System")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("UnitTests")]
|
||||
[assembly: AssemblyProduct("TweetTest.System")]
|
||||
[assembly: AssemblyCopyright("")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
@ -6,8 +6,8 @@
|
||||
<ProjectGuid>{A958FA7A-4A2C-42A7-BFA0-159343483F4E}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>UnitTests</RootNamespace>
|
||||
<AssemblyName>UnitTests</AssemblyName>
|
||||
<RootNamespace>TweetTest</RootNamespace>
|
||||
<AssemblyName>TweetTest.System</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
@ -45,13 +45,12 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="Configuration\TestUserConfig.cs" />
|
||||
<Compile Include="Data\TestCombinedFileStream.cs" />
|
||||
<Compile Include="Data\TestCommandLineArgs.cs" />
|
||||
<Compile Include="Data\TestFileSerializer.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="UnitTestIO.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\TweetDuck.csproj">
|
||||
<ProjectReference Include="..\..\TweetDuck.csproj">
|
||||
<Project>{2389a7cd-e0d3-4706-8294-092929a33a2d}</Project>
|
||||
<Name>TweetDuck</Name>
|
||||
</ProjectReference>
|
@ -3,9 +3,9 @@
|
||||
using System.IO;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace UnitTests{
|
||||
namespace TweetTest{
|
||||
[TestClass]
|
||||
public class UnitTestIO{
|
||||
public class TestIO{
|
||||
private static readonly HashSet<string> CreatedFolders = new HashSet<string>();
|
||||
|
||||
[TestInitialize]
|
@ -1,4 +1,4 @@
|
||||
namespace Unit.Core.BrowserUtils
|
||||
namespace TweetTest.Core.BrowserUtils
|
||||
|
||||
open Xunit
|
||||
open TweetDuck.Core.Utils
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace Unit.Core.StringUtils
|
||||
namespace TweetTest.Core.StringUtils
|
||||
|
||||
open Xunit
|
||||
open TweetDuck.Core.Utils
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace Unit.Core.TwitterUtils
|
||||
namespace TweetTest.Core.TwitterUtils
|
||||
|
||||
open Xunit
|
||||
open TweetDuck.Core.Utils
|
||||
|
368
lib/TweetTest.Unit/Data/TestCommandLineArgs.fs
Normal file
368
lib/TweetTest.Unit/Data/TestCommandLineArgs.fs
Normal file
@ -0,0 +1,368 @@
|
||||
namespace TweetTest.Data.CommandLineArgs
|
||||
|
||||
open Xunit
|
||||
open TweetDuck.Data
|
||||
|
||||
|
||||
type _TestData =
|
||||
|
||||
static member empty
|
||||
with get() = CommandLineArgs()
|
||||
|
||||
static member flags
|
||||
with get() =
|
||||
let args = CommandLineArgs()
|
||||
args.AddFlag("flag1")
|
||||
args.AddFlag("flag2")
|
||||
args.AddFlag("flag3")
|
||||
args
|
||||
|
||||
static member values
|
||||
with get() =
|
||||
let args = CommandLineArgs()
|
||||
args.SetValue("val1", "hello")
|
||||
args.SetValue("val2", "world")
|
||||
args
|
||||
|
||||
static member mixed
|
||||
with get() =
|
||||
let args = CommandLineArgs()
|
||||
args.AddFlag("flag1")
|
||||
args.AddFlag("flag2")
|
||||
args.AddFlag("flag3")
|
||||
args.SetValue("val1", "hello")
|
||||
args.SetValue("val2", "world")
|
||||
args
|
||||
|
||||
static member duplicate
|
||||
with get() =
|
||||
let args = CommandLineArgs()
|
||||
args.AddFlag("duplicate")
|
||||
args.SetValue("duplicate", "value")
|
||||
args
|
||||
|
||||
|
||||
module Count =
|
||||
|
||||
[<Fact>]
|
||||
let ``counts nothing correctly`` () =
|
||||
Assert.Equal(0, _TestData.empty.Count)
|
||||
|
||||
[<Fact>]
|
||||
let ``counts flags correctly`` () =
|
||||
Assert.Equal(3, _TestData.flags.Count)
|
||||
|
||||
[<Fact>]
|
||||
let ``counts values correctly`` () =
|
||||
Assert.Equal(2, _TestData.values.Count)
|
||||
|
||||
[<Fact>]
|
||||
let ``counts mixed flags and values correctly`` () =
|
||||
Assert.Equal(5, _TestData.mixed.Count)
|
||||
|
||||
|
||||
module Flags =
|
||||
|
||||
[<Theory>]
|
||||
[<InlineData("flag1")>]
|
||||
[<InlineData("flag2")>]
|
||||
[<InlineData("flag3")>]
|
||||
let ``HasFlag returns false if flag is missing`` (flag: string) =
|
||||
Assert.False(_TestData.empty.HasFlag(flag))
|
||||
Assert.False(_TestData.values.HasFlag(flag))
|
||||
|
||||
[<Theory>]
|
||||
[<InlineData("val1")>]
|
||||
[<InlineData("val2")>]
|
||||
let ``HasFlag returns false if the name only specifies a key`` (flag: string) =
|
||||
Assert.False(_TestData.values.HasFlag(flag))
|
||||
|
||||
[<Fact>]
|
||||
let ``HasFlag returns true if the name specifies both a flag and a value key`` () =
|
||||
Assert.True(_TestData.duplicate.HasFlag("duplicate"))
|
||||
|
||||
[<Theory>]
|
||||
[<InlineData("flag1")>]
|
||||
[<InlineData("flag2")>]
|
||||
[<InlineData("flag3")>]
|
||||
let ``HasFlag returns true if flag is present`` (flag: string) =
|
||||
Assert.True(_TestData.flags.HasFlag(flag))
|
||||
|
||||
[<Theory>]
|
||||
[<InlineData("FLAG1")>]
|
||||
[<InlineData("FlAg1")>]
|
||||
let ``HasFlag is case-insensitive`` (flag: string) =
|
||||
Assert.True(_TestData.flags.HasFlag(flag))
|
||||
|
||||
[<Fact>]
|
||||
let ``AddFlag adds new flag`` () =
|
||||
let args = _TestData.flags
|
||||
args.AddFlag("flag4")
|
||||
|
||||
Assert.Equal(4, args.Count)
|
||||
Assert.True(args.HasFlag("flag4"))
|
||||
|
||||
[<Fact>]
|
||||
let ``AddFlag does nothing if flag is already present`` () =
|
||||
let args = _TestData.flags
|
||||
args.AddFlag("flag1")
|
||||
|
||||
Assert.Equal(3, args.Count)
|
||||
|
||||
[<Theory>]
|
||||
[<InlineData("flag1")>]
|
||||
[<InlineData("flag2")>]
|
||||
[<InlineData("flag3")>]
|
||||
let ``RemoveFlag removes existing flag`` (flag: string) =
|
||||
let args = _TestData.flags
|
||||
args.RemoveFlag(flag)
|
||||
|
||||
Assert.Equal(2, args.Count)
|
||||
Assert.False(args.HasFlag(flag))
|
||||
|
||||
[<Theory>]
|
||||
[<InlineData("FLAG1")>]
|
||||
[<InlineData("FlAg1")>]
|
||||
let ``RemoveFlag is case-insensitive`` (flag: string) =
|
||||
let args = _TestData.flags
|
||||
args.RemoveFlag(flag)
|
||||
|
||||
Assert.Equal(2, args.Count)
|
||||
Assert.False(args.HasFlag(flag))
|
||||
|
||||
[<Fact>]
|
||||
let ``RemoveFlag does nothing if flag is missing`` () =
|
||||
let args = _TestData.flags
|
||||
args.RemoveFlag("missing")
|
||||
|
||||
Assert.Equal(3, args.Count)
|
||||
|
||||
|
||||
module Values =
|
||||
|
||||
[<Theory>]
|
||||
[<InlineData("val1")>]
|
||||
[<InlineData("val2")>]
|
||||
let ``HasValue returns false if key is missing`` (key: string) =
|
||||
Assert.False(_TestData.empty.HasValue(key))
|
||||
Assert.False(_TestData.flags.HasValue(key))
|
||||
|
||||
[<Theory>]
|
||||
[<InlineData("flag1")>]
|
||||
[<InlineData("flag2")>]
|
||||
[<InlineData("flag3")>]
|
||||
let ``HasValue returns false if the name specifies a flag`` (key: string) =
|
||||
Assert.False(_TestData.flags.HasValue(key))
|
||||
|
||||
[<Fact>]
|
||||
let ``HasValue returns true if the name specifies both a flag and a value key`` () =
|
||||
Assert.True(_TestData.duplicate.HasValue("duplicate"))
|
||||
|
||||
[<Theory>]
|
||||
[<InlineData("val1")>]
|
||||
[<InlineData("val2")>]
|
||||
let ``HasValue returns true if key is present`` (key: string) =
|
||||
Assert.True(_TestData.values.HasValue(key))
|
||||
|
||||
[<Theory>]
|
||||
[<InlineData("VAL1")>]
|
||||
[<InlineData("VaL1")>]
|
||||
let ``HasValue is case-insensitive`` (key: string) =
|
||||
Assert.True(_TestData.values.HasValue(key))
|
||||
|
||||
[<Theory>]
|
||||
[<InlineData("val1", "hello")>]
|
||||
[<InlineData("val2", "world")>]
|
||||
let ``GetValue returns correct value if key is present`` (key: string, expectedValue: string) =
|
||||
Assert.Equal(expectedValue, _TestData.values.GetValue(key, ""))
|
||||
|
||||
[<Theory>]
|
||||
[<InlineData("VAL1", "hello")>]
|
||||
[<InlineData("VaL1", "hello")>]
|
||||
let ``GetValue is case-insensitive`` (key: string, expectedValue: string) =
|
||||
Assert.Equal(expectedValue, _TestData.values.GetValue(key, ""))
|
||||
|
||||
[<Fact>]
|
||||
let ``GetValue returns default value if key is missing`` () =
|
||||
Assert.Equal("oh no", _TestData.values.GetValue("missing", "oh no"))
|
||||
|
||||
[<Fact>]
|
||||
let ``SetValue adds new value`` () =
|
||||
let args = _TestData.values
|
||||
args.SetValue("val3", "this is nice")
|
||||
|
||||
Assert.Equal(3, args.Count)
|
||||
Assert.Equal("this is nice", args.GetValue("val3", ""))
|
||||
|
||||
[<Fact>]
|
||||
let ``SetValue replaces existing value`` () =
|
||||
let args = _TestData.values
|
||||
args.SetValue("val2", "mom")
|
||||
|
||||
Assert.Equal(2, args.Count)
|
||||
Assert.Equal("mom", args.GetValue("val2", ""))
|
||||
|
||||
[<Theory>]
|
||||
[<InlineData("val1")>]
|
||||
[<InlineData("val2")>]
|
||||
let ``RemoveValue removes existing key`` (key: string) =
|
||||
let args = _TestData.values
|
||||
args.RemoveValue(key)
|
||||
|
||||
Assert.Equal(1, args.Count)
|
||||
Assert.False(args.HasValue(key))
|
||||
|
||||
[<Theory>]
|
||||
[<InlineData("VAL1")>]
|
||||
[<InlineData("VaL1")>]
|
||||
let ``RemoveValue is case-insensitive`` (key: string) =
|
||||
let args = _TestData.values
|
||||
args.RemoveValue(key)
|
||||
|
||||
Assert.Equal(1, args.Count)
|
||||
Assert.False(args.HasValue(key))
|
||||
|
||||
[<Fact>]
|
||||
let ``RemoveValue does nothing if key is missing`` () =
|
||||
let args = _TestData.values
|
||||
args.RemoveValue("missing")
|
||||
|
||||
Assert.Equal(2, args.Count)
|
||||
|
||||
|
||||
module Clone =
|
||||
|
||||
[<Fact>]
|
||||
let ``clones flags and values correctly`` () =
|
||||
let clone = _TestData.mixed.Clone()
|
||||
|
||||
Assert.True(clone.HasFlag("flag1"))
|
||||
Assert.True(clone.HasFlag("flag2"))
|
||||
Assert.True(clone.HasFlag("flag3"))
|
||||
Assert.Equal("hello", clone.GetValue("val1", ""))
|
||||
Assert.Equal("world", clone.GetValue("val2", ""))
|
||||
|
||||
[<Fact>]
|
||||
let ``cloning creates a new object`` () =
|
||||
let args = _TestData.mixed
|
||||
|
||||
Assert.NotSame(args.Clone(), args)
|
||||
|
||||
[<Fact>]
|
||||
let ``modifying a clone does not modify the original`` () =
|
||||
let original = _TestData.mixed
|
||||
let clone = original.Clone()
|
||||
|
||||
clone.RemoveFlag("flag1")
|
||||
clone.AddFlag("flag4")
|
||||
clone.SetValue("val1", "goodbye")
|
||||
|
||||
Assert.True(original.HasFlag("flag1"))
|
||||
Assert.False(original.HasFlag("flag4"))
|
||||
Assert.Equal("hello", original.GetValue("val1", ""))
|
||||
|
||||
|
||||
module ToDictionary =
|
||||
open System.Collections.Generic
|
||||
|
||||
[<Fact>]
|
||||
let ``does nothing with empty args`` () =
|
||||
let dict = Dictionary<string, string>()
|
||||
_TestData.empty.ToDictionary(dict)
|
||||
|
||||
Assert.Equal(0, dict.Count)
|
||||
|
||||
[<Fact>]
|
||||
let ``converts flags and values correctly`` () =
|
||||
let dict = Dictionary<string, string>()
|
||||
_TestData.mixed.ToDictionary(dict)
|
||||
|
||||
Assert.Equal(5, dict.Count)
|
||||
Assert.Equal("1", dict.["flag1"])
|
||||
Assert.Equal("1", dict.["flag2"])
|
||||
Assert.Equal("1", dict.["flag3"])
|
||||
Assert.Equal("hello", dict.["val1"])
|
||||
Assert.Equal("world", dict.["val2"])
|
||||
|
||||
[<Fact>]
|
||||
let ``prefers value if the same name is used for a flag and value`` () =
|
||||
let dict = Dictionary<string, string>()
|
||||
_TestData.duplicate.ToDictionary(dict)
|
||||
|
||||
Assert.Equal(1, dict.Count)
|
||||
Assert.Equal("value", dict.["duplicate"])
|
||||
|
||||
|
||||
module ToString =
|
||||
|
||||
[<Fact>]
|
||||
let ``returns empty string for empty args`` () =
|
||||
Assert.Equal("", _TestData.empty.ToString())
|
||||
|
||||
[<Fact>]
|
||||
let ``converts flags and values correctly`` () =
|
||||
Assert.Equal("flag1 flag2 flag3 val1 \"hello\" val2 \"world\"", _TestData.mixed.ToString())
|
||||
// not guaranteed to be in order but works for now
|
||||
|
||||
[<Fact>]
|
||||
let ``handle duplicate names in a probably pretty decent way tbh`` () =
|
||||
Assert.Equal("duplicate duplicate \"value\"", _TestData.duplicate.ToString())
|
||||
|
||||
|
||||
module FromStringArray =
|
||||
|
||||
[<Fact>]
|
||||
let ``returns empty args if input array is empty`` () =
|
||||
Assert.Equal(0, CommandLineArgs.FromStringArray('-', Array.empty).Count)
|
||||
|
||||
[<Fact>]
|
||||
let ``returns empty args if no entry starts with entry char`` () =
|
||||
Assert.Equal(0, CommandLineArgs.FromStringArray('-', [| ""; "~nope"; ":fail" |]).Count)
|
||||
|
||||
[<Fact>]
|
||||
let ``reads flags and values correctly`` () =
|
||||
let args = CommandLineArgs.FromStringArray('-', [| "-flag1"; "-flag2"; "-flag3"; "-val1"; "first value"; "-val2"; "second value" |])
|
||||
|
||||
Assert.Equal(5, args.Count)
|
||||
Assert.True(args.HasFlag("-flag1"))
|
||||
Assert.True(args.HasFlag("-flag2"))
|
||||
Assert.True(args.HasFlag("-flag3"))
|
||||
Assert.Equal("first value", args.GetValue("-val1", ""))
|
||||
Assert.Equal("second value", args.GetValue("-val2", ""))
|
||||
|
||||
|
||||
module ReadCefArguments =
|
||||
|
||||
[<Fact>]
|
||||
let ``returns empty args if input string is empty`` () =
|
||||
Assert.Equal(0, CommandLineArgs.ReadCefArguments("").Count)
|
||||
|
||||
[<Fact>]
|
||||
let ``returns empty args if input string is whitespace`` () =
|
||||
Assert.Equal(0, CommandLineArgs.ReadCefArguments(" \r\n \t").Count)
|
||||
|
||||
[<Fact>]
|
||||
let ``reads values correctly`` () =
|
||||
let args = CommandLineArgs.ReadCefArguments("--first-value=10 --second-value=\"long string with spaces\"")
|
||||
|
||||
Assert.Equal(2, args.Count)
|
||||
Assert.Equal("10", args.GetValue("first-value", ""))
|
||||
Assert.Equal("long string with spaces", args.GetValue("second-value", ""))
|
||||
|
||||
[<Fact>]
|
||||
let ``reads flags as value keys with values of 1`` () =
|
||||
let args = CommandLineArgs.ReadCefArguments("--first-flag-as-value --second-flag-as-value")
|
||||
|
||||
Assert.Equal(2, args.Count)
|
||||
Assert.Equal("1", args.GetValue("first-flag-as-value", ""))
|
||||
Assert.Equal("1", args.GetValue("second-flag-as-value", ""))
|
||||
|
||||
[<Fact>]
|
||||
let ``reads complex string with whitespace correctly`` () =
|
||||
let args = CommandLineArgs.ReadCefArguments("\t--first-value=55.5\r\n--first-flag-as-value\r\n --second-value=\"long string\"\t--second-flag-as-value ")
|
||||
|
||||
Assert.Equal(4, args.Count)
|
||||
Assert.Equal("55.5", args.GetValue("first-value", ""))
|
||||
Assert.Equal("long string", args.GetValue("second-value", ""))
|
||||
Assert.Equal("1", args.GetValue("first-flag-as-value", ""))
|
||||
Assert.Equal("1", args.GetValue("second-flag-as-value", ""))
|
@ -1,4 +1,4 @@
|
||||
namespace Unit.Data.InjectedHTML
|
||||
namespace TweetTest.Data.InjectedHTML
|
||||
|
||||
open Xunit
|
||||
open TweetDuck.Data
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace Unit.Data.Result
|
||||
namespace TweetTest.Data.Result
|
||||
|
||||
open Xunit
|
||||
open TweetDuck.Data
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace Unit.Data.TwoKeyDictionary
|
||||
namespace TweetTest.Data.TwoKeyDictionary
|
||||
|
||||
open Xunit
|
||||
open TweetDuck.Data
|
||||
|
@ -65,6 +65,7 @@
|
||||
<Compile Include="Core\TestBrowserUtils.fs" />
|
||||
<Compile Include="Core\TestStringUtils.fs" />
|
||||
<Compile Include="Core\TestTwitterUtils.fs" />
|
||||
<Compile Include="Data\TestCommandLineArgs.fs" />
|
||||
<Compile Include="Data\TestInjectedHTML.fs" />
|
||||
<Compile Include="Data\TestResult.fs" />
|
||||
<Compile Include="Data\TestTwoKeyDictionary.fs" />
|
||||
|
@ -1,182 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using TweetDuck.Data;
|
||||
|
||||
namespace UnitTests.Data{
|
||||
[TestClass]
|
||||
public class TestCommandLineArgs{
|
||||
[TestMethod]
|
||||
public void TestEmpty(){
|
||||
CommandLineArgs args = new CommandLineArgs();
|
||||
|
||||
Assert.AreEqual(0, args.Count);
|
||||
Assert.AreEqual(string.Empty, args.ToString());
|
||||
|
||||
Assert.IsFalse(args.HasFlag("x"));
|
||||
Assert.IsFalse(args.HasValue("x"));
|
||||
Assert.AreEqual("default", args.GetValue("x", "default"));
|
||||
|
||||
args.RemoveFlag("x");
|
||||
args.RemoveValue("x");
|
||||
|
||||
var dict = new Dictionary<string, string>();
|
||||
args.ToDictionary(dict);
|
||||
Assert.AreEqual(0, dict.Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestFlags(){
|
||||
CommandLineArgs args = new CommandLineArgs();
|
||||
|
||||
args.AddFlag("my_test_flag_1");
|
||||
args.AddFlag("my_test_flag_2");
|
||||
args.AddFlag("aAaAa");
|
||||
|
||||
Assert.IsFalse(args.HasValue("aAaAa"));
|
||||
|
||||
Assert.AreEqual(3, args.Count);
|
||||
Assert.IsTrue(args.HasFlag("my_test_flag_1"));
|
||||
Assert.IsTrue(args.HasFlag("my_test_flag_2"));
|
||||
Assert.IsTrue(args.HasFlag("aaaaa"));
|
||||
Assert.IsTrue(args.HasFlag("AAAAA"));
|
||||
Assert.AreEqual("my_test_flag_1 my_test_flag_2 aaaaa", args.ToString());
|
||||
|
||||
args.RemoveFlag("Aaaaa");
|
||||
|
||||
Assert.AreEqual(2, args.Count);
|
||||
Assert.IsTrue(args.HasFlag("my_test_flag_1"));
|
||||
Assert.IsTrue(args.HasFlag("my_test_flag_2"));
|
||||
Assert.IsFalse(args.HasFlag("aaaaa"));
|
||||
Assert.AreEqual("my_test_flag_1 my_test_flag_2", args.ToString());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestValues(){
|
||||
CommandLineArgs args = new CommandLineArgs();
|
||||
|
||||
args.SetValue("test_value", "My Test Value");
|
||||
args.SetValue("aAaAa", "aaaaa");
|
||||
|
||||
Assert.IsFalse(args.HasFlag("aAaAa"));
|
||||
|
||||
Assert.AreEqual(2, args.Count);
|
||||
Assert.IsTrue(args.HasValue("test_value"));
|
||||
Assert.IsTrue(args.HasValue("aaaaa"));
|
||||
Assert.IsTrue(args.HasValue("AAAAA"));
|
||||
Assert.AreEqual("My Test Value", args.GetValue("test_value", string.Empty));
|
||||
Assert.AreEqual("aaaaa", args.GetValue("aaaaa", string.Empty));
|
||||
Assert.AreEqual("test_value \"My Test Value\" aaaaa \"aaaaa\"", args.ToString());
|
||||
|
||||
args.RemoveValue("Aaaaa");
|
||||
|
||||
Assert.AreEqual(1, args.Count);
|
||||
Assert.IsTrue(args.HasValue("test_value"));
|
||||
Assert.IsFalse(args.HasValue("aaaaa"));
|
||||
Assert.AreEqual("test_value \"My Test Value\"", args.ToString());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestFlagAndValueMix(){
|
||||
CommandLineArgs args = new CommandLineArgs();
|
||||
|
||||
args.AddFlag("my_test_flag_1");
|
||||
args.AddFlag("my_test_flag_2");
|
||||
args.AddFlag("aAaAa");
|
||||
|
||||
args.SetValue("test_value", "My Test Value");
|
||||
args.SetValue("aAaAa", "aaaaa");
|
||||
|
||||
Assert.AreEqual(5, args.Count);
|
||||
Assert.IsTrue(args.HasFlag("aaaaa"));
|
||||
Assert.IsTrue(args.HasValue("aaaaa"));
|
||||
Assert.AreEqual("my_test_flag_1 my_test_flag_2 aaaaa test_value \"My Test Value\" aaaaa \"aaaaa\"", args.ToString());
|
||||
|
||||
var dict = new Dictionary<string, string>();
|
||||
args.ToDictionary(dict); // loses 'aaaaa' flag
|
||||
|
||||
Assert.AreEqual(4, dict.Count);
|
||||
Assert.AreEqual("1", dict["my_test_flag_1"]);
|
||||
Assert.AreEqual("1", dict["my_test_flag_2"]);
|
||||
Assert.AreEqual("My Test Value", dict["test_value"]);
|
||||
Assert.AreEqual("aaaaa", dict["aaaaa"]);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestClone(){
|
||||
CommandLineArgs args = new CommandLineArgs();
|
||||
|
||||
args.AddFlag("my_test_flag_1");
|
||||
args.AddFlag("my_test_flag_2");
|
||||
args.AddFlag("aAaAa");
|
||||
|
||||
args.SetValue("test_value", "My Test Value");
|
||||
args.SetValue("aAaAa", "aaaaa");
|
||||
|
||||
CommandLineArgs clone = args.Clone();
|
||||
args.RemoveFlag("aaaaa");
|
||||
args.RemoveValue("aaaaa");
|
||||
clone.RemoveFlag("my_test_flag_1");
|
||||
clone.RemoveFlag("my_test_flag_2");
|
||||
clone.RemoveValue("test_value");
|
||||
|
||||
Assert.AreEqual(3, args.Count);
|
||||
Assert.AreEqual(2, clone.Count);
|
||||
|
||||
Assert.AreEqual("my_test_flag_1 my_test_flag_2 test_value \"My Test Value\"", args.ToString());
|
||||
Assert.AreEqual("aaaaa aaaaa \"aaaaa\"", clone.ToString());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestEmptyStringArray(){
|
||||
CommandLineArgs args;
|
||||
|
||||
args = CommandLineArgs.FromStringArray('-', new string[0]);
|
||||
Assert.AreEqual(0, args.Count);
|
||||
|
||||
args = CommandLineArgs.FromStringArray('-', new string[]{ "", "+fail", "@nope" });
|
||||
Assert.AreEqual(0, args.Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestValidStringArray(){
|
||||
CommandLineArgs args;
|
||||
|
||||
args = CommandLineArgs.FromStringArray('-', new string[]{ "-flag1", "-flag2", "-FLAG3" });
|
||||
Assert.AreEqual(3, args.Count);
|
||||
Assert.IsTrue(args.HasFlag("-flag1"));
|
||||
Assert.IsTrue(args.HasFlag("-flag2"));
|
||||
Assert.IsTrue(args.HasFlag("-flag3"));
|
||||
|
||||
args = CommandLineArgs.FromStringArray('-', new string[]{ "-flag", "-value", "Here is some text!" });
|
||||
Assert.AreEqual(2, args.Count);
|
||||
Assert.IsTrue(args.HasFlag("-flag"));
|
||||
Assert.IsTrue(args.HasValue("-value"));
|
||||
Assert.AreEqual("Here is some text!", args.GetValue("-value", string.Empty));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestCefEmptyString(){
|
||||
Assert.AreEqual(0, CommandLineArgs.ReadCefArguments("").Count);
|
||||
Assert.AreEqual(0, CommandLineArgs.ReadCefArguments(" ").Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestCefValidString(){
|
||||
CommandLineArgs args = CommandLineArgs.ReadCefArguments("--aaa --bbb --first-value=123 --SECOND-VALUE=\"a b c d e\"\r\n--ccc");
|
||||
// cef has no flags, flag arguments have a value of 1
|
||||
// the processing removes all dashes in front of each key
|
||||
|
||||
Assert.AreEqual(5, args.Count);
|
||||
Assert.IsTrue(args.HasValue("aaa"));
|
||||
Assert.IsTrue(args.HasValue("bbb"));
|
||||
Assert.IsTrue(args.HasValue("ccc"));
|
||||
Assert.IsTrue(args.HasValue("first-value"));
|
||||
Assert.IsTrue(args.HasValue("second-value"));
|
||||
Assert.AreEqual("1", args.GetValue("aaa", string.Empty));
|
||||
Assert.AreEqual("1", args.GetValue("bbb", string.Empty));
|
||||
Assert.AreEqual("1", args.GetValue("ccc", string.Empty));
|
||||
Assert.AreEqual("123", args.GetValue("first-value", string.Empty));
|
||||
Assert.AreEqual("a b c d e", args.GetValue("second-value", string.Empty));
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user