diff --git a/Podfile b/Podfile new file mode 100644 index 00000000..8a965cc0 --- /dev/null +++ b/Podfile @@ -0,0 +1,6 @@ +#use_frameworks! +source 'https://github.com/CocoaPods/Specs.git' +target 'Stay' do + pod 'InterAppCommunication' + pod 'SDWebImage' +end diff --git a/Stay Extension/MatchPattern.h b/Stay Extension/MatchPattern.h new file mode 100644 index 00000000..d924f84c --- /dev/null +++ b/Stay Extension/MatchPattern.h @@ -0,0 +1,18 @@ +// +// MatchPattern.h +// Stay Extension +// +// Created by ris on 2022/5/25. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface MatchPattern : NSObject + +- (instancetype)initWithPattern:(NSString *)pattern; +- (BOOL)doMatch:(NSString *)urlString; +@end + +NS_ASSUME_NONNULL_END diff --git a/Stay Extension/MatchPattern.m b/Stay Extension/MatchPattern.m new file mode 100644 index 00000000..dac04b5c --- /dev/null +++ b/Stay Extension/MatchPattern.m @@ -0,0 +1,153 @@ +// +// MatchPattern.m +// Stay Extension +// +// Created by ris on 2022/5/25. +// + +#import "MatchPattern.h" + +@interface MatchPattern() + +@property (nonatomic, assign) BOOL all; +@property (nonatomic, strong) NSString *protocol; +@property (nonatomic, assign) BOOL pass; +@property (nonatomic, strong) NSRegularExpression *hostExpr; +@property (nonatomic, strong) NSRegularExpression *pathExpr; + +//Const members. +@property (nonatomic, strong) NSArray *validProtocols; +@property (nonatomic, strong) NSRegularExpression *REG_PARTS; +@property (nonatomic, strong) NSRegularExpression *REG_HOST; +@property (nonatomic, strong) NSRegularExpression *tldRegExp; + +@end + +@implementation MatchPattern + +- (instancetype)initWithPattern:(NSString *)pattern{ + if (self = [super init]){ + if ([pattern isEqualToString:@""]){ + self.all = YES; + self.protocol = @"all_urls"; + } + + do{ + NSArray *results = [self.REG_PARTS matchesInString:pattern + options:0 + range:NSMakeRange(0, pattern.length)]; + + if (results.count == 0){ + NSLog(@"Pattern (%@) is not vailed",pattern); + break; + } + + NSTextCheckingResult *result = results.firstObject; + NSInteger n = result.numberOfRanges; + + if (n < 4){ + NSLog(@"Pattern (%@) is not vailed",pattern); + break; + } + + self.protocol = [pattern substringWithRange:[result rangeAtIndex:1]]; + NSString *host = [pattern substringWithRange:[result rangeAtIndex:2]]; + NSString *path = [pattern substringWithRange:[result rangeAtIndex:3]]; + + if (![self.protocol isEqualToString:@"*:"] && ![self.validProtocols containsObject:self.protocol]){ + NSLog(@"@match: Invalid protocol (%@) specified.",self.protocol); + break; + } + + results = [self.REG_HOST matchesInString:host options:0 range:NSMakeRange(0, host.length)]; + if (results.count == 0){ + NSLog(@"@match: Invalid host (%@) specified.",host); + break; + } + + if (![[path substringWithRange:NSMakeRange(0, 1)] isEqualToString:@"/"]){ + NSLog(@"@match: Invalid path (%@) specified.",path); + break; + } + + if (host.length > 0){ + NSString *expr = [[[host stringByReplacingOccurrencesOfString:@"." withString:@"\\."] + stringByReplacingOccurrencesOfString:@"*" withString:@".*"] + stringByReplacingOccurrencesOfString:@"*." withString:@"*\\."]; + self.hostExpr = [[NSRegularExpression alloc] initWithPattern:[NSString stringWithFormat:@"^%@$",expr] options:0 error:nil]; + + } + + //TLD missed. + NSMutableString *builder = [[NSMutableString alloc] initWithString:@"^"]; + for (int i = 0; i < path.length; i++){ + unichar c = [path characterAtIndex:i]; + switch(c){ + case '*' : [builder appendString:@".*"]; + break; + case '.' : + case '?' : + case '^' : + case '$' : + case '+' : + case '{' : + case '}' : + case '[' : + case ']' : + case '|' : + case '(' : + case ')' : + case '\\' : [builder appendFormat:@"\\%C",c]; + break; + case ' ' : + break; + default : [builder appendFormat:@"%C",c]; + break; + } + } + + [builder appendString:@"$"]; + self.pathExpr = [[NSRegularExpression alloc] initWithPattern:builder options:0 error:nil]; + self.pass = YES; + + }while(0); + } + + return self; +} + +- (NSArray *)validProtocols{ + return @[@"http:", @"https:"]; +} + +- (NSRegularExpression *)REG_PARTS{ + if (nil == _REG_PARTS){ + _REG_PARTS = [[NSRegularExpression alloc] initWithPattern:@"^([a-z*]+:|\\*:)\\/\\/([^\\/]+)?(\\/.*)$" options:0 error:nil]; + } + return _REG_PARTS; +} + +- (NSRegularExpression *)REG_HOST{ + if (nil == _REG_HOST){ + _REG_HOST = [[NSRegularExpression alloc] initWithPattern:@"^(?:\\*\\.)?[^*\\/]+$|^\\*$|^$" options:0 error:nil]; + } + return _REG_HOST; +} + +- (NSRegularExpression *)tldRegExp{ + if (nil == _tldRegExp){ + _tldRegExp = [[NSRegularExpression alloc] initWithPattern:@"^([^:]+:\\/\\/[^\\/]+)\\.tld(\\/.*)?$" options:0 error:nil]; + } + return _tldRegExp; +} + +- (BOOL)doMatch:(NSString *)urlString{ + if(!self.pass) return NO; + NSURL *url = [NSURL URLWithString:urlString]; + if (!url) return NO; + if (self.all) return YES; + + return [self.hostExpr matchesInString:url.host options:0 range:NSMakeRange(0, url.host.length)].count > 0 + && [self.pathExpr matchesInString:url.path options:0 range:NSMakeRange(0, url.path.length)].count > 0; +} +@end diff --git a/Stay Extension/Resources/background.js b/Stay Extension/Resources/background.js index ec2a4b54..0cc6d277 100644 --- a/Stay Extension/Resources/background.js +++ b/Stay Extension/Resources/background.js @@ -34,7 +34,7 @@ browser.runtime.onMessage.addListener((request, sender, sendResponse) => { if ("bootstrap" == request.from || "iframe" == request.from) { if ("fetchScripts" == request.operate) { console.log("background---fetchScripts request==", request); - browser.runtime.sendNativeMessage("application.id", { type: request.operate }, function (response) { + browser.runtime.sendNativeMessage("application.id", { type: request.operate, url: request.url, digest: request.digest }, function (response) { sendResponse(response); }); return true; @@ -449,7 +449,14 @@ browser.runtime.onMessage.addListener((request, sender, sendResponse) => { sendResponse(response); }); - } else if ("fetchMatchedScriptLog" == request.operate) { + } + else if ("exeScriptManually" == request.operate) { + browser.tabs.query({ active: true, currentWindow: true }, (tabs) => { + browser.tabs.sendMessage(tabs[0].id, { from: "background", operate: "exeScriptManually", uuid: request.uuid }); + }); + + } + else if ("fetchMatchedScriptLog" == request.operate) { if (matchAppScriptList && matchAppScriptList.length > 0) { if (matchAppScriptConsole.length > 0) { matchAppScriptConsole = []; @@ -545,4 +552,4 @@ function xhrAddListeners(xhr, tab, id, xhrId, details) { if (details.ontimeout) { xhr.addEventListener("timeout", e => xhrHandleEvent(e, xhr, tab, id, xhrId)); } -} \ No newline at end of file +} diff --git a/Stay Extension/Resources/bootstrap.js b/Stay Extension/Resources/bootstrap.js index 1b39d82e..65b833dc 100644 --- a/Stay Extension/Resources/bootstrap.js +++ b/Stay Extension/Resources/bootstrap.js @@ -46,7 +46,7 @@ const $_matchesCheck = (userLibraryScript,url) => { if (matched){ for (var i = 0; i < userLibraryScript.includes.length; i++){ matched = matchRule(url.href, userLibraryScript.includes[i]); - console.log("matchRule",url.href,userLibraryScript.includes[i],matched); + // console.log("matchRule",url.href,userLibraryScript.includes[i],matched); if (matched) break; } @@ -100,7 +100,7 @@ const $_injectInPageWithTiming = (script, runAt) => { } else { $_injectInPage(script); } - } else if (runAt === "document_end") { + } else if (runAt === "document_end" || runAt === "document_body") { if (document.readyState !== "loading") { $_injectInPage(script); } else { @@ -121,7 +121,7 @@ const $_injectInPageWithTiming = (script, runAt) => { } } -//let injectScripts = [] +let matchedScripts; (function(){ browser.runtime.onMessage.addListener((request, sender, sendResponse) => { let operate = request.operate; @@ -132,6 +132,69 @@ const $_injectInPageWithTiming = (script, runAt) => { let menuId = request.id; window.postMessage({ name: "execRegisterMenuCommand", menuId: menuId, id: id }); } + else if (request.from == "background" && "exeScriptManually" === operate){ + let targetScript; + for (var i=0; i < matchedScripts.length; i++){ + if (matchedScripts[i].uuid == request.uuid){ + targetScript = matchedScripts[i]; + break; + } + } + + if (targetScript){ + if (targetScript.requireUrls.length > 0){ + targetScript.requireUrls.forEach((url)=>{ + if (injectedVendor.has(url)) return; + injectedVendor.add(url); + if (url.startsWith('stay://')){ + browser.runtime.sendMessage({ + from: "bootstrap", + operate: "injectFile", + file:$_res($_uri(url).pathname.substring(1)), + allFrames:true, + runAt:"document_start" + }); + } + else{ + var pageInject = script.installType === "page"; + console.log("pageInject---",pageInject) + targetScript.requireCodes.forEach((urlCodeDic)=>{ + if (urlCodeDic.url == url){ + if (pageInject){ + $_injectRequiredInPage(urlCodeDic.name,urlCodeDic.code); + } + else{ + browser.runtime.sendMessage({ + from: "bootstrap", + operate: "injectScript", + code:urlCodeDic.code, + allFrames:true, + runAt:"document_start" + }); + } + + } + }); + } + }); + } + + if (targetScript.installType === "page"){ + console.log("Manually page inject"); + $_injectInPageWithTiming(targetScript,"document_start"); + } + else{ + console.log("Manually content inject"); + browser.runtime.sendMessage({ + from: "bootstrap", + operate: "injectScript", + code:targetScript.content, + allFrames:!targetScript.noFrames, + runAt:"document_start" + }); + } + } + } else if (operate.startsWith("RESP_API_XHR_BG_")) { // only respond to messages on the correct content script if (request.id !== id) return; @@ -161,19 +224,29 @@ const $_injectInPageWithTiming = (script, runAt) => { } return true; }); - browser.runtime.sendMessage({ from: "bootstrap", operate: "fetchScripts" }, (response) => { + + browser.runtime.sendMessage({ from: "bootstrap", operate: "fetchScripts", url: location.href, digest: "no"}, (response) => { let injectedVendor = new Set(); - let userLibraryScripts = JSON.parse(response.body); - let injectScripts = []; - userLibraryScripts.forEach((userLibraryScript)=>{ - console.log(userLibraryScript); - - if ($_matchesCheck(userLibraryScript,new URL(location.href))){ - injectScripts.push(userLibraryScript); - } - }); +// let userLibraryScripts = response.body; //JSON.parse(response.body); +// console.log("response",response.body); +// let injectScripts = []; +// userLibraryScripts.forEach((userLibraryScript)=>{ +// console.log("script from library",userLibraryScript); +// try { +// if ($_matchesCheck(userLibraryScript,new URL(location.href))){ +// console.log("userLibraryScript-", userLibraryScript) +// injectScripts.push(userLibraryScript); +// } +// +// } catch (error) { +// console.error("¥_matchesCheck-----error", error) +// } +// }); + + matchedScripts = response.body; - injectScripts.forEach((script) => { + console.log("matchedScripts-", matchedScripts) + matchedScripts.forEach((script) => { if (script.requireUrls.length > 0 && script.active){ script.requireUrls.forEach((url)=>{ if (injectedVendor.has(url)) return; @@ -228,12 +301,6 @@ const $_injectInPageWithTiming = (script, runAt) => { } }); - - browser.runtime.sendMessage({ - from: "bootstrap", - operate: "setMatchedScripts", - matchScripts: injectScripts - }); }); window.addEventListener('message', (e) => { if (!e || !e.data || !e.data.name) return; diff --git a/Stay Extension/Resources/checkUserscript.js b/Stay Extension/Resources/checkUserscript.js new file mode 100644 index 00000000..ec2bb005 --- /dev/null +++ b/Stay Extension/Resources/checkUserscript.js @@ -0,0 +1,34 @@ + + + +console.log("test checkUserscript") +let userscriptText = document.body.textContent; + +let meta = extractMeta(userscriptText).match(/.+/g); +const is_dark = () => { + return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; +} +if(meta){ + let url = encodeURIComponent(window.location.href); + let stayImg = browser.runtime.getURL("images/icon-256.png"); + let bg = "background: #fff;"; + let fontColor = "color: #000000;" + if (is_dark()) { + bg = "background: #000;"; + fontColor = "color: #F3F3F3;" + } + let schemeUrl = "stay://x-callback-url/install?scriptURL="+url; + let popToastTemp = [ + '' + ]; + let temp = popToastTemp.join(""); + let tempDom = document.createElement("div"); + tempDom.innerHTML = temp; + document.body.appendChild(tempDom); +} diff --git a/Stay Extension/Resources/images/manaully.png b/Stay Extension/Resources/images/manaully.png new file mode 100644 index 00000000..d2d72637 Binary files /dev/null and b/Stay Extension/Resources/images/manaully.png differ diff --git a/Stay Extension/Resources/manifest.json b/Stay Extension/Resources/manifest.json index 9ad9242b..5a0ca856 100644 --- a/Stay Extension/Resources/manifest.json +++ b/Stay Extension/Resources/manifest.json @@ -25,6 +25,12 @@ "matches": [ "*://*/*" ], "all_frames": false, "run_at": "document_start" + }, + { + "js": [ "parse-meta-line.js","parse-user-script.js","checkUserscript.js"], + "matches": [ "*://*/*.js" ], + "all_frames": false, + "run_at": "document_end" } ], diff --git a/Stay Extension/Resources/parse-meta-line.js b/Stay Extension/Resources/parse-meta-line.js new file mode 100644 index 00000000..fb81bd49 --- /dev/null +++ b/Stay Extension/Resources/parse-meta-line.js @@ -0,0 +1,793 @@ +/** + Source code: https://github.com/greasemonkey/greasemonkey/blob/master/src/parse-meta-line.js + Lincese: https://github.com/greasemonkey/greasemonkey/blob/master/LICENSE.mit + Github: https://github.com/greasemonkey/greasemonkey + */ + +'use strict'; + +// Private implementation. +(function() { + +/* + * Generated by PEG.js 0.9.0. + * + * http://pegjs.org/ + */ + +function peg$subclass(child, parent) { + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor(); +} + +function peg$SyntaxError(message, expected, found, location) { + this.message = message; + this.expected = expected; + this.found = found; + this.location = location; + this.name = "SyntaxError"; + + if (typeof Error.captureStackTrace === "function") { + Error.captureStackTrace(this, peg$SyntaxError); + } +} + +peg$subclass(peg$SyntaxError, Error); + +window.parseMetaLine = function(input) { + var options = arguments.length > 1 ? arguments[1] : {}, + parser = this, + + peg$FAILED = {}, + + peg$startRuleFunctions = { line: peg$parseline }, + peg$startRuleFunction = peg$parseline, + + peg$c0 = "// @", + peg$c1 = { type: "literal", value: "// @", description: "\"// @\"" }, + peg$c2 = "\n", + peg$c3 = { type: "literal", value: "\n", description: "\"\\n\"" }, + peg$c4 = function(meta) { return meta; }, + peg$c5 = /^[ \t\n]/, + peg$c6 = { type: "class", value: "[ \\t\\n]", description: "[ \\t\\n]" }, + peg$c7 = /^[^ \t\n]/, + peg$c8 = { type: "class", value: "[^ \\t\\n]", description: "[^ \\t\\n]" }, + peg$c9 = /^[^\n]/, + peg$c10 = { type: "class", value: "[^\\n]", description: "[^\\n]" }, + peg$c11 = "noframes", + peg$c12 = { type: "literal", value: "noframes", description: "\"noframes\"" }, + peg$c13 = function(keyword) { return {keyword:keyword}; }, + peg$c14 = "author", + peg$c15 = { type: "literal", value: "author", description: "\"author\"" }, + peg$c16 = "downloadURL", + peg$c17 = { type: "literal", value: "downloadURL", description: "\"downloadURL\"" }, + peg$c18 = "exclude", + peg$c19 = { type: "literal", value: "exclude", description: "\"exclude\"" }, + peg$c20 = "grant", + peg$c21 = { type: "literal", value: "grant", description: "\"grant\"" }, + peg$c22 = "homepageURL", + peg$c23 = { type: "literal", value: "homepageURL", description: "\"homepageURL\"" }, + peg$c24 = "icon", + peg$c25 = { type: "literal", value: "icon", description: "\"icon\"" }, + peg$c26 = "include", + peg$c27 = { type: "literal", value: "include", description: "\"include\"" }, + peg$c28 = "installURL", + peg$c29 = { type: "literal", value: "installURL", description: "\"installURL\"" }, + peg$c30 = "match", + peg$c31 = { type: "literal", value: "match", description: "\"match\"" }, + peg$c32 = "namespace", + peg$c33 = { type: "literal", value: "namespace", description: "\"namespace\"" }, + peg$c34 = "require", + peg$c35 = { type: "literal", value: "require", description: "\"require\"" }, + peg$c36 = "run-at", + peg$c37 = { type: "literal", value: "run-at", description: "\"run-at\"" }, + peg$c38 = "updateURL", + peg$c39 = { type: "literal", value: "updateURL", description: "\"updateURL\"" }, + peg$c40 = "version", + peg$c41 = { type: "literal", value: "version", description: "\"version\"" }, + peg$c42 = function(keyword, value) { return {keyword:keyword, value:value}; }, + peg$c43 = "resource", + peg$c44 = { type: "literal", value: "resource", description: "\"resource\"" }, + peg$c45 = function(keyword, value1, value2) { return {keyword:keyword, value1:value1, value2:value2}; }, + peg$c46 = "description", + peg$c47 = { type: "literal", value: "description", description: "\"description\"" }, + peg$c48 = "name", + peg$c49 = { type: "literal", value: "name", description: "\"name\"" }, + peg$c50 = ":", + peg$c51 = { type: "literal", value: ":", description: "\":\"" }, + peg$c52 = /^[a-zA-Z\-]/, + peg$c53 = { type: "class", value: "[a-zA-Z-]", description: "[a-zA-Z-]" }, + peg$c54 = function(keyword, localeValue) { return localeValue }, + peg$c55 = function(keyword, locale, value) { return {keyword:keyword, locale:locale, value:value}; }, + + peg$c56 = "note", + peg$c57 = { type: "literal", value: "note", description: "\"note\"" }, + + peg$currPos = 0, + peg$savedPos = 0, + peg$posDetailsCache = [{ line: 1, column: 1, seenCR: false }], + peg$maxFailPos = 0, + peg$maxFailExpected = [], + peg$silentFails = 0, + + peg$result; + + if ("startRule" in options) { + if (!(options.startRule in peg$startRuleFunctions)) { + throw new Error("Can't start parsing from rule \"" + options.startRule + "\"."); + } + + peg$startRuleFunction = peg$startRuleFunctions[options.startRule]; + } + + function text() { + return input.substring(peg$savedPos, peg$currPos); + } + + function location() { + return peg$computeLocation(peg$savedPos, peg$currPos); + } + + function expected(description) { + throw peg$buildException( + null, + [{ type: "other", description: description }], + input.substring(peg$savedPos, peg$currPos), + peg$computeLocation(peg$savedPos, peg$currPos) + ); + } + + function error(message) { + throw peg$buildException( + message, + null, + input.substring(peg$savedPos, peg$currPos), + peg$computeLocation(peg$savedPos, peg$currPos) + ); + } + + function peg$computePosDetails(pos) { + var details = peg$posDetailsCache[pos], + p, ch; + + if (details) { + return details; + } else { + p = pos - 1; + while (!peg$posDetailsCache[p]) { + p--; + } + + details = peg$posDetailsCache[p]; + details = { + line: details.line, + column: details.column, + seenCR: details.seenCR + }; + + while (p < pos) { + ch = input.charAt(p); + if (ch === "\n") { + if (!details.seenCR) { details.line++; } + details.column = 1; + details.seenCR = false; + } else if (ch === "\r" || ch === "\u2028" || ch === "\u2029") { + details.line++; + details.column = 1; + details.seenCR = true; + } else { + details.column++; + details.seenCR = false; + } + + p++; + } + + peg$posDetailsCache[pos] = details; + return details; + } + } + + function peg$computeLocation(startPos, endPos) { + var startPosDetails = peg$computePosDetails(startPos), + endPosDetails = peg$computePosDetails(endPos); + + return { + start: { + offset: startPos, + line: startPosDetails.line, + column: startPosDetails.column + }, + end: { + offset: endPos, + line: endPosDetails.line, + column: endPosDetails.column + } + }; + } + + function peg$fail(expected) { + if (peg$currPos < peg$maxFailPos) { return; } + + if (peg$currPos > peg$maxFailPos) { + peg$maxFailPos = peg$currPos; + peg$maxFailExpected = []; + } + + peg$maxFailExpected.push(expected); + } + + function peg$buildException(message, expected, found, location) { + function cleanupExpected(expected) { + var i = 1; + + expected.sort(function(a, b) { + if (a.description < b.description) { + return -1; + } else if (a.description > b.description) { + return 1; + } else { + return 0; + } + }); + + while (i < expected.length) { + if (expected[i - 1] === expected[i]) { + expected.splice(i, 1); + } else { + i++; + } + } + } + + function buildMessage(expected, found) { + function stringEscape(s) { + function hex(ch) { return ch.charCodeAt(0).toString(16).toUpperCase(); } + + return s + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"') + .replace(/\x08/g, '\\b') + .replace(/\t/g, '\\t') + .replace(/\n/g, '\\n') + .replace(/\f/g, '\\f') + .replace(/\r/g, '\\r') + .replace(/[\x00-\x07\x0B\x0E\x0F]/g, function(ch) { return '\\x0' + hex(ch); }) + .replace(/[\x10-\x1F\x80-\xFF]/g, function(ch) { return '\\x' + hex(ch); }) + .replace(/[\u0100-\u0FFF]/g, function(ch) { return '\\u0' + hex(ch); }) + .replace(/[\u1000-\uFFFF]/g, function(ch) { return '\\u' + hex(ch); }); + } + + var expectedDescs = new Array(expected.length), + expectedDesc, foundDesc, i; + + for (i = 0; i < expected.length; i++) { + expectedDescs[i] = expected[i].description; + } + + expectedDesc = expected.length > 1 + ? expectedDescs.slice(0, -1).join(", ") + + " or " + + expectedDescs[expected.length - 1] + : expectedDescs[0]; + + foundDesc = found ? "\"" + stringEscape(found) + "\"" : "end of input"; + + return "Expected " + expectedDesc + " but " + foundDesc + " found."; + } + + if (expected !== null) { + cleanupExpected(expected); + } + + return new peg$SyntaxError( + message !== null ? message : buildMessage(expected, found), + expected, + found, + location + ); + } + + function peg$parseline() { + var s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 4) === peg$c0) { + s1 = peg$c0; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c1); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parsekeyword0(); + if (s2 === peg$FAILED) { + s2 = peg$parsekeyword1(); + if (s2 === peg$FAILED) { + s2 = peg$parsekeyword2(); + if (s2 === peg$FAILED) { + s2 = peg$parsekeywordLocale(); + } + } + } + if (s2 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 10) { + s3 = peg$c2; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c3); } + } + if (s3 === peg$FAILED) { + s3 = null; + } + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$c4(s2); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsewhitespace() { + var s0, s1; + + s0 = []; + if (peg$c5.test(input.charAt(peg$currPos))) { + s1 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c6); } + } + if (s1 !== peg$FAILED) { + while (s1 !== peg$FAILED) { + s0.push(s1); + if (peg$c5.test(input.charAt(peg$currPos))) { + s1 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c6); } + } + } + } else { + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsenon_whitespace() { + var s0, s1, s2; + + s0 = peg$currPos; + s1 = []; + if (peg$c7.test(input.charAt(peg$currPos))) { + s2 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c8); } + } + if (s2 !== peg$FAILED) { + while (s2 !== peg$FAILED) { + s1.push(s2); + if (peg$c7.test(input.charAt(peg$currPos))) { + s2 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c8); } + } + } + } else { + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + s0 = input.substring(s0, peg$currPos); + } else { + s0 = s1; + } + + return s0; + } + + function peg$parsenon_newline() { + var s0, s1, s2; + + s0 = peg$currPos; + s1 = []; + if (peg$c9.test(input.charAt(peg$currPos))) { + s2 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c10); } + } + if (s2 !== peg$FAILED) { + while (s2 !== peg$FAILED) { + s1.push(s2); + if (peg$c9.test(input.charAt(peg$currPos))) { + s2 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c10); } + } + } + } else { + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + s0 = input.substring(s0, peg$currPos); + } else { + s0 = s1; + } + + return s0; + } + + function peg$parsekeyword0() { + var s0, s1; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 8) === peg$c11) { + s1 = peg$c11; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c12); } + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$c13(s1); + } + s0 = s1; + + return s0; + } + + function peg$parsekeyword1() { + var s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 6) === peg$c14) { + s1 = peg$c14; + peg$currPos += 6; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c15); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 11) === peg$c16) { + s1 = peg$c16; + peg$currPos += 11; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c17); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 7) === peg$c18) { + s1 = peg$c18; + peg$currPos += 7; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c19); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 5) === peg$c20) { + s1 = peg$c20; + peg$currPos += 5; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c21); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 11) === peg$c22) { + s1 = peg$c22; + peg$currPos += 11; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c23); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c24) { + s1 = peg$c24; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c25); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 7) === peg$c26) { + s1 = peg$c26; + peg$currPos += 7; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c27); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 10) === peg$c28) { + s1 = peg$c28; + peg$currPos += 10; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c29); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 5) === peg$c30) { + s1 = peg$c30; + peg$currPos += 5; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c31); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 9) === peg$c32) { + s1 = peg$c32; + peg$currPos += 9; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c33); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 7) === peg$c34) { + s1 = peg$c34; + peg$currPos += 7; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c35); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c36) { + s1 = peg$c36; + peg$currPos += 6; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c37); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 9) === peg$c38) { + s1 = peg$c38; + peg$currPos += 9; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c39); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 7) === peg$c40) { + s1 = peg$c40; + peg$currPos += 7; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c41); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c56) { + s1 = peg$c56; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c57); } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + if (s1 !== peg$FAILED) { + s2 = peg$parsewhitespace(); + if (s2 !== peg$FAILED) { + s3 = peg$parsenon_newline(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$c42(s1, s3); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsekeyword2() { + var s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 8) === peg$c43) { + s1 = peg$c43; + peg$currPos += 8; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c44); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parsewhitespace(); + if (s2 !== peg$FAILED) { + s3 = peg$parsenon_whitespace(); + if (s3 !== peg$FAILED) { + s4 = peg$parsewhitespace(); + if (s4 !== peg$FAILED) { + s5 = peg$parsenon_newline(); + if (s5 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$c45(s1, s3, s5); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsekeywordLocale() { + var s0, s1, s2, s3, s4, s5, s6; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 11) === peg$c46) { + s1 = peg$c46; + peg$currPos += 11; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c47); } + } + if (s1 === peg$FAILED) { + if (input.substr(peg$currPos, 4) === peg$c48) { + s1 = peg$c48; + peg$currPos += 4; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c49); } + } + } + if (s1 !== peg$FAILED) { + s2 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 58) { + s3 = peg$c50; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c51); } + } + if (s3 !== peg$FAILED) { + s4 = peg$currPos; + s5 = []; + if (peg$c52.test(input.charAt(peg$currPos))) { + s6 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c53); } + } + if (s6 !== peg$FAILED) { + while (s6 !== peg$FAILED) { + s5.push(s6); + if (peg$c52.test(input.charAt(peg$currPos))) { + s6 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$c53); } + } + } + } else { + s5 = peg$FAILED; + } + if (s5 !== peg$FAILED) { + s4 = input.substring(s4, peg$currPos); + } else { + s4 = s5; + } + if (s4 !== peg$FAILED) { + peg$savedPos = s2; + s3 = peg$c54(s1, s4); + s2 = s3; + } else { + peg$currPos = s2; + s2 = peg$FAILED; + } + } else { + peg$currPos = s2; + s2 = peg$FAILED; + } + if (s2 === peg$FAILED) { + s2 = null; + } + if (s2 !== peg$FAILED) { + s3 = peg$parsewhitespace(); + if (s3 !== peg$FAILED) { + s4 = peg$parsenon_newline(); + if (s4 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$c55(s1, s2, s4); + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + peg$result = peg$startRuleFunction(); + + if (peg$result !== peg$FAILED && peg$currPos === input.length) { + return peg$result; + } else { + if (peg$result !== peg$FAILED && peg$currPos < input.length) { + peg$fail({ type: "end", description: "end of input" }); + } + + throw peg$buildException( + null, + peg$maxFailExpected, + peg$maxFailPos < input.length ? input.charAt(peg$maxFailPos) : null, + peg$maxFailPos < input.length + ? peg$computeLocation(peg$maxFailPos, peg$maxFailPos + 1) + : peg$computeLocation(peg$maxFailPos, peg$maxFailPos) + ); + } +} + +})(); diff --git a/Stay Extension/Resources/parse-user-script.js b/Stay Extension/Resources/parse-user-script.js new file mode 100644 index 00000000..8e54f2a2 --- /dev/null +++ b/Stay Extension/Resources/parse-user-script.js @@ -0,0 +1,217 @@ +/** + Source code: https://github.com/greasemonkey/greasemonkey/blob/master/src/parse-user-script.js + Lincese: https://github.com/greasemonkey/greasemonkey/blob/master/LICENSE.mit + Github: https://github.com/greasemonkey/greasemonkey + */ + + +'use strict'; + +const gAllMetaRegexp = new RegExp( + '^(\u00EF\u00BB\u00BF)?// ==UserScript==([\\s\\S]*?)^// ==/UserScript==', + 'm'); + + +/** Get just the stuff between ==UserScript== lines. */ +function extractMeta(content) { + const meta = content && content.match(gAllMetaRegexp); + if (meta) return meta[2].replace(/^\s+/, ''); + return ''; +} + + +// Private implementation. +(function() { + +/** Pull the filename part from the URL, without `.user.js`. */ +function nameFromUrl(url) { + native.nslog("nameFromUrl"); + let name = url.substring(0, url.indexOf(".user.js")); + name = name.substring(name.lastIndexOf("/") + 1); + return name; +} + + +// Safely construct a new URL object from a path and base. According to MDN, +// if a URL constructor received an absolute URL as the path then the base +// is ignored. Unfortunately that doesn't seem to be the case. And if the +// base is invalid (null / empty string) then an exception is thrown. +function safeUrl(path, base) { + //TODO: + //new URL() seems error in javascriptcore, just return origin path; + return path; +} + +// Defaults that can only be applied after the meta block has been parsed. +function prepDefaults(details) { + // We couldn't set this default above in case of real data, so if there's + // still no includes, set the default of include everything. + if (details.includes.length == 0 && details.matches.length == 0) { + details.includes.push('*'); + } + + if (details.grants.includes('none') && details.grants.length > 1) { + details.grants = ['none']; + } + + return details; +} + + +/** Parse the source of a script; produce object of data. */ +window.parseUserScript = function(content, url, failWhenMissing=false) { + if (!content) { + throw new Error('parseUserScript() got no content!'); + } + + // Populate with defaults in case the script specifies no value. + const details = { + 'downloadUrl': '', + 'updateUrl': '', + 'excludes': [], + 'grants': [], + 'homePageUrl': '', + 'author': 'Unnamed Author', + 'includes': [], + 'locales': {}, + 'matches': [], + 'name': url && nameFromUrl(url) || 'Unnamed Script', + 'namespace': url && new URL(url).host || '', + 'noFrames': false, + 'requireUrls': [], + 'resourceUrls': {}, + 'notes':[], + 'runAt': 'end', + 'pass':true, + 'errorMessage':'' + }; + + let meta = extractMeta(content).match(/.+/g); + if (!meta) { + native.nslog("no meta"); + if (failWhenMissing) { + throw new Error('Could not parse, no meta.'); + } else { + details.pass = false; + details.errorMessage = "no meta"; + return prepDefaults(details); + } + } + + + for (let i = 0, metaLine = ''; metaLine = meta[i]; i++) { + let data; + try { + data = window.parseMetaLine(metaLine.replace(/\s+$/, '')); + } catch (e) { + // Ignore invalid/unsupported meta lines. + continue; + } + native.nslog(data.keyword); + if (UserScriptUnsupport_TAGS.has(data.keyword)){ + details.pass = false; + details.errorMessage += "Unsupport tag: "+data.keyword+"\n"; + continue; + } + + native.nslog(data.keyword); + switch (data.keyword) { + case 'noframes': + details.noFrames = true; + break; + case 'homepageURL': + details.homePageUrl = data.value; + break; + case 'updateURL': + details.updateUrl = data.value; + break; + case 'downloadURL': + details.downloadUrl = data.value; + break; + case 'namespace': + case 'version': + details[data.keyword] = data.value; + break; + case 'run-at': + if (RunAtUnsupport_ATTRS.has(data.value)) { + details.pass = false; + details.errorMessage += 'Unsupport run-at '+data.value+'\n'; + } + else{ + details.runAt = data.value.replace('document-', ''); + } + // TODO: Assert/normalize to supported value. + break; + case 'grant': + if (GM_APIS.has(data.value)) { + details.grants.push(data.value); + } + else{ + details.pass = false; + details.errorMessage += 'Unsupport GM api '+data.value+'\n'; + } + break; + case 'description': + case 'name': + let locale = data.locale; + native.nslog("locale"); + native.nslog(locale); + if (locale) { + if (!details.locales[locale]) details.locales[locale] = {}; + details.locales[locale][data.keyword] = data.value; + } else { + details[data.keyword] = data.value; + } + break; + case 'exclude': + details.excludes.push(data.value); + break; + case 'include': + details.includes.push(data.value); + break; + case 'note': + details.notes.push(data.value); + break; + case 'match': + try { + new window.MatchPattern(data.value); + details.matches.push(data.value); + } catch (e) { + details.errorMessage += 'Unsupport match pattern' + data.value; + } + break; + case 'icon': + details.iconUrl = safeUrl(data.value, url).toString(); + break; + case 'require': + //hard code cuz only support stay:// now. +// if (data.value.startsWith('stay://')){ +// details.requireUrls.push(safeUrl(data.value, url).toString()); +// } +// else{ +// details.pass = false; +// details.errorMessage += 'Unsupport require protocol: '+data.value; +// } + + details.requireUrls.push(safeUrl(data.value, url).toString()); + + break; + case 'resource': + let resourceName = data.value1; + let resourceUrl = data.value2; + if (resourceName in details.resourceUrls) { + throw new Error(_('duplicate_resource_NAME', resourceName)); + } + details.resourceUrls[resourceName] = safeUrl(resourceUrl, url).toString(); + break; + case 'author': + details.author = data.value; + break; + } + } + native.nslog(details); + return prepDefaults(details); +} + +})(); + diff --git a/Stay Extension/Resources/popup.css b/Stay Extension/Resources/popup.css index e806d466..7c7584fe 100644 --- a/Stay Extension/Resources/popup.css +++ b/Stay Extension/Resources/popup.css @@ -248,6 +248,17 @@ section { background-position: center; /* top: 50%; */ } +.active-manually{ + /* position: absolute; */ + width: 40px; + height: 40px; + /* right: 50px; */ + background-image: url(./images/manaully.png); + background-size: 50%; + background-repeat: no-repeat; + background-position: center; + /* top: 50%; */ +} .start .active-case .active-icon{ /* transform: scale(0.5, 0.5); */ /* transition: transform 0.5s linear, opacity 0.5s linear; */ diff --git a/Stay Extension/Resources/popup.js b/Stay Extension/Resources/popup.js index b45e9ac3..c8f6d7de 100644 --- a/Stay Extension/Resources/popup.js +++ b/Stay Extension/Resources/popup.js @@ -47,6 +47,7 @@ let browserLangurage = "", '
', '
', '
', + // '
', '
'].join(''), registerMenuItemTemp = [ '' @@ -105,16 +106,18 @@ browser.runtime.onMessage.addListener((request, sender, sendResponse) => { function fetchMatchedScriptList(){ browser.tabs.getSelected(null, (tab) => { browserRunUrl = tab.url; - browser.runtime.sendMessage({ from: "bootstrap", operate: "fetchScripts" }, (response) => { + browser.runtime.sendMessage({ from: "bootstrap", operate: "fetchScripts", url: browserRunUrl, digest: "yes" }, (response) => { try{ - let userLibraryScripts = JSON.parse(response.body); - userLibraryScripts.forEach((userLibraryScript) => { - let urlParse = new URL(browserRunUrl) - - if (matchesCheck(userLibraryScript, urlParse)) { - scriptStateList.push(userLibraryScript); - } - }); +// let userLibraryScripts = response.body; //JSON.parse(response.body); +// userLibraryScripts.forEach((userLibraryScript) => { +// let urlParse = new URL(browserRunUrl) +// +// if (matchesCheck(userLibraryScript, urlParse)) { +// scriptStateList.push(userLibraryScript); +// } +// }); + scriptStateList = response.body; + //fetch register menu from popup to content browser.runtime.sendMessage({ from: "popup", operate: "fetchRegisterMenuCommand" }); renderScriptContent(scriptStateList); @@ -245,6 +248,12 @@ window.onload=function(){ handleScriptActive(uuid, active.bool()); return; } + if (target && target.nodeName.toLowerCase() == "div" && target.className.toLowerCase() == "active-manually") { + // 获取到具体事件触发的active-case,进行active + let uuid = target.getAttribute("uuid"); + handleExecScriptManually(uuid); + return; + } // register menu click if (target && target.nodeName.toLowerCase() == "div" && target.className.toLowerCase() == "active-setting") { let uuid = target.getAttribute("uuid"); @@ -326,6 +335,8 @@ function renderScriptContent(datas) { let showMenu = grants && grants.length > 0 && (grants.includes("GM.registerMenuCommand") || grants.includes("GM_registerMenuCommand")) ? "block":"none" data.showMenu = showMenu data.status = item.active ? i18nProp["state_actived"] : i18nProp["state_stopped"] + let showManually = !item.active ? "block":"none" + data.showManually = showManually; var _dom = document.createElement('div'); let index = data.active ? 1 : 0; _dom.setAttribute('class', 'content-item ' + scriptState[index]); @@ -442,6 +453,22 @@ function handleScriptActive(uuid, active) { } } +/** + * 控制脚本是否运行 + * @param {string} uuid 脚本id + */ +function handleExecScriptManually(uuid) { + if (uuid && uuid != "" && typeof uuid == "string") { + browser.runtime.sendMessage({ + from: "popup", + operate: "exeScriptManually", + uuid: uuid, + }, (response) => { + console.log("exeScriptManually response,", response) + }) + } +} + /** * tab切换点击事件 diff --git a/Stay Extension/SafariWebExtensionHandler.m b/Stay Extension/SafariWebExtensionHandler.m index 1760de97..e714e5c0 100644 --- a/Stay Extension/SafariWebExtensionHandler.m +++ b/Stay Extension/SafariWebExtensionHandler.m @@ -8,34 +8,105 @@ #import "SafariWebExtensionHandler.h" #import "Stroge.h" #import +#import "MatchPattern.h" +#import "SharedStorageManager.h" @implementation SafariWebExtensionHandler +//https://wiki.greasespot.net/Include_and_exclude_rules +- (NSRegularExpression *)convert2GlobsRegExp:(NSString *)str{ + NSString *expr = [[[[str stringByReplacingOccurrencesOfString:@"." withString:@"\\."] + stringByReplacingOccurrencesOfString:@"?" withString:@"\\?"] + stringByReplacingOccurrencesOfString:@"*" withString:@".*"] + stringByReplacingOccurrencesOfString:@"*." withString:@"*\\."]; + return [[NSRegularExpression alloc] initWithPattern:[NSString stringWithFormat:@"^%@$",expr] options:0 error:nil]; +} + +- (BOOL)matchesCheck:(NSDictionary *)userscript url:(NSString *)url{ + NSArray *matches = userscript[@"matches"]; + + BOOL matched = NO; + for (NSString *match in matches){ + @autoreleasepool { + MatchPattern *matchPattern = [[MatchPattern alloc] initWithPattern:match]; + if ([matchPattern doMatch:url]){ + matched = YES; + break; + } + } + + } + + if (!matched){ //Fallback and treat match as globs expr + for (NSString *match in matches){ + NSRegularExpression *fallbackMatchExpr = [self convert2GlobsRegExp:match]; + NSArray *result = [fallbackMatchExpr matchesInString:url options:0 range:NSMakeRange(0, url.length)]; + if (result.count > 0){ + matched = YES; + break; + } + } + } + + if (!matched){ + NSArray *includes = userscript[@"includes"]; + for (NSString *include in includes){ + NSRegularExpression *includeExpr = [self convert2GlobsRegExp:include]; + NSArray *result = [includeExpr matchesInString:url options:0 range:NSMakeRange(0, url.length)]; + if (result.count > 0){ + matched = YES; + break; + } + } + } + + if (matched){ + NSArray *excludes = userscript[@"excludes"]; + for (NSString *exclude in excludes){ + NSRegularExpression *excludeExpr = [self convert2GlobsRegExp:exclude]; + NSArray *result = [excludeExpr matchesInString:url options:0 range:NSMakeRange(0, url.length)]; + if (result.count > 0){ + matched = NO; + break; + } + } + } + + return matched; +} + - (void)beginRequestWithExtensionContext:(NSExtensionContext *)context { NSDictionary *message = (NSDictionary *)[context.inputItems.firstObject userInfo][SFExtensionMessageKey]; NSExtensionItem *response = [[NSExtensionItem alloc] init]; - NSUserDefaults *groupUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.dajiu.stay.pro"]; - id body = [NSNull null]; if ([message[@"type"] isEqualToString:@"fetchScripts"]){ - NSMutableArray *datas = [NSMutableArray arrayWithArray:[groupUserDefaults arrayForKey:@"ACTIVE_SCRIPTS"]]; + NSString *url = message[@"url"]; + NSString *digest = message[@"digest"]; + [SharedStorageManager shared].userscriptHeaders = nil; + NSMutableArray *datas = [NSMutableArray arrayWithArray:[SharedStorageManager shared].userscriptHeaders.content]; for(int i = 0;i < datas.count; i++) { NSDictionary *data = datas[i]; - NSArray *requireUrlsAndCodes = [self getUserScriptRequireListByUserScript:data]; - if (requireUrlsAndCodes != nil) { - NSMutableDictionary *mulDic = [NSMutableDictionary dictionaryWithDictionary:data]; - mulDic[@"requireCodes"] = requireUrlsAndCodes; - [datas replaceObjectAtIndex:i withObject:mulDic]; + if (![self matchesCheck:data url:url]){ + [datas removeObjectAtIndex:i]; + i--; + continue; + } + + if (digest.length == 0 || [digest isEqualToString:@"no"]){ + NSArray *requireUrlsAndCodes = [self getUserScriptRequireListByUserScript:data]; + if (requireUrlsAndCodes != nil) { + NSMutableDictionary *mulDic = [NSMutableDictionary dictionaryWithDictionary:data]; + mulDic[@"requireCodes"] = requireUrlsAndCodes; + mulDic[@"content"] = [self getContentWithUUID:data[@"uuid"]]; + [datas replaceObjectAtIndex:i withObject:mulDic]; + } } } - body = [[NSString alloc] initWithData: - [NSJSONSerialization dataWithJSONObject:datas - options:0 - error:nil] - encoding:NSUTF8StringEncoding]; + + body = datas; } else if ([message[@"type"] isEqualToString:@"GM_setValue"]){ NSString *uuid = message[@"uuid"]; @@ -67,7 +138,7 @@ - (void)beginRequestWithExtensionContext:(NSExtensionContext *)context } } else if ([message[@"type"] isEqualToString:@"setScriptActive"]){ - NSMutableArray *datas = [NSMutableArray arrayWithArray:[groupUserDefaults arrayForKey:@"ACTIVE_SCRIPTS"]]; + NSMutableArray *datas = [NSMutableArray arrayWithArray:[SharedStorageManager shared].userscriptHeaders.content]; NSString *uuid = message[@"uuid"]; bool activeVal = [message[@"active"] boolValue]; if (datas != NULL && datas.count > 0) { @@ -78,29 +149,17 @@ - (void)beginRequestWithExtensionContext:(NSExtensionContext *)context [datas removeObject:dic]; [mdic setValue:@(activeVal) forKey:@"active"]; [datas addObject:mdic]; - [groupUserDefaults setObject:datas forKey:@"ACTIVE_SCRIPTS"]; - [groupUserDefaults synchronize]; + [SharedStorageManager shared].userscriptHeaders.content = datas; + [[SharedStorageManager shared].userscriptHeaders flush]; break; } } } - - if([groupUserDefaults arrayForKey:@"ACTIVE_CHANGE"] != NULL && [groupUserDefaults arrayForKey:@"ACTIVE_CHANGE"].count > 0){ - NSMutableArray *datas = [NSMutableArray arrayWithArray:[groupUserDefaults arrayForKey:@"ACTIVE_CHANGE"]]; - NSDictionary *dic = @{@"uuid":uuid,@"active":activeVal?@"1":@"0"}; - [datas addObject:dic]; - NSUserDefaults *groupUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.dajiu.stay.pro"]; - [groupUserDefaults setObject:datas forKey:@"ACTIVE_CHANGE"]; - [groupUserDefaults synchronize]; - } else { - NSMutableArray *datas = [[NSMutableArray alloc] init]; - NSDictionary *dic = @{@"uuid":uuid,@"active":activeVal?@"1":@"0"}; - [datas addObject:dic]; - NSUserDefaults *groupUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.dajiu.stay.pro"]; - [groupUserDefaults setObject:datas forKey:@"ACTIVE_CHANGE"]; - [groupUserDefaults synchronize]; - } + NSMutableDictionary *changed = [NSMutableDictionary dictionaryWithDictionary:[SharedStorageManager shared].activateChanged.content]; + changed[uuid] = activeVal?@(YES):@(NO); + [SharedStorageManager shared].activateChanged.content = changed; + [[SharedStorageManager shared].activateChanged flush]; } else if ([message[@"type"] isEqualToString:@"GM_getResourceText"]){ NSString *uuid = message[@"uuid"]; @@ -125,7 +184,7 @@ - (void)beginRequestWithExtensionContext:(NSExtensionContext *)context } else if ([message[@"type"] isEqualToString:@"GM_getResourceUrl"]){ - NSMutableArray *datas = [NSMutableArray arrayWithArray:[groupUserDefaults arrayForKey:@"ACTIVE_SCRIPTS"]]; + NSMutableArray *datas = [NSMutableArray arrayWithArray:[SharedStorageManager shared].userscriptHeaders.content]; NSString *uuid = message[@"uuid"]; NSString *key = message[@"key"]; if (datas != NULL && datas.count > 0) { @@ -142,7 +201,7 @@ - (void)beginRequestWithExtensionContext:(NSExtensionContext *)context } } else if ([message[@"type"] isEqualToString:@"GM_getAllResourceUrl"]){ - NSMutableArray *datas = [NSMutableArray arrayWithArray:[groupUserDefaults arrayForKey:@"ACTIVE_SCRIPTS"]]; + NSMutableArray *datas = [NSMutableArray arrayWithArray:[SharedStorageManager shared].userscriptHeaders.content]; NSString *uuid = message[@"uuid"]; NSString *key = message[@"key"]; if (datas != NULL && datas.count > 0) { @@ -157,7 +216,6 @@ - (void)beginRequestWithExtensionContext:(NSExtensionContext *)context } } } - response.userInfo = @{ SFExtensionMessageKey: @{ @"type": message[@"type"], @"body": body == nil ? [NSNull null]:body, @@ -167,6 +225,13 @@ - (void)beginRequestWithExtensionContext:(NSExtensionContext *)context } +- (NSString *)getContentWithUUID:(NSString *)uuid{ + UserscriptInfo *info = [[SharedStorageManager shared] getInfoOfUUID:uuid]; + return info.content[@"content"]; + return nil; +} + + - (NSArray *)getUserScriptRequireListByUserScript:(NSDictionary *)scrpit { if(scrpit != nil && scrpit[@"requireUrls"] != nil){ NSArray *array = scrpit[@"requireUrls"]; diff --git a/Stay.xcodeproj/project.pbxproj b/Stay.xcodeproj/project.pbxproj old mode 100755 new mode 100644 index 2269d8f8..0bad5265 --- a/Stay.xcodeproj/project.pbxproj +++ b/Stay.xcodeproj/project.pbxproj @@ -8,6 +8,9 @@ /* Begin PBXBuildFile section */ 15C79AB327912BC600559EA5 /* message.js in Resources */ = {isa = PBXBuildFile; fileRef = 15C79AB227912BC600559EA5 /* message.js */; }; + 15F20BD1281BD09300330A56 /* checkUserscript.js in Resources */ = {isa = PBXBuildFile; fileRef = 15F20BD0281BD09300330A56 /* checkUserscript.js */; }; + 15F20BD4281BD24800330A56 /* parse-user-script.js in Resources */ = {isa = PBXBuildFile; fileRef = 15F20BD2281BD24800330A56 /* parse-user-script.js */; }; + 15F20BD5281BD24800330A56 /* parse-meta-line.js in Resources */ = {isa = PBXBuildFile; fileRef = 15F20BD3281BD24800330A56 /* parse-meta-line.js */; }; 582F9DE2276F329800C88135 /* jquery-3.5.1.min.js in Resources */ = {isa = PBXBuildFile; fileRef = 582F9DE1276F329800C88135 /* jquery-3.5.1.min.js */; }; 582F9DE4276F32B900C88135 /* base64.min.js in Resources */ = {isa = PBXBuildFile; fileRef = 582F9DE3276F32B900C88135 /* base64.min.js */; }; 5862157E277EE75C00AF8B11 /* jquery.cookie.min.js in Resources */ = {isa = PBXBuildFile; fileRef = 5862157D277EE75C00AF8B11 /* jquery.cookie.min.js */; }; @@ -38,6 +41,16 @@ 5889731A27213C8300BE123E /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5889731C27213C8300BE123E /* Localizable.strings */; }; 58897320272156C900BE123E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5889731F272156C900BE123E /* LaunchScreen.storyboard */; }; 5889733E2723033600BE123E /* icon@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 5889733D2723033600BE123E /* icon@3x.png */; }; + 588B831C2845F7BF00AB2D73 /* FCDisk.m in Sources */ = {isa = PBXBuildFile; fileRef = 588B831B2845F7BF00AB2D73 /* FCDisk.m */; }; + 588B831F2845FBFC00AB2D73 /* SharedStorageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 588B831E2845FBFC00AB2D73 /* SharedStorageManager.m */; }; + 588B83202845FBFC00AB2D73 /* SharedStorageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 588B831E2845FBFC00AB2D73 /* SharedStorageManager.m */; }; + 588B83212845FC0C00AB2D73 /* FCDisk.m in Sources */ = {isa = PBXBuildFile; fileRef = 588B831B2845F7BF00AB2D73 /* FCDisk.m */; }; + 588B8324284603FF00AB2D73 /* UserscriptHeaders.m in Sources */ = {isa = PBXBuildFile; fileRef = 588B8323284603FF00AB2D73 /* UserscriptHeaders.m */; }; + 588B8325284603FF00AB2D73 /* UserscriptHeaders.m in Sources */ = {isa = PBXBuildFile; fileRef = 588B8323284603FF00AB2D73 /* UserscriptHeaders.m */; }; + 588B83282846147000AB2D73 /* UserscriptInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 588B83272846147000AB2D73 /* UserscriptInfo.m */; }; + 588B83292846147000AB2D73 /* UserscriptInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 588B83272846147000AB2D73 /* UserscriptInfo.m */; }; + 588B832C284620F700AB2D73 /* ActivateChanged.m in Sources */ = {isa = PBXBuildFile; fileRef = 588B832B284620F700AB2D73 /* ActivateChanged.m */; }; + 588B832D284620F700AB2D73 /* ActivateChanged.m in Sources */ = {isa = PBXBuildFile; fileRef = 588B832B284620F700AB2D73 /* ActivateChanged.m */; }; 58F7F4B827438CB000270873 /* bootstrap.js in Resources */ = {isa = PBXBuildFile; fileRef = 58F7F4B727438CB000270873 /* bootstrap.js */; }; 58F7F4BB2743A93E00270873 /* parse-user-script.js in Resources */ = {isa = PBXBuildFile; fileRef = 58F7F4BA2743A93E00270873 /* parse-user-script.js */; }; 58F7F4BE2743AABE00270873 /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 58F7F4BD2743AABE00270873 /* JavaScriptCore.framework */; }; @@ -56,6 +69,20 @@ 58F7F4E6274754B900270873 /* gm-apis-gen.js in Resources */ = {isa = PBXBuildFile; fileRef = 58F7F4E5274754B900270873 /* gm-apis-gen.js */; }; 58F7F4E9274776AB00270873 /* Stroge.m in Sources */ = {isa = PBXBuildFile; fileRef = 58F7F4E8274776AB00270873 /* Stroge.m */; }; 58F7F4ED2747A9FE00270873 /* gm-api-create.js in Resources */ = {isa = PBXBuildFile; fileRef = 58F7F4EC2747A9FE00270873 /* gm-api-create.js */; }; + 58FEB1E0283B55C500BC2A92 /* FCSlideController.m in Sources */ = {isa = PBXBuildFile; fileRef = 58FEB1DD283B55C400BC2A92 /* FCSlideController.m */; }; + 58FEB1E1283B55C500BC2A92 /* FCBlockView.m in Sources */ = {isa = PBXBuildFile; fileRef = 58FEB1DF283B55C500BC2A92 /* FCBlockView.m */; }; + 58FEB1E4283B561B00BC2A92 /* FCView.m in Sources */ = {isa = PBXBuildFile; fileRef = 58FEB1E2283B561A00BC2A92 /* FCView.m */; }; + 58FEB1E7283B563000BC2A92 /* FCRoundedShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 58FEB1E6283B563000BC2A92 /* FCRoundedShadowView.m */; }; + 58FEB1EB283B57BB00BC2A92 /* FCStyle.m in Sources */ = {isa = PBXBuildFile; fileRef = 58FEB1EA283B57BB00BC2A92 /* FCStyle.m */; }; + 58FEB1EE283B5AE100BC2A92 /* FCApp.m in Sources */ = {isa = PBXBuildFile; fileRef = 58FEB1ED283B5AE100BC2A92 /* FCApp.m */; }; + 58FEB1F8283B5B3E00BC2A92 /* ModalNavigationBar.m in Sources */ = {isa = PBXBuildFile; fileRef = 58FEB1F0283B5B3D00BC2A92 /* ModalNavigationBar.m */; }; + 58FEB1F9283B5B3E00BC2A92 /* ModalNavigationItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 58FEB1F1283B5B3D00BC2A92 /* ModalNavigationItem.m */; }; + 58FEB1FA283B5B3E00BC2A92 /* ModalViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 58FEB1F2283B5B3D00BC2A92 /* ModalViewController.m */; }; + 58FEB1FB283B5B3E00BC2A92 /* ModalNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 58FEB1F5283B5B3D00BC2A92 /* ModalNavigationController.m */; }; + 58FEB1FE283B5BF900BC2A92 /* LoadingSlideController.m in Sources */ = {isa = PBXBuildFile; fileRef = 58FEB1FD283B5BF900BC2A92 /* LoadingSlideController.m */; }; + 58FEB202283B5D3F00BC2A92 /* LoadingStatusModalViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 58FEB201283B5D3F00BC2A92 /* LoadingStatusModalViewController.m */; }; + 58FEB209283E072C00BC2A92 /* MatchPattern.m in Sources */ = {isa = PBXBuildFile; fileRef = 58FEB208283E072B00BC2A92 /* MatchPattern.m */; }; + 6196D6DAD37ED57CA6DA1A01 /* libPods-Stay.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8324085BF6D1637082EC8C6B /* libPods-Stay.a */; }; 8410C840275E028A005531EC /* codemirror.js in Resources */ = {isa = PBXBuildFile; fileRef = 8410C837275E028A005531EC /* codemirror.js */; }; 8410C841275E028A005531EC /* typescript.html in Resources */ = {isa = PBXBuildFile; fileRef = 8410C83A275E028A005531EC /* typescript.html */; }; 8410C842275E028A005531EC /* index.html in Resources */ = {isa = PBXBuildFile; fileRef = 8410C83B275E028A005531EC /* index.html */; }; @@ -75,6 +102,11 @@ 8496DFBA275935B30075ABD5 /* add@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8496DFB9275935B30075ABD5 /* add@3x.png */; }; 8496DFC32759CADC0075ABD5 /* SYEditViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8496DFC22759CADC0075ABD5 /* SYEditViewController.m */; }; 849E203527B0AF7C000E5C02 /* UserscriptUpdateManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 849E203427B0AF7C000E5C02 /* UserscriptUpdateManager.m */; }; + 84A15E01282A4FFF002E121D /* BrowseView.m in Sources */ = {isa = PBXBuildFile; fileRef = 84A15E00282A4FFF002E121D /* BrowseView.m */; }; + 84A15E04282CE1FC002E121D /* addedScript@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 84A15E02282CE1FC002E121D /* addedScript@3x.png */; }; + 84A15E05282CE1FC002E121D /* addScript@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 84A15E03282CE1FC002E121D /* addScript@3x.png */; }; + 84A15E08282DEB2D002E121D /* ScriptMananger.m in Sources */ = {isa = PBXBuildFile; fileRef = 84A15E07282DEB2C002E121D /* ScriptMananger.m */; }; + 84A15E0B282DF0D2002E121D /* ScriptEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = 84A15E0A282DF0D2002E121D /* ScriptEntity.m */; }; 84AC68D127E434E600F356DF /* SYNotesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 84AC68D027E434E500F356DF /* SYNotesViewController.m */; }; 84BDC83C2745575E005367C6 /* UIViewExt.m in Sources */ = {isa = PBXBuildFile; fileRef = 84BDC83B2745575E005367C6 /* UIViewExt.m */; }; 84BDC843274BB820005367C6 /* DataManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 84BDC842274BB820005367C6 /* DataManager.m */; }; @@ -141,7 +173,11 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 061AFC211A05E3B5F4B9737A /* Pods-Stay.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Stay.release.xcconfig"; path = "Target Support Files/Pods-Stay/Pods-Stay.release.xcconfig"; sourceTree = ""; }; 15C79AB227912BC600559EA5 /* message.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = message.js; sourceTree = ""; }; + 15F20BD0281BD09300330A56 /* checkUserscript.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = checkUserscript.js; sourceTree = ""; }; + 15F20BD2281BD24800330A56 /* parse-user-script.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = "parse-user-script.js"; sourceTree = ""; }; + 15F20BD3281BD24800330A56 /* parse-meta-line.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = "parse-meta-line.js"; sourceTree = ""; }; 582F9DE1276F329800C88135 /* jquery-3.5.1.min.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = "jquery-3.5.1.min.js"; sourceTree = ""; }; 582F9DE3276F32B900C88135 /* base64.min.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = base64.min.js; sourceTree = ""; }; 5862157D277EE75C00AF8B11 /* jquery.cookie.min.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = jquery.cookie.min.js; sourceTree = ""; }; @@ -183,6 +219,16 @@ 5889731E27213CB100BE123E /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; 5889731F272156C900BE123E /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; 5889733D2723033600BE123E /* icon@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon@3x.png"; sourceTree = ""; }; + 588B831A2845F7BE00AB2D73 /* FCDisk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FCDisk.h; sourceTree = ""; }; + 588B831B2845F7BF00AB2D73 /* FCDisk.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FCDisk.m; sourceTree = ""; }; + 588B831D2845FBFC00AB2D73 /* SharedStorageManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SharedStorageManager.h; sourceTree = ""; }; + 588B831E2845FBFC00AB2D73 /* SharedStorageManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SharedStorageManager.m; sourceTree = ""; }; + 588B8322284603FF00AB2D73 /* UserscriptHeaders.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UserscriptHeaders.h; sourceTree = ""; }; + 588B8323284603FF00AB2D73 /* UserscriptHeaders.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UserscriptHeaders.m; sourceTree = ""; }; + 588B83262846147000AB2D73 /* UserscriptInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UserscriptInfo.h; sourceTree = ""; }; + 588B83272846147000AB2D73 /* UserscriptInfo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UserscriptInfo.m; sourceTree = ""; }; + 588B832A284620F700AB2D73 /* ActivateChanged.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ActivateChanged.h; sourceTree = ""; }; + 588B832B284620F700AB2D73 /* ActivateChanged.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ActivateChanged.m; sourceTree = ""; }; 58F7F4B727438CB000270873 /* bootstrap.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = bootstrap.js; sourceTree = ""; }; 58F7F4BA2743A93E00270873 /* parse-user-script.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = "parse-user-script.js"; sourceTree = ""; }; 58F7F4BD2743AABE00270873 /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; @@ -206,6 +252,38 @@ 58F7F4E7274776AB00270873 /* Stroge.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Stroge.h; sourceTree = ""; }; 58F7F4E8274776AB00270873 /* Stroge.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Stroge.m; sourceTree = ""; }; 58F7F4EC2747A9FE00270873 /* gm-api-create.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = "gm-api-create.js"; sourceTree = ""; }; + 58FEB1DB283B55C400BC2A92 /* FCSlideController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FCSlideController.h; sourceTree = ""; }; + 58FEB1DC283B55C400BC2A92 /* FCPresenting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FCPresenting.h; sourceTree = ""; }; + 58FEB1DD283B55C400BC2A92 /* FCSlideController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FCSlideController.m; sourceTree = ""; }; + 58FEB1DE283B55C400BC2A92 /* FCBlockView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FCBlockView.h; sourceTree = ""; }; + 58FEB1DF283B55C500BC2A92 /* FCBlockView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FCBlockView.m; sourceTree = ""; }; + 58FEB1E2283B561A00BC2A92 /* FCView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FCView.m; sourceTree = ""; }; + 58FEB1E3283B561B00BC2A92 /* FCView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FCView.h; sourceTree = ""; }; + 58FEB1E5283B563000BC2A92 /* FCRoundedShadowView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FCRoundedShadowView.h; sourceTree = ""; }; + 58FEB1E6283B563000BC2A92 /* FCRoundedShadowView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FCRoundedShadowView.m; sourceTree = ""; }; + 58FEB1E9283B57BB00BC2A92 /* FCStyle.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FCStyle.h; sourceTree = ""; }; + 58FEB1EA283B57BB00BC2A92 /* FCStyle.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FCStyle.m; sourceTree = ""; }; + 58FEB1EC283B5AE100BC2A92 /* FCApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FCApp.h; sourceTree = ""; }; + 58FEB1ED283B5AE100BC2A92 /* FCApp.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FCApp.m; sourceTree = ""; }; + 58FEB1F0283B5B3D00BC2A92 /* ModalNavigationBar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ModalNavigationBar.m; sourceTree = ""; }; + 58FEB1F1283B5B3D00BC2A92 /* ModalNavigationItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ModalNavigationItem.m; sourceTree = ""; }; + 58FEB1F2283B5B3D00BC2A92 /* ModalViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ModalViewController.m; sourceTree = ""; }; + 58FEB1F3283B5B3D00BC2A92 /* ModalNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ModalNavigationController.h; sourceTree = ""; }; + 58FEB1F4283B5B3D00BC2A92 /* ModalNavigationBar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ModalNavigationBar.h; sourceTree = ""; }; + 58FEB1F5283B5B3D00BC2A92 /* ModalNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ModalNavigationController.m; sourceTree = ""; }; + 58FEB1F6283B5B3E00BC2A92 /* ModalNavigationItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ModalNavigationItem.h; sourceTree = ""; }; + 58FEB1F7283B5B3E00BC2A92 /* ModalViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ModalViewController.h; sourceTree = ""; }; + 58FEB1FC283B5BF900BC2A92 /* LoadingSlideController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LoadingSlideController.h; sourceTree = ""; }; + 58FEB1FD283B5BF900BC2A92 /* LoadingSlideController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LoadingSlideController.m; sourceTree = ""; }; + 58FEB200283B5D3F00BC2A92 /* LoadingStatusModalViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LoadingStatusModalViewController.h; sourceTree = ""; }; + 58FEB201283B5D3F00BC2A92 /* LoadingStatusModalViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LoadingStatusModalViewController.m; sourceTree = ""; }; + 58FEB203283B636900BC2A92 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.html; name = ja; path = ../ja.lproj/Main.html; sourceTree = ""; }; + 58FEB204283B636900BC2A92 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = ""; }; + 58FEB205283B647500BC2A92 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.html; name = "zh-Hant"; path = "../zh-Hant.lproj/Main.html"; sourceTree = ""; }; + 58FEB206283B647500BC2A92 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = ""; }; + 58FEB207283E072B00BC2A92 /* MatchPattern.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MatchPattern.h; sourceTree = ""; }; + 58FEB208283E072B00BC2A92 /* MatchPattern.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MatchPattern.m; sourceTree = ""; }; + 8324085BF6D1637082EC8C6B /* libPods-Stay.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Stay.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 8410C837275E028A005531EC /* codemirror.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = codemirror.js; sourceTree = ""; }; 8410C83A275E028A005531EC /* typescript.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = typescript.html; sourceTree = ""; }; 8410C83B275E028A005531EC /* index.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = index.html; sourceTree = ""; }; @@ -233,6 +311,14 @@ 8496DFC4275A6D7E0075ABD5 /* Stay-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Stay-Bridging-Header.h"; sourceTree = ""; }; 849E203327B0AF7C000E5C02 /* UserscriptUpdateManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UserscriptUpdateManager.h; sourceTree = ""; }; 849E203427B0AF7C000E5C02 /* UserscriptUpdateManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UserscriptUpdateManager.m; sourceTree = ""; }; + 84A15DFF282A4FFF002E121D /* BrowseView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BrowseView.h; sourceTree = ""; }; + 84A15E00282A4FFF002E121D /* BrowseView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BrowseView.m; sourceTree = ""; }; + 84A15E02282CE1FC002E121D /* addedScript@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "addedScript@3x.png"; sourceTree = ""; }; + 84A15E03282CE1FC002E121D /* addScript@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "addScript@3x.png"; sourceTree = ""; }; + 84A15E06282DEB2C002E121D /* ScriptMananger.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScriptMananger.h; sourceTree = ""; }; + 84A15E07282DEB2C002E121D /* ScriptMananger.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScriptMananger.m; sourceTree = ""; }; + 84A15E09282DF0D2002E121D /* ScriptEntity.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScriptEntity.h; sourceTree = ""; }; + 84A15E0A282DF0D2002E121D /* ScriptEntity.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScriptEntity.m; sourceTree = ""; }; 84AC68CF27E434E500F356DF /* SYNotesViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SYNotesViewController.h; sourceTree = ""; }; 84AC68D027E434E500F356DF /* SYNotesViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SYNotesViewController.m; sourceTree = ""; }; 84BDC83A2745575E005367C6 /* UIViewExt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIViewExt.h; sourceTree = ""; }; @@ -272,6 +358,7 @@ 84F48336273B709500217BB3 /* JSDetailCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JSDetailCell.m; sourceTree = ""; }; 84F48339273B964900217BB3 /* JSDetailEntity.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JSDetailEntity.m; sourceTree = ""; }; 84F4833B273B96F400217BB3 /* JSDetailEntity.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSDetailEntity.h; sourceTree = ""; }; + B8B67DF96D5260DD0072C091 /* Pods-Stay.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Stay.debug.xcconfig"; path = "Target Support Files/Pods-Stay/Pods-Stay.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -280,6 +367,7 @@ buildActionMask = 2147483647; files = ( 58F7F4BE2743AABE00270873 /* JavaScriptCore.framework in Frameworks */, + 6196D6DAD37ED57CA6DA1A01 /* libPods-Stay.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -316,6 +404,7 @@ 587689102719238000F834A4 /* Stay Extension */, 587688D52719237E00F834A4 /* Products */, 58F7F4BC2743AABE00270873 /* Frameworks */, + 61471BD8CEEC7081650F0787 /* Pods */, ); sourceTree = ""; }; @@ -392,6 +481,8 @@ 58F7F4E8274776AB00270873 /* Stroge.m */, 587689112719238000F834A4 /* SafariWebExtensionHandler.h */, 587689122719238000F834A4 /* SafariWebExtensionHandler.m */, + 58FEB207283E072B00BC2A92 /* MatchPattern.h */, + 58FEB208283E072B00BC2A92 /* MatchPattern.m */, 587689252719238000F834A4 /* Info.plist */, 587689142719238000F834A4 /* Resources */, ); @@ -401,6 +492,9 @@ 587689142719238000F834A4 /* Resources */ = { isa = PBXGroup; children = ( + 15F20BD3281BD24800330A56 /* parse-meta-line.js */, + 15F20BD2281BD24800330A56 /* parse-user-script.js */, + 15F20BD0281BD09300330A56 /* checkUserscript.js */, 15C79AB227912BC600559EA5 /* message.js */, 58F7F4E02746588600270873 /* vendor */, 587689152719238000F834A4 /* _locales */, @@ -422,6 +516,8 @@ 5889733A2722FF7600BE123E /* Images */ = { isa = PBXGroup; children = ( + 84A15E02282CE1FC002E121D /* addedScript@3x.png */, + 84A15E03282CE1FC002E121D /* addScript@3x.png */, 8457A84A280276010010A387 /* webback@3x.png */, 84C1ED51277AE1B500651DF9 /* search-selected@3x.png */, 84C1ED4F277AD93300651DF9 /* arrow-dark@3x.png */, @@ -461,6 +557,7 @@ isa = PBXGroup; children = ( 58F7F4BD2743AABE00270873 /* JavaScriptCore.framework */, + 8324085BF6D1637082EC8C6B /* libPods-Stay.a */, ); name = Frameworks; sourceTree = ""; @@ -514,6 +611,80 @@ path = vendor; sourceTree = ""; }; + 58FEB1D9283B54C600BC2A92 /* Controls */ = { + isa = PBXGroup; + children = ( + 588B831A2845F7BE00AB2D73 /* FCDisk.h */, + 588B831B2845F7BF00AB2D73 /* FCDisk.m */, + 58FEB1FF283B5C1D00BC2A92 /* Loading */, + 58FEB1EF283B5B2700BC2A92 /* Modal */, + 58FEB1EC283B5AE100BC2A92 /* FCApp.h */, + 58FEB1ED283B5AE100BC2A92 /* FCApp.m */, + 58FEB1E3283B561B00BC2A92 /* FCView.h */, + 58FEB1E2283B561A00BC2A92 /* FCView.m */, + 58FEB1DA283B559D00BC2A92 /* SlideController */, + ); + path = Controls; + sourceTree = ""; + }; + 58FEB1DA283B559D00BC2A92 /* SlideController */ = { + isa = PBXGroup; + children = ( + 58FEB1DE283B55C400BC2A92 /* FCBlockView.h */, + 58FEB1DF283B55C500BC2A92 /* FCBlockView.m */, + 58FEB1DC283B55C400BC2A92 /* FCPresenting.h */, + 58FEB1DB283B55C400BC2A92 /* FCSlideController.h */, + 58FEB1DD283B55C400BC2A92 /* FCSlideController.m */, + 58FEB1E5283B563000BC2A92 /* FCRoundedShadowView.h */, + 58FEB1E6283B563000BC2A92 /* FCRoundedShadowView.m */, + ); + path = SlideController; + sourceTree = ""; + }; + 58FEB1E8283B57A400BC2A92 /* Style */ = { + isa = PBXGroup; + children = ( + 58FEB1E9283B57BB00BC2A92 /* FCStyle.h */, + 58FEB1EA283B57BB00BC2A92 /* FCStyle.m */, + ); + path = Style; + sourceTree = ""; + }; + 58FEB1EF283B5B2700BC2A92 /* Modal */ = { + isa = PBXGroup; + children = ( + 58FEB1F4283B5B3D00BC2A92 /* ModalNavigationBar.h */, + 58FEB1F0283B5B3D00BC2A92 /* ModalNavigationBar.m */, + 58FEB1F3283B5B3D00BC2A92 /* ModalNavigationController.h */, + 58FEB1F5283B5B3D00BC2A92 /* ModalNavigationController.m */, + 58FEB1F6283B5B3E00BC2A92 /* ModalNavigationItem.h */, + 58FEB1F1283B5B3D00BC2A92 /* ModalNavigationItem.m */, + 58FEB1F7283B5B3E00BC2A92 /* ModalViewController.h */, + 58FEB1F2283B5B3D00BC2A92 /* ModalViewController.m */, + ); + path = Modal; + sourceTree = ""; + }; + 58FEB1FF283B5C1D00BC2A92 /* Loading */ = { + isa = PBXGroup; + children = ( + 58FEB1FC283B5BF900BC2A92 /* LoadingSlideController.h */, + 58FEB1FD283B5BF900BC2A92 /* LoadingSlideController.m */, + 58FEB200283B5D3F00BC2A92 /* LoadingStatusModalViewController.h */, + 58FEB201283B5D3F00BC2A92 /* LoadingStatusModalViewController.m */, + ); + path = Loading; + sourceTree = ""; + }; + 61471BD8CEEC7081650F0787 /* Pods */ = { + isa = PBXGroup; + children = ( + B8B67DF96D5260DD0072C091 /* Pods-Stay.debug.xcconfig */, + 061AFC211A05E3B5F4B9737A /* Pods-Stay.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; 8410C835275E0278005531EC /* CodeMirror */ = { isa = PBXGroup; children = ( @@ -582,12 +753,29 @@ path = controller; sourceTree = ""; }; + 84A15DFE282A4FCE002E121D /* View */ = { + isa = PBXGroup; + children = ( + 84A15DFF282A4FFF002E121D /* BrowseView.h */, + 84A15E00282A4FFF002E121D /* BrowseView.m */, + ); + path = View; + sourceTree = ""; + }; 84BDC83F274BB7FB005367C6 /* Data */ = { isa = PBXGroup; children = ( 84C1ED47276C637200651DF9 /* scriptManager.sqlite */, 84BDC846274BC50B005367C6 /* DataModel */, 84BDC840274BB80B005367C6 /* DataManager */, + 588B831D2845FBFC00AB2D73 /* SharedStorageManager.h */, + 588B831E2845FBFC00AB2D73 /* SharedStorageManager.m */, + 588B8322284603FF00AB2D73 /* UserscriptHeaders.h */, + 588B8323284603FF00AB2D73 /* UserscriptHeaders.m */, + 588B83262846147000AB2D73 /* UserscriptInfo.h */, + 588B83272846147000AB2D73 /* UserscriptInfo.m */, + 588B832A284620F700AB2D73 /* ActivateChanged.h */, + 588B832B284620F700AB2D73 /* ActivateChanged.m */, ); path = Data; sourceTree = ""; @@ -632,6 +820,8 @@ 84F4830D273A69AA00217BB3 /* Main */ = { isa = PBXGroup; children = ( + 58FEB1E8283B57A400BC2A92 /* Style */, + 58FEB1D9283B54C600BC2A92 /* Controls */, 8410C835275E0278005531EC /* CodeMirror */, 8496DFBD2759CA750075ABD5 /* SYEdit */, 84C54CF62753BD4B00A9F14C /* SYDetail */, @@ -690,6 +880,7 @@ 84F48312273A6A2100217BB3 /* SYSearch */ = { isa = PBXGroup; children = ( + 84A15DFE282A4FCE002E121D /* View */, 84F48313273A6C4C00217BB3 /* Controller */, ); path = SYSearch; @@ -714,10 +905,14 @@ 84BDC83B2745575E005367C6 /* UIViewExt.m */, 848A48B4279A4C7800D55FFD /* SYNetworkUtils.h */, 848A48B7279A5BA000D55FFD /* SYVersionUtils.h */, - 848A48B8279A5BA000D55FFD /* SYVersionUtils.m */, 848A48B5279A4C7800D55FFD /* SYNetworkUtils.m */, 849E203327B0AF7C000E5C02 /* UserscriptUpdateManager.h */, + 848A48B8279A5BA000D55FFD /* SYVersionUtils.m */, 849E203427B0AF7C000E5C02 /* UserscriptUpdateManager.m */, + 84A15E06282DEB2C002E121D /* ScriptMananger.h */, + 84A15E07282DEB2C002E121D /* ScriptMananger.m */, + 84A15E09282DF0D2002E121D /* ScriptEntity.h */, + 84A15E0A282DF0D2002E121D /* ScriptEntity.m */, ); path = Common; sourceTree = ""; @@ -746,6 +941,7 @@ isa = PBXNativeTarget; buildConfigurationList = 5876892D2719238000F834A4 /* Build configuration list for PBXNativeTarget "Stay" */; buildPhases = ( + 0A3ACB5B24D9B9A165231F4F /* [CP] Check Pods Manifest.lock */, 587688D02719237E00F834A4 /* Sources */, 587688D12719237E00F834A4 /* Frameworks */, 587688D22719237E00F834A4 /* Resources */, @@ -848,6 +1044,8 @@ en, Base, "zh-Hans", + ja, + "zh-Hant", ); mainGroup = 587688CB2719237E00F834A4; productRefGroup = 587688D52719237E00F834A4 /* Products */; @@ -887,6 +1085,7 @@ 58F7F4D627461F9A00270873 /* stay-taskloop.js in Resources */, 8410C843275E028A005531EC /* test.js in Resources */, 58F7F4CD27454E7300270873 /* stay-bootstrap.js in Resources */, + 84A15E05282CE1FC002E121D /* addScript@3x.png in Resources */, 84C1ED4E277AD26700651DF9 /* base16-dark.css in Resources */, 8496DFBA275935B30075ABD5 /* add@3x.png in Resources */, 8410C845275E028A005531EC /* json-ld.html in Resources */, @@ -894,6 +1093,7 @@ 5889731A27213C8300BE123E /* Localizable.strings in Resources */, 58F7F4C12743C00600270873 /* leetcode-cn-submission-share.user.js in Resources */, 58897320272156C900BE123E /* LaunchScreen.storyboard in Resources */, + 84A15E04282CE1FC002E121D /* addedScript@3x.png in Resources */, 84C1ED2327659F7600651DF9 /* arrow@3x.png in Resources */, 58F7F4ED2747A9FE00270873 /* gm-api-create.js in Resources */, 84C1ED44276C31AE00651DF9 /* back@3x.png in Resources */, @@ -938,9 +1138,12 @@ 587689222719238000F834A4 /* popup.css in Resources */, 15C79AB327912BC600559EA5 /* message.js in Resources */, 587689202719238000F834A4 /* popup.html in Resources */, + 15F20BD1281BD09300330A56 /* checkUserscript.js in Resources */, 5881343127294C0E0055E1CE /* iframe.js in Resources */, 58F7F4DD274648FA00270873 /* MatchPattern.js in Resources */, 587689182719238000F834A4 /* images in Resources */, + 15F20BD4281BD24800330A56 /* parse-user-script.js in Resources */, + 15F20BD5281BD24800330A56 /* parse-meta-line.js in Resources */, 58F7F4E6274754B900270873 /* gm-apis-gen.js in Resources */, 5876891A2719238000F834A4 /* manifest.json in Resources */, 582F9DE2276F329800C88135 /* jquery-3.5.1.min.js in Resources */, @@ -953,21 +1156,58 @@ }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + 0A3ACB5B24D9B9A165231F4F /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Stay-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ 587688D02719237E00F834A4 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 58FEB1EB283B57BB00BC2A92 /* FCStyle.m in Sources */, + 58FEB1EE283B5AE100BC2A92 /* FCApp.m in Sources */, + 588B83282846147000AB2D73 /* UserscriptInfo.m in Sources */, 587688E72719237E00F834A4 /* SYMoreViewController.m in Sources */, 84C54CFC2753BDAF00A9F14C /* SYDetailViewController.m in Sources */, 84C1ED1D27631AE200651DF9 /* SYCodeMirrorView.m in Sources */, 84BDC843274BB820005367C6 /* DataManager.m in Sources */, + 58FEB1FE283B5BF900BC2A92 /* LoadingSlideController.m in Sources */, 8457A84927FEF0670010A387 /* SYWebScriptViewController.m in Sources */, 84BDC849274BC5AD005367C6 /* ScriptDetailModel.m in Sources */, + 84A15E0B282DF0D2002E121D /* ScriptEntity.m in Sources */, + 588B8324284603FF00AB2D73 /* UserscriptHeaders.m in Sources */, + 58FEB1F8283B5B3E00BC2A92 /* ModalNavigationBar.m in Sources */, + 58FEB1FB283B5B3E00BC2A92 /* ModalNavigationController.m in Sources */, + 58FEB1E4283B561B00BC2A92 /* FCView.m in Sources */, 84C1ED262765E29D00651DF9 /* NSString+Urlencode.m in Sources */, 848A48B6279A4C7900D55FFD /* SYNetworkUtils.m in Sources */, 587688D92719237E00F834A4 /* AppDelegate.m in Sources */, 84BDC83C2745575E005367C6 /* UIViewExt.m in Sources */, + 84A15E01282A4FFF002E121D /* BrowseView.m in Sources */, + 58FEB1FA283B5B3E00BC2A92 /* ModalViewController.m in Sources */, + 588B831C2845F7BF00AB2D73 /* FCDisk.m in Sources */, 84F48324273ACAD400217BB3 /* SYSearchViewController.m in Sources */, 84F48318273A739800217BB3 /* MainTabBarController.m in Sources */, 84F4833A273B964900217BB3 /* JSDetailEntity.m in Sources */, @@ -975,14 +1215,22 @@ 84F48337273B709500217BB3 /* JSDetailCell.m in Sources */, 848A48B9279A5BA000D55FFD /* SYVersionUtils.m in Sources */, 84AC68D127E434E600F356DF /* SYNotesViewController.m in Sources */, + 588B831F2845FBFC00AB2D73 /* SharedStorageManager.m in Sources */, 8457A84327FD398D0010A387 /* SYAddScriptController.m in Sources */, 8496DFC32759CADC0075ABD5 /* SYEditViewController.m in Sources */, 84F48321273ACAC000217BB3 /* SYHomeViewController.m in Sources */, + 58FEB1E0283B55C500BC2A92 /* FCSlideController.m in Sources */, 587688F12719238000F834A4 /* main.m in Sources */, + 58FEB1E1283B55C500BC2A92 /* FCBlockView.m in Sources */, 587688E42719237E00F834A4 /* SceneDelegate.m in Sources */, + 58FEB202283B5D3F00BC2A92 /* LoadingStatusModalViewController.m in Sources */, 58F7F4C52745336100270873 /* Tampermonkey.m in Sources */, 849E203527B0AF7C000E5C02 /* UserscriptUpdateManager.m in Sources */, + 58FEB1E7283B563000BC2A92 /* FCRoundedShadowView.m in Sources */, + 84A15E08282DEB2D002E121D /* ScriptMananger.m in Sources */, 8457A84627FE8CA50010A387 /* SYScriptAddViewController.m in Sources */, + 588B832C284620F700AB2D73 /* ActivateChanged.m in Sources */, + 58FEB1F9283B5B3E00BC2A92 /* ModalNavigationItem.m in Sources */, 58F7F4D22745FC0C00270873 /* UserScript.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1008,8 +1256,14 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 58FEB209283E072C00BC2A92 /* MatchPattern.m in Sources */, 587689132719238000F834A4 /* SafariWebExtensionHandler.m in Sources */, + 588B8325284603FF00AB2D73 /* UserscriptHeaders.m in Sources */, + 588B83212845FC0C00AB2D73 /* FCDisk.m in Sources */, + 588B83292846147000AB2D73 /* UserscriptInfo.m in Sources */, + 588B83202845FBFC00AB2D73 /* SharedStorageManager.m in Sources */, 58F7F4E9274776AB00270873 /* Stroge.m in Sources */, + 588B832D284620F700AB2D73 /* ActivateChanged.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1038,6 +1292,8 @@ isa = PBXVariantGroup; children = ( 5889731427213BC200BE123E /* en */, + 58FEB203283B636900BC2A92 /* ja */, + 58FEB205283B647500BC2A92 /* zh-Hant */, ); name = Main.html; sourceTree = ""; @@ -1047,6 +1303,8 @@ children = ( 5889731B27213C8300BE123E /* en */, 5889731E27213CB100BE123E /* zh-Hans */, + 58FEB204283B636900BC2A92 /* ja */, + 58FEB206283B647500BC2A92 /* zh-Hant */, ); name = Localizable.strings; sourceTree = ""; @@ -1242,6 +1500,7 @@ }; 5876892E2719238000F834A4 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = B8B67DF96D5260DD0072C091 /* Pods-Stay.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -1249,7 +1508,7 @@ CODE_SIGN_ENTITLEMENTS = Stay/Stay.entitlements; CODE_SIGN_STYLE = Automatic; COPY_HEADERS_RUN_UNIFDEF = NO; - CURRENT_PROJECT_VERSION = 32; + CURRENT_PROJECT_VERSION = 37; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = J25G7D7AL3; GCC_PREFIX_HEADER = "$(SRCROOT)/Stay/PrefixHeader.pch"; @@ -1271,7 +1530,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.0.6; + MARKETING_VERSION = 2.1.0; OTHER_LDFLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = com.dajiu.stay.pro; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1287,6 +1546,7 @@ }; 5876892F2719238000F834A4 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 061AFC211A05E3B5F4B9737A /* Pods-Stay.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -1294,7 +1554,7 @@ CODE_SIGN_ENTITLEMENTS = Stay/Stay.entitlements; CODE_SIGN_STYLE = Automatic; COPY_HEADERS_RUN_UNIFDEF = NO; - CURRENT_PROJECT_VERSION = 32; + CURRENT_PROJECT_VERSION = 37; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = J25G7D7AL3; GCC_PREFIX_HEADER = "$(SRCROOT)/Stay/PrefixHeader.pch"; @@ -1316,7 +1576,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.0.6; + MARKETING_VERSION = 2.1.0; OTHER_LDFLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = com.dajiu.stay.pro; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/Stay.xcworkspace/contents.xcworkspacedata b/Stay.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..be4879b2 --- /dev/null +++ b/Stay.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/Stay/AppDelegate.m b/Stay/AppDelegate.m index 4a668456..32c3ac55 100644 --- a/Stay/AppDelegate.m +++ b/Stay/AppDelegate.m @@ -9,6 +9,8 @@ #import "NavigationController.h" #import "SceneDelegate.h" #import "DataManager.h" +#import "IACManager.h" +#import "SYEditViewController.h" @implementation AppDelegate @@ -16,6 +18,31 @@ @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. + [IACManager sharedManager].callbackURLScheme = @"stay"; + [[IACManager sharedManager] handleAction:@"install" withBlock:^(NSDictionary *inputParameters, IACSuccessBlock success, IACFailureBlock failure) { + NSString *url = inputParameters[@"scriptURL"]; + NSString *decodeUrl = [url stringByRemovingPercentEncoding];//编码 + NSMutableCharacterSet *set = [[NSCharacterSet URLFragmentAllowedCharacterSet] mutableCopy]; + [set addCharactersInString:@"#"]; + NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:[decodeUrl stringByAddingPercentEncodingWithAllowedCharacters:set]]]; + dispatch_async(dispatch_get_main_queue(),^{ + + if(data != nil ) { + NSString *str = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]; + SYEditViewController *cer = [[SYEditViewController alloc] init]; + cer.content = str; + cer.downloadUrl = url; + UINavigationController *nav = [self getCurrentNCFrom:[UIApplication sharedApplication].keyWindow.rootViewController]; + [nav pushViewController:cer animated:true]; + } + }); + + }]; + +// NSUserDefaults *groupUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.dajiu.stay.pro"]; +// +// NSMutableArray *datas = [NSMutableArray arrayWithArray:[groupUserDefaults arrayForKey:@"STAY_SCRIPTS"]]; + return YES; } @@ -23,7 +50,33 @@ - (UISceneConfiguration *)application:(UIApplication *)application configuration return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role]; } +//递归 +- (UINavigationController *)getCurrentNCFrom:(UIViewController *)vc +{ + if ([vc isKindOfClass:[UITabBarController class]]) { + UINavigationController *nc = ((UITabBarController *)vc).selectedViewController; + return [self getCurrentNCFrom:nc]; + } + else if ([vc isKindOfClass:[UINavigationController class]]) { + if (((UINavigationController *)vc).presentedViewController) { + return [self getCurrentNCFrom:((UINavigationController *)vc).presentedViewController]; + } + return [self getCurrentNCFrom:((UINavigationController *)vc).topViewController]; + } + else if ([vc isKindOfClass:[UIViewController class]]) { + if (vc.presentedViewController) { + return [self getCurrentNCFrom:vc.presentedViewController]; + } + else { + return vc.navigationController; + } + } + else { + NSAssert(0, @"未获取到导航控制器"); + return nil; + } +} + -//- applicationDidBecomeActive: @end diff --git a/Stay/Assets.xcassets/AccentClassicColor.colorset/Contents.json b/Stay/Assets.xcassets/AccentClassicColor.colorset/Contents.json new file mode 100644 index 00000000..283a3168 --- /dev/null +++ b/Stay/Assets.xcassets/AccentClassicColor.colorset/Contents.json @@ -0,0 +1,23 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "1.000", + "blue" : "0xE0", + "green" : "0x20", + "red" : "0xB6" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "localizable" : true + } +} diff --git a/Stay/Assets.xcassets/AccentColor.colorset/Contents.json b/Stay/Assets.xcassets/AccentColor.colorset/Contents.json deleted file mode 100644 index eb878970..00000000 --- a/Stay/Assets.xcassets/AccentColor.colorset/Contents.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "colors" : [ - { - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Stay/Assets.xcassets/BackgroundColor.colorset/Contents.json b/Stay/Assets.xcassets/BackgroundColor.colorset/Contents.json new file mode 100644 index 00000000..1ff70282 --- /dev/null +++ b/Stay/Assets.xcassets/BackgroundColor.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.965", + "green" : "0.965", + "red" : "0.965" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.078", + "green" : "0.078", + "red" : "0.078" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Stay/Assets.xcassets/BackgroundGoldenColor.colorset/Contents.json b/Stay/Assets.xcassets/BackgroundGoldenColor.colorset/Contents.json new file mode 100644 index 00000000..4cd8b828 --- /dev/null +++ b/Stay/Assets.xcassets/BackgroundGoldenColor.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.553", + "green" : "0.875", + "red" : "0.976" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Stay/Assets.xcassets/BorderColor.colorset/Contents.json b/Stay/Assets.xcassets/BorderColor.colorset/Contents.json new file mode 100644 index 00000000..5e28964d --- /dev/null +++ b/Stay/Assets.xcassets/BorderColor.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.933", + "green" : "0.933", + "red" : "0.933" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.141", + "green" : "0.141", + "red" : "0.141" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Stay/Assets.xcassets/BorderGoldenColor.colorset/Contents.json b/Stay/Assets.xcassets/BorderGoldenColor.colorset/Contents.json new file mode 100644 index 00000000..8710347f --- /dev/null +++ b/Stay/Assets.xcassets/BorderGoldenColor.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.420", + "green" : "0.710", + "red" : "0.835" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Stay/Assets.xcassets/DefaultBackgroundColor.colorset/Contents.json b/Stay/Assets.xcassets/DefaultBackgroundColor.colorset/Contents.json new file mode 100644 index 00000000..960aeadc --- /dev/null +++ b/Stay/Assets.xcassets/DefaultBackgroundColor.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "1.000", + "green" : "1.000", + "red" : "1.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.098", + "green" : "0.098", + "red" : "0.098" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Stay/Assets.xcassets/FCBlackColor.colorset/Contents.json b/Stay/Assets.xcassets/FCBlackColor.colorset/Contents.json new file mode 100644 index 00000000..d8907191 --- /dev/null +++ b/Stay/Assets.xcassets/FCBlackColor.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.000", + "green" : "0.000", + "red" : "0.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "1.000", + "green" : "1.000", + "red" : "1.000" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Stay/Assets.xcassets/FCBlueColor.colorset/Contents.json b/Stay/Assets.xcassets/FCBlueColor.colorset/Contents.json new file mode 100644 index 00000000..7809d05c --- /dev/null +++ b/Stay/Assets.xcassets/FCBlueColor.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.965", + "green" : "0.482", + "red" : "0.200" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Stay/Assets.xcassets/FCGoldenColor.colorset/Contents.json b/Stay/Assets.xcassets/FCGoldenColor.colorset/Contents.json new file mode 100644 index 00000000..c50287aa --- /dev/null +++ b/Stay/Assets.xcassets/FCGoldenColor.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.114", + "green" : "0.337", + "red" : "0.518" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Stay/Assets.xcassets/FCSecondaryBlackColor.colorset/Contents.json b/Stay/Assets.xcassets/FCSecondaryBlackColor.colorset/Contents.json new file mode 100644 index 00000000..2fad47ad --- /dev/null +++ b/Stay/Assets.xcassets/FCSecondaryBlackColor.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.478", + "green" : "0.478", + "red" : "0.478" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.439", + "green" : "0.439", + "red" : "0.439" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Stay/Assets.xcassets/FCSeparatorColor.colorset/Contents.json b/Stay/Assets.xcassets/FCSeparatorColor.colorset/Contents.json new file mode 100644 index 00000000..75d4a0e9 --- /dev/null +++ b/Stay/Assets.xcassets/FCSeparatorColor.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.871", + "green" : "0.871", + "red" : "0.871" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.161", + "green" : "0.161", + "red" : "0.161" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Stay/Assets.xcassets/FCShadowLineColor.colorset/Contents.json b/Stay/Assets.xcassets/FCShadowLineColor.colorset/Contents.json new file mode 100644 index 00000000..9a141bbc --- /dev/null +++ b/Stay/Assets.xcassets/FCShadowLineColor.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.910", + "green" : "0.910", + "red" : "0.910" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.000", + "green" : "0.000", + "red" : "0.000" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Stay/Assets.xcassets/LargeIcon.imageset/Contents.json b/Stay/Assets.xcassets/LargeIcon.imageset/Contents.json deleted file mode 100644 index a19a5492..00000000 --- a/Stay/Assets.xcassets/LargeIcon.imageset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Stay/Assets.xcassets/PlaceholderColor.colorset/Contents.json b/Stay/Assets.xcassets/PlaceholderColor.colorset/Contents.json new file mode 100644 index 00000000..f9e41013 --- /dev/null +++ b/Stay/Assets.xcassets/PlaceholderColor.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.902", + "green" : "0.902", + "red" : "0.902" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Stay/Assets.xcassets/PopupColor.colorset/Contents.json b/Stay/Assets.xcassets/PopupColor.colorset/Contents.json new file mode 100644 index 00000000..786414f4 --- /dev/null +++ b/Stay/Assets.xcassets/PopupColor.colorset/Contents.json @@ -0,0 +1,41 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "1.000", + "blue" : "1.000", + "green" : "1.000", + "red" : "1.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "1.000", + "blue" : "0.110", + "green" : "0.110", + "red" : "0.110" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "localizable" : true + } +} diff --git a/Stay/Assets.xcassets/ProBgColor.colorset/Contents.json b/Stay/Assets.xcassets/ProBgColor.colorset/Contents.json new file mode 100644 index 00000000..4cd8b828 --- /dev/null +++ b/Stay/Assets.xcassets/ProBgColor.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.553", + "green" : "0.875", + "red" : "0.976" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Stay/Assets.xcassets/ProBorderColor.colorset/Contents.json b/Stay/Assets.xcassets/ProBorderColor.colorset/Contents.json new file mode 100644 index 00000000..c50287aa --- /dev/null +++ b/Stay/Assets.xcassets/ProBorderColor.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.114", + "green" : "0.337", + "red" : "0.518" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Stay/Assets.xcassets/ProLabelColor.colorset/Contents.json b/Stay/Assets.xcassets/ProLabelColor.colorset/Contents.json new file mode 100644 index 00000000..c50287aa --- /dev/null +++ b/Stay/Assets.xcassets/ProLabelColor.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.114", + "green" : "0.337", + "red" : "0.518" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Stay/Assets.xcassets/SecondaryAccentClassicColor.colorset/Contents.json b/Stay/Assets.xcassets/SecondaryAccentClassicColor.colorset/Contents.json new file mode 100644 index 00000000..a8057155 --- /dev/null +++ b/Stay/Assets.xcassets/SecondaryAccentClassicColor.colorset/Contents.json @@ -0,0 +1,41 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "0.100", + "blue" : "0xE0", + "green" : "0x20", + "red" : "0xB6" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "0.300", + "blue" : "0xE0", + "green" : "0x20", + "red" : "0xB6" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "localizable" : true + } +} diff --git a/Stay/Assets.xcassets/SecondaryBackgroundColor.colorset/Contents.json b/Stay/Assets.xcassets/SecondaryBackgroundColor.colorset/Contents.json new file mode 100644 index 00000000..c9ac8704 --- /dev/null +++ b/Stay/Assets.xcassets/SecondaryBackgroundColor.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "1.000", + "green" : "1.000", + "red" : "1.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.110", + "green" : "0.110", + "red" : "0.110" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Stay/Assets.xcassets/SecondaryPopupColor.colorset/Contents.json b/Stay/Assets.xcassets/SecondaryPopupColor.colorset/Contents.json new file mode 100644 index 00000000..e639f296 --- /dev/null +++ b/Stay/Assets.xcassets/SecondaryPopupColor.colorset/Contents.json @@ -0,0 +1,41 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "1.000", + "blue" : "0.969", + "green" : "0.969", + "red" : "0.969" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "1.000", + "blue" : "0.153", + "green" : "0.153", + "red" : "0.153" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "localizable" : true + } +} diff --git a/Stay/Assets.xcassets/SelectedAccentClassicColor.colorset/Contents.json b/Stay/Assets.xcassets/SelectedAccentClassicColor.colorset/Contents.json new file mode 100644 index 00000000..6cdf086d --- /dev/null +++ b/Stay/Assets.xcassets/SelectedAccentClassicColor.colorset/Contents.json @@ -0,0 +1,23 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "0.600", + "blue" : "0xE0", + "green" : "0x20", + "red" : "0xB6" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "localizable" : true + } +} diff --git a/Stay/Assets.xcassets/TertiaryAccentClassicColor.colorset/Contents.json b/Stay/Assets.xcassets/TertiaryAccentClassicColor.colorset/Contents.json new file mode 100644 index 00000000..53bfd7a8 --- /dev/null +++ b/Stay/Assets.xcassets/TertiaryAccentClassicColor.colorset/Contents.json @@ -0,0 +1,41 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "0.050", + "blue" : "0xE0", + "green" : "0x20", + "red" : "0xB6" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "0.300", + "blue" : "0xE0", + "green" : "0x20", + "red" : "0xB6" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "localizable" : true + } +} diff --git a/Stay/Assets.xcassets/TertiaryBackgroundColor.colorset/Contents.json b/Stay/Assets.xcassets/TertiaryBackgroundColor.colorset/Contents.json new file mode 100644 index 00000000..ac460a06 --- /dev/null +++ b/Stay/Assets.xcassets/TertiaryBackgroundColor.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.882", + "green" : "0.882", + "red" : "0.882" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.122", + "green" : "0.122", + "red" : "0.122" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Stay/Images/addScript@3x.png b/Stay/Images/addScript@3x.png new file mode 100644 index 00000000..d2d4d5dd Binary files /dev/null and b/Stay/Images/addScript@3x.png differ diff --git a/Stay/Images/addedScript@3x.png b/Stay/Images/addedScript@3x.png new file mode 100644 index 00000000..faa0354a Binary files /dev/null and b/Stay/Images/addedScript@3x.png differ diff --git a/Stay/Info.plist b/Stay/Info.plist index 2e02cc06..c160863d 100644 --- a/Stay/Info.plist +++ b/Stay/Info.plist @@ -11,10 +11,19 @@ com.stay CFBundleURLSchemes - prefs stay + + CFBundleTypeRole + Editor + CFBundleURLName + com.stay2 + CFBundleURLSchemes + + stay2 + + NSAppTransportSecurity diff --git a/Stay/Main/CodeMirror/editor.html b/Stay/Main/CodeMirror/editor.html index 3f70eed3..8654c6cb 100644 --- a/Stay/Main/CodeMirror/editor.html +++ b/Stay/Main/CodeMirror/editor.html @@ -32,7 +32,7 @@ // document.querySelector(".CodeMirror").style.height=innerHeight; let isDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches let myCodeMirror = CodeMirror(document.body, { - value: "function myScript(){return 100;}\n", + value: "", mode: "javascript", lineNumbers: true, fullScreen: true, diff --git a/Stay/Main/Common/SYDefines.h b/Stay/Main/Common/SYDefines.h index 6bf3095e..46a468a0 100644 --- a/Stay/Main/Common/SYDefines.h +++ b/Stay/Main/Common/SYDefines.h @@ -1,6 +1,7 @@ #ifndef SYDefines_h #define SYDefines_h +#define DynamicColor(DARK_COLOR,LIGHT_COLOR) ([UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull traitCollection) {if (traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {return DARK_COLOR;} else {return LIGHT_COLOR;}}]) #define iPhone3GS ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(320, 480), [[UIScreen mainScreen] currentMode].size) : NO) diff --git a/Stay/Main/Common/ScriptEntity.h b/Stay/Main/Common/ScriptEntity.h new file mode 100644 index 00000000..67eee4aa --- /dev/null +++ b/Stay/Main/Common/ScriptEntity.h @@ -0,0 +1,26 @@ +// +// ScriptEntity.h +// Stay +// +// Created by zly on 2022/5/13. +// + +#import +#import "UserScript.h" +NS_ASSUME_NONNULL_BEGIN + +@interface ScriptEntity : NSObject + +@property (nonatomic, strong) UserScript *script; + +@property (nonatomic, assign) BOOL needUpdate; + +@property (nonatomic, strong) UserScript *updateScript; + +- (NSDictionary *)toDictionary; + ++ (instancetype)ofDictionary:(NSDictionary *)dic; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Stay/Main/Common/ScriptEntity.m b/Stay/Main/Common/ScriptEntity.m new file mode 100644 index 00000000..12a2a0ba --- /dev/null +++ b/Stay/Main/Common/ScriptEntity.m @@ -0,0 +1,36 @@ +// +// ScriptEntity.m +// Stay +// +// Created by zly on 2022/5/13. +// + +#import "ScriptEntity.h" + +@implementation ScriptEntity + + +- (NSDictionary *)toDictionary{ + return @{ + @"script":self.script ? self.script.toDictionary : @"", + @"needUpdate":@(self.needUpdate), + @"updateScript":self.updateScript ? self.updateScript.toDictionary : @"" + + }; +} + ++ (instancetype)ofDictionary:(NSDictionary *)dic{ + ScriptEntity *script = [[ScriptEntity alloc] init]; + script.needUpdate = [dic[@"needUpdate"] boolValue]; + + if(dic[@"updateScript"] != NULL && [dic[@"updateScript"] isKindOfClass:[NSDictionary class]]) { + script.updateScript = [UserScript ofDictionary:dic[@"updateScript"]]; + } + if(dic[@"script"] != NULL) { + script.script = [UserScript ofDictionary:dic[@"script"]]; + } + + return script; +} + +@end diff --git a/Stay/Main/Common/ScriptMananger.h b/Stay/Main/Common/ScriptMananger.h new file mode 100644 index 00000000..8f9a3827 --- /dev/null +++ b/Stay/Main/Common/ScriptMananger.h @@ -0,0 +1,26 @@ +// +// ScriptMananger.h +// Stay +// +// Created by zly on 2022/5/13. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface ScriptMananger : NSObject + + +// 数据源数组 +@property (nonatomic, strong) NSMutableDictionary *scriptDic; + ++ (instancetype)shareManager; + +- (void)buildData; + +- (void)refreshData; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Stay/Main/Common/ScriptMananger.m b/Stay/Main/Common/ScriptMananger.m new file mode 100644 index 00000000..58152bc9 --- /dev/null +++ b/Stay/Main/Common/ScriptMananger.m @@ -0,0 +1,199 @@ +// +// ScriptMananger.m +// Stay +// +// Created by zly on 2022/5/13. +// + +#import "ScriptMananger.h" +#import "DataManager.h" +#import "ScriptEntity.h" +#import "SYNetworkUtils.h" +#import "Tampermonkey.h" +#import "SYVersionUtils.h" + +@implementation ScriptMananger + + ++ (instancetype)shareManager { + + static ScriptMananger *instance = nil; + static dispatch_once_t onceToken; + + dispatch_once(&onceToken, ^{ + instance = [[ScriptMananger alloc] init]; + [instance queryScript]; + [instance buildData]; + }); + +// [instance checkScript]; + + return instance; + +} + +- (void)buildData{ + + NSArray *array = [[DataManager shareManager] findScript:1]; + + if(array != NULL) { + for(int i = 0; i < array.count; i++) { + ScriptEntity *scriptEntity = [[ScriptEntity alloc] init]; + UserScript *scrpit = array[i]; + scriptEntity.script = scrpit; + if(self.scriptDic[scrpit.uuid] == nil) { + self.scriptDic[scrpit.uuid] = scriptEntity; + } else { + scriptEntity = self.scriptDic[scrpit.uuid]; + } + + if(scrpit.updateUrl != NULL && scrpit.updateUrl.length > 0 && !scrpit.updateSwitch) { + [[SYNetworkUtils shareInstance] requestGET:scrpit.updateUrl params:nil successBlock:^(NSString * _Nonnull responseObject) { + if(responseObject != nil) { + UserScript *userScript = [[Tampermonkey shared] parseWithScriptContent:responseObject]; + if(userScript.version != NULL) { + NSInteger status = [SYVersionUtils compareVersion:userScript.version toVersion:scrpit.version]; + if(status == 1) { + if(userScript.downloadUrl == nil || userScript.downloadUrl.length <= 0){ + if(userScript.content != nil && userScript.content.length > 0) { + userScript.uuid = scrpit.uuid; + userScript.active = scrpit.active; + scriptEntity.updateScript = userScript; + scriptEntity.needUpdate = true; + [self updateScript]; + } + } else { + [[SYNetworkUtils shareInstance] requestGET:scrpit.downloadUrl params:nil successBlock:^(NSString * _Nonnull responseObject) { + if(responseObject != nil) { + UserScript *userScript = [[Tampermonkey shared] parseWithScriptContent:responseObject]; + userScript.uuid = scrpit.uuid; + userScript.active = scrpit.active; + if(userScript != nil && userScript.errorMessage != nil && userScript.errorMessage.length <= 0) { + scriptEntity.updateScript = userScript; + scriptEntity.needUpdate = true; + [self updateScript]; + + + } + } + } failBlock:^(NSError * _Nonnull error) { + + }]; + } + } + } + + } + } failBlock:^(NSError * _Nonnull error) { + + }]; + } else if(scrpit.downloadUrl != NULL && scrpit.downloadUrl.length > 0 && !scrpit.updateSwitch) { + [[SYNetworkUtils shareInstance] requestGET:scrpit.downloadUrl params:nil successBlock:^(NSString * _Nonnull responseObject) { + if(responseObject != nil) { + UserScript *userScript = [[Tampermonkey shared] parseWithScriptContent:responseObject]; + if(userScript.version != NULL) { + NSInteger status = [SYVersionUtils compareVersion:userScript.version toVersion:scrpit.version]; + if(status == 1) { + userScript.uuid = scrpit.uuid; + userScript.active = scrpit.active; + if(userScript != nil && userScript.errorMessage != nil && userScript.errorMessage.length <= 0) { + scriptEntity.updateScript = userScript; + scriptEntity.needUpdate = true; + [self updateScript]; + } + } + } + } + } failBlock:^(NSError * _Nonnull error) { + + }]; + } + } + + [self checkScript]; + } + +} + + +- (void)checkScript { + + NSArray *array = [self scriptDic].allKeys; + + for(int i = 0; i < array.count; i++) { + NSString *uuid = array[i]; + UserScript *scritp = [[DataManager shareManager] selectScriptByUuid:uuid]; + if (scritp == NULL || scritp.uuid == nil) { + [[self scriptDic] removeObjectForKey:uuid]; + } else { + ScriptEntity *scriptEntity = self.scriptDic[uuid]; + scriptEntity.script = scritp; + if (scriptEntity.updateScript != NULL && [scritp.version isEqualToString:scriptEntity.updateScript.version]) { + scriptEntity.needUpdate = false; + scriptEntity.updateScript = nil; + } + } + } + [self saveScript]; +} + +- (void)updateScript { + NSNotification *notification = [NSNotification notificationWithName:@"needUpdate" object:nil]; + [[NSNotificationCenter defaultCenter]postNotification:notification]; +} + + +- (void)refreshData{ + NSArray *array = [[DataManager shareManager] findScript:1]; + if(array != NULL) { + for(int i = 0; i < array.count; i++) { + ScriptEntity *scriptEntity = [[ScriptEntity alloc] init]; + UserScript *scrpit = array[i]; + scriptEntity.script = scrpit; + if(self.scriptDic[scrpit.uuid] == nil) { + self.scriptDic[scrpit.uuid] = scriptEntity; + } + } + } + [self checkScript]; +} + + +- (void)saveScript { + NSUserDefaults *groupUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.dajiu.stay.pro"]; + + NSMutableDictionary *dic = [NSMutableDictionary dictionary]; + + for(NSString *uuid in self.scriptDic.allKeys) { + + ScriptEntity *entity = self.scriptDic[uuid]; + + dic[uuid] = [entity toDictionary]; + } + + [groupUserDefaults setObject:dic forKey:@"SCRIPT_INSTANCE"]; + [groupUserDefaults synchronize]; +} + +- (void)queryScript { + NSUserDefaults *groupUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.dajiu.stay.pro"]; + if([groupUserDefaults dictionaryForKey:@"SCRIPT_INSTANCE"] != NULL && [groupUserDefaults dictionaryForKey:@"SCRIPT_INSTANCE"].allValues.count > 0){ + NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithDictionary:[groupUserDefaults dictionaryForKey:@"SCRIPT_INSTANCE"]]; + + for(NSString *uuid in dic.allKeys) { + self.scriptDic[uuid] = [ScriptEntity ofDictionary:dic[uuid]]; + } + + + } + +} + +- (NSMutableDictionary *)scriptDic { + if(_scriptDic == nil) { + _scriptDic = [NSMutableDictionary dictionary]; + } + return _scriptDic; +} + +@end diff --git a/Stay/Main/Controls/FCApp.h b/Stay/Main/Controls/FCApp.h new file mode 100644 index 00000000..6b0fe6ef --- /dev/null +++ b/Stay/Main/Controls/FCApp.h @@ -0,0 +1,17 @@ +// +// FCApp.h +// FastClip-iOS +// +// Created by ris on 2022/2/8. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface FCApp : NSObject +@property (readonly, class) UIWindow *keyWindow; +@end + +NS_ASSUME_NONNULL_END diff --git a/Stay/Main/Controls/FCApp.m b/Stay/Main/Controls/FCApp.m new file mode 100644 index 00000000..4169eb99 --- /dev/null +++ b/Stay/Main/Controls/FCApp.m @@ -0,0 +1,29 @@ +// +// FCApp.m +// FastClip-iOS +// +// Created by ris on 2022/2/8. +// + +#import "FCApp.h" + +@implementation FCApp + ++ (UIWindow *)keyWindow{ + if (@available(ios 15.0, *)) { + NSSet *connectedScenes = [[UIApplication sharedApplication] connectedScenes]; + for (UIScene *scene in connectedScenes){ + if (scene.activationState == UISceneActivationStateForegroundActive){ + return ((UIWindowScene *)scene).keyWindow; + } + } + if ([UIApplication sharedApplication].windows.count > 0){ + return [UIApplication sharedApplication].windows[0]; + } + else return nil; + } else { + return [UIApplication sharedApplication].keyWindow; + } +} + +@end diff --git a/Stay/Main/Controls/FCDisk.h b/Stay/Main/Controls/FCDisk.h new file mode 100644 index 00000000..0d648607 --- /dev/null +++ b/Stay/Main/Controls/FCDisk.h @@ -0,0 +1,69 @@ +// +// FCDisk.h +// fastclipcore +// +// Created by ris on 2021/12/23. +// + +/** + This is a class support write/read with the disk + */ +#import + +@protocol FCArchiving +@required +// Return nil will not trigger - beginWriteData & endWriteData. +- (NSData *_Nullable)archiveData; +- (void)unarchiveData:(NSData *_Nullable)data; +- (dispatch_queue_t _Nonnull )dispatchQueue; + +@optional +- (nullable NSObject *)logicalLockOnWirte; +- (void)beginWriteData; +- (void)endWriteData; +@end + +NS_ASSUME_NONNULL_BEGIN +@interface FCDisk : NSObject{ + NSURL *_url; /* File/Directory url */ +} +// Subclass must implement this method. +- (instancetype)initWithPath:(NSString *)path isDirectory:(BOOL)isDirectory; +// Create a new instance under the disk path. +- (instancetype)initUnder:(FCDisk *)disk + relativePath:(NSString *)relativePath + isDirectory:(BOOL)isDirectory; + +// - readAtOffset & - readAtOffsetToEnd have lock to guarantee read sync from disk. +- (NSData *)readAtOffset:(NSUInteger)offset length:(NSUInteger)length; +- (NSData *)readAtOffsetToEnd:(NSUInteger)offset; + +// Load disk data to memory. +- (void)loadSync:(BOOL)sync; +// Flush archive data to disk. +- (void)flush; +// Remove self from the disk. +- (void)remove; + +// - initWithPath:isDirectory: & - initUnder:relativePath:isDirectory: always call onInit; +- (void)onInit; + +// Indicate if you need init your object with the disk file. ++ (BOOL)unarchiveOnInit; +/* If the file is empty or have not been created yet, you can use implement + this method to init your object.*/ +- (void)initOnEmpty; +- (NSString *)queueName:(NSString *)name; + +@property (readonly) NSURL *url; +@property (readonly) NSString *path; +@property (readonly) BOOL isDirectory; +@property (readonly) NSFileHandle *readFH; +@property (readonly) NSFileHandle *writeFH; +@property (readonly) BOOL isEmptyFile; + +@property (nonatomic, copy) NSData *(^encrypt)(NSData *); +@property (nonatomic, copy) NSData *(^dencrypt)(NSData *); +@end + +NS_ASSUME_NONNULL_END diff --git a/Stay/Main/Controls/FCDisk.m b/Stay/Main/Controls/FCDisk.m new file mode 100644 index 00000000..1dfb1d05 --- /dev/null +++ b/Stay/Main/Controls/FCDisk.m @@ -0,0 +1,249 @@ +// +// FCDisk.m +// fastclipcore +// +// Created by ris on 2021/12/23. +// + +#import "FCDisk.h" + +#define FCDiskRename @".rename" +@interface FCDisk(){ + BOOL _isDirectory; + BOOL _isEmptyFile; + NSFileHandle *_readFH; + NSFileHandle *_writeFH; + NSString *_path; +} +@property (nonatomic, strong) NSURL *renameURL; +@property (nonatomic, strong) NSObject *readFHLock; +@end +@implementation FCDisk + +- (instancetype)initWithPath:(NSString *)path isDirectory:(BOOL)isDirectory{ + if (self = [super init]){ + [self onInit]; + _path = path; + _url = [NSURL fileURLWithPath:path]; + _isDirectory = isDirectory; + _isEmptyFile = YES; + + //Create the directory first. + NSString *directoryPath = _isDirectory ? _path : [_path stringByDeletingLastPathComponent]; + if (![[NSFileManager defaultManager] fileExistsAtPath:directoryPath]){ + [[NSFileManager defaultManager] createDirectoryAtPath:directoryPath + withIntermediateDirectories:YES + attributes:nil + error:nil]; + } + + //Create file. + if (!_isDirectory){ + if (![[NSFileManager defaultManager] fileExistsAtPath:_path]){ + [[NSFileManager defaultManager] createFileAtPath:_path contents:nil attributes:nil]; + } + + [self readFH]; + NSData *oneByte = [self.readFH readDataOfLength:1]; + _isEmptyFile = oneByte.length == 0; + [self.readFH seekToFileOffset:0]; + if ([[self class] unarchiveOnInit]){ + if (!_isEmptyFile) [self _doLoad]; + else [self initOnEmpty]; + } + } + } + + return self; +} + +- (instancetype)initUnder:(FCDisk *)disk + relativePath:(NSString *)relativePath + isDirectory:(BOOL)isDirectory{ + BOOL diskIsDirectory; + [[NSFileManager defaultManager] fileExistsAtPath:[disk path] isDirectory:&diskIsDirectory]; + NSString *path = diskIsDirectory ? [[disk path] stringByAppendingPathComponent:relativePath] : + [[[disk path] stringByDeletingLastPathComponent] stringByAppendingPathComponent:relativePath]; + return [self initWithPath:path isDirectory:isDirectory]; +} + +- (void)onInit{} + +- (void)_doLoad{ + NSData *loadData = [self readAtOffsetToEnd:0]; + if (self.dencrypt != nil){ + loadData = self.dencrypt(loadData); + } + [self unarchiveData:loadData]; +} + +- (void)loadSync:(BOOL)sync{ + dispatch_semaphore_t sem; + if (sync){ + sem = dispatch_semaphore_create(0); + } + dispatch_async(self.dispatchQueue, ^{ + [self _doLoad]; + if (sync){ + dispatch_semaphore_signal(sem); + } + }); + if (sync){ + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + } +} + + +- (void)_doFlush{ + NSData *flushData = [self archiveData]; + if (nil == flushData) return; + if (self.encrypt != nil){ + flushData = self.encrypt(flushData); + } + [self write:flushData]; +} + +- (void)flush{ + dispatch_async(self.dispatchQueue, ^{ + + NSObject *lock = [self logicalLockOnWirte]; + + if (lock){ + @synchronized (lock) { + [self beginWriteData]; + [self _doFlush]; + [self endWriteData]; + } + } + else{ + [self beginWriteData]; + [self _doFlush]; + [self endWriteData]; + } + + + }); +} + +- (NSData *)readAtOffset:(NSUInteger)offset length:(NSUInteger)length{ + NSData *readData = nil; + @synchronized (self.readFHLock) { + [self.readFH seekToFileOffset:offset]; + readData = [self.readFH readDataOfLength:length]; + } + + return readData; +} + +- (NSData *)readAtOffsetToEnd:(NSUInteger)offset{ + NSData *readData = nil; + @synchronized (self.readFHLock) { + [self.readFH seekToFileOffset:offset]; + readData = [self.readFH readDataToEndOfFile]; + } + + return readData; +} + +- (void)remove{ + [[NSFileManager defaultManager] removeItemAtPath:_path error:nil]; +} + +- (BOOL)readOnInit{ return YES; } +- (NSURL *)url{ return _url; } +- (NSString *)path{ return _path; } + +- (NSURL *)renameURL{ + if (nil == _renameURL){ + NSURL *copyURL = [_url copy]; + NSString *newLastComponent = [NSString stringWithFormat:@"%@.rename",[copyURL lastPathComponent]]; + _renameURL = [[copyURL URLByDeletingLastPathComponent] URLByAppendingPathComponent:newLastComponent]; + } + return _renameURL; +} + +- (NSObject *)readFHLock{ + if (nil == _readFHLock){ + _readFHLock = [[NSObject alloc] init]; + } + return _readFHLock; +} + +- (BOOL)isDirectory{ + return _isDirectory; +} + +- (BOOL)isEmptyFile{ + return _isEmptyFile; +} + +- (NSFileHandle *)readFH{ + if (nil == _readFH && _url != nil){ + _readFH = [NSFileHandle fileHandleForReadingFromURL:_url error:nil]; + } + + return _readFH; +} + +- (NSFileHandle *)writeFH{ + if (nil == _writeFH && self.renameURL != nil){ + @synchronized (self.renameURL) { + [[NSFileManager defaultManager] removeItemAtPath:[self.renameURL path] error:nil]; + [[NSFileManager defaultManager] createFileAtPath:[self.renameURL path] + contents:nil + attributes:nil]; + _writeFH = [NSFileHandle fileHandleForWritingToURL:self.renameURL error:nil]; + } + } + + return _writeFH; +} + + +- (void)write:(NSData *)data{ + [self.writeFH truncateFileAtOffset:0]; + [self.writeFH writeData:data error:nil]; + [self.writeFH synchronizeFile]; + [self.writeFH closeFile]; + _writeFH = nil; + _isEmptyFile = NO; + @synchronized (self.readFHLock) { + [_readFH closeFile]; + _readFH = nil; + NSError *error; + [[NSFileManager defaultManager] removeItemAtPath:[_url path] error:&error]; + @synchronized (self.renameURL) { + [[NSFileManager defaultManager] moveItemAtURL:self.renameURL toURL:_url error:&error]; + } + [self readFH]; + } +} + + +- (NSString *)queueName:(NSString *)name{ + return [NSString stringWithFormat:@"com.fastclip.index.%@.%@",name,[[NSUUID UUID] UUIDString]]; +} + +/* + Virtual methods*/ +- (void)initOnEmpty{} + +- (NSData * _Nullable)archiveData { + return nil; +} + +- (dispatch_queue_t _Nonnull)dispatchQueue { + return dispatch_get_main_queue(); +} + +- (void)unarchiveData:(NSData * _Nullable)data {} + ++ (BOOL)unarchiveOnInit{ + return YES; +} + +- (NSObject *)logicalLockOnWirte{ return nil; } +- (void)beginWriteData{}; +- (void)endWriteData{}; + +@end diff --git a/Stay/Main/Controls/FCView.h b/Stay/Main/Controls/FCView.h new file mode 100644 index 00000000..d1d70c5e --- /dev/null +++ b/Stay/Main/Controls/FCView.h @@ -0,0 +1,18 @@ +// +// FCView.h +// FastClip-iOS +// +// Created by ris on 2022/2/7. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface FCView : UIView + + +@end + +NS_ASSUME_NONNULL_END diff --git a/Stay/Main/Controls/FCView.m b/Stay/Main/Controls/FCView.m new file mode 100644 index 00000000..83407901 --- /dev/null +++ b/Stay/Main/Controls/FCView.m @@ -0,0 +1,16 @@ +// +// FCView.m +// FastClip-iOS +// +// Created by ris on 2022/2/7. +// + +#import "FCView.h" + +@interface FCView() + +@end + +@implementation FCView + +@end diff --git a/Stay/Main/Controls/Loading/LoadingSlideController.h b/Stay/Main/Controls/Loading/LoadingSlideController.h new file mode 100644 index 00000000..32edfe92 --- /dev/null +++ b/Stay/Main/Controls/Loading/LoadingSlideController.h @@ -0,0 +1,20 @@ +// +// LoadingSlideController.h +// Stay +// +// Created by ris on 2022/5/23. +// + +#import "FCSlideController.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface LoadingSlideController : FCSlideController + +@property (nonatomic, strong) NSString *originMainText; +@property (nonatomic, strong) NSString *originSubText; +- (void)updateMainText:(NSString *)text; +- (void)updateSubText:(NSString *)text; +@end + +NS_ASSUME_NONNULL_END diff --git a/Stay/Main/Controls/Loading/LoadingSlideController.m b/Stay/Main/Controls/Loading/LoadingSlideController.m new file mode 100644 index 00000000..67389bb8 --- /dev/null +++ b/Stay/Main/Controls/Loading/LoadingSlideController.m @@ -0,0 +1,63 @@ +// +// LoadingSlideController.m +// Stay +// +// Created by ris on 2022/5/23. +// + +#import "LoadingSlideController.h" +#import "LoadingStatusModalViewController.h" + +@interface LoadingSlideController() + +@property (nonatomic, strong) ModalNavigationController *navController; +@end + +@implementation LoadingSlideController + +- (ModalNavigationController *)navController{ + if (nil == _navController){ + LoadingStatusModalViewController *cer = [[LoadingStatusModalViewController alloc] init]; + cer.hideNavigationBar = YES; + cer.originMainText = self.originMainText; + cer.originSubText = self.originSubText; + _navController = [[ModalNavigationController alloc] initWithRootModalViewController:cer radius:15]; + _navController.slideController = self; + } + + return _navController; +} + +- (ModalNavigationController *)modalNavigationController{ + return self.navController; +} + + +- (void)updateMainText:(NSString *)text{ + LoadingStatusModalViewController *cer = (LoadingStatusModalViewController *)self.navController.rootModalViewController; + [cer updateMainText:text]; +} + +- (void)updateSubText:(NSString *)text{ + LoadingStatusModalViewController *cer = (LoadingStatusModalViewController *)self.navController.rootModalViewController; + [cer updateSubText:text]; +} + +- (FCPresentingFrom)from{ + return FCPresentingFromBottom; +} + +- (CGFloat)marginToFrom{ + return 80; +} + +- (BOOL)blockAction{ + return YES; +} + +- (BOOL)dismissable{ + return NO; +} + + +@end diff --git a/Stay/Main/Controls/Loading/LoadingStatusModalViewController.h b/Stay/Main/Controls/Loading/LoadingStatusModalViewController.h new file mode 100644 index 00000000..dabf3d43 --- /dev/null +++ b/Stay/Main/Controls/Loading/LoadingStatusModalViewController.h @@ -0,0 +1,20 @@ +// +// LoadingStatusModalViewController.h +// Stay +// +// Created by ris on 2022/5/23. +// + +#import "ModalViewController.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface LoadingStatusModalViewController : ModalViewController + +@property (nonatomic, strong) NSString *originMainText; +@property (nonatomic, strong) NSString *originSubText; +- (void)updateMainText:(NSString *)text; +- (void)updateSubText:(NSString *)text; +@end + +NS_ASSUME_NONNULL_END diff --git a/Stay/Main/Controls/Loading/LoadingStatusModalViewController.m b/Stay/Main/Controls/Loading/LoadingStatusModalViewController.m new file mode 100644 index 00000000..0b1f1a1f --- /dev/null +++ b/Stay/Main/Controls/Loading/LoadingStatusModalViewController.m @@ -0,0 +1,70 @@ +// +// LoadingStatusModalViewController.m +// Stay +// +// Created by ris on 2022/5/23. +// + +#import "LoadingStatusModalViewController.h" +#import "FCStyle.h" + +@interface LoadingStatusModalViewController() + +@property (nonatomic, strong) UILabel *mainLabel; +@property (nonatomic, strong) UILabel *subLabel; +@end + +@implementation LoadingStatusModalViewController + +- (void)viewDidLoad{ + [super viewDidLoad]; + [self mainLabel]; + [self subLabel]; +} + +- (void)updateMainText:(NSString *)text{ + dispatch_async(dispatch_get_main_queue(), ^{ + self.mainLabel.text = text; + }); +} + +- (void)updateSubText:(NSString *)text{ + dispatch_async(dispatch_get_main_queue(), ^{ + self.subLabel.text = text; + }); +} + +- (UILabel *)mainLabel{ + if (nil == _mainLabel){ + _mainLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 30, self.view.frame.size.width, 17)]; + _mainLabel.textAlignment = UITextAlignmentCenter; + _mainLabel.text = self.originMainText; + _mainLabel.font = FCStyle.headlineBold; + _mainLabel.textColor = FCStyle.fcBlack; + [self.view addSubview:_mainLabel]; + } + + return _mainLabel; +} + +- (UILabel *)subLabel{ + if (nil == _subLabel){ + _subLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 55, self.view.frame.size.width, 16)]; + _subLabel.textAlignment = UITextAlignmentCenter; + _subLabel.text = self.originSubText; + _subLabel.font = FCStyle.body; + _subLabel.textColor = FCStyle.fcSecondaryBlack; + [self.view addSubview:_subLabel]; + } + + return _subLabel; +} + +- (CGSize)mainViewSize{ + CGFloat width = 300; + CGFloat height = 100; + return CGSizeMake(width, height); +} + + +@end diff --git a/Stay/Main/Controls/Modal/ModalNavigationBar.h b/Stay/Main/Controls/Modal/ModalNavigationBar.h new file mode 100644 index 00000000..17d1ea1d --- /dev/null +++ b/Stay/Main/Controls/Modal/ModalNavigationBar.h @@ -0,0 +1,26 @@ +// +// ModalNavigationBar.h +// FastClip-iOS +// +// Created by ris on 2022/2/7. +// + +#import "FCView.h" + +NS_ASSUME_NONNULL_BEGIN + +@protocol ModalNavigationBarDelegate +- (void)navigationBarDidClickCancelButton; +@end + +@interface ModalNavigationBar : FCView + +@property (nonatomic, strong) NSString *title; +@property (readonly) CGFloat height; +@property (nonatomic, assign) BOOL showCancel; +@property (nonatomic, weak) id delegate; +- (instancetype)init; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Stay/Main/Controls/Modal/ModalNavigationBar.m b/Stay/Main/Controls/Modal/ModalNavigationBar.m new file mode 100644 index 00000000..0fbc14d0 --- /dev/null +++ b/Stay/Main/Controls/Modal/ModalNavigationBar.m @@ -0,0 +1,84 @@ +// +// ModalNavigationBar.m +// FastClip-iOS +// +// Created by ris on 2022/2/7. +// + +#import "ModalNavigationBar.h" +#import "FCStyle.h" + +@interface ModalNavigationBar() + +@property (nonatomic, strong) UILabel *titleLabel; +@property (nonatomic, strong) UIButton *cancelButton; +@end + +@implementation ModalNavigationBar + +- (instancetype)init{ + if (self = [super initWithFrame:CGRectMake(0, 0, 0, self.height)]){ + self.backgroundColor = FCStyle.popup; + [self titleLabel]; + } + + return self; +} + +- (void)setTitle:(NSString *)title{ + _title = title; + self.titleLabel.text = _title; +} + +- (void)willMoveToSuperview:(UIView *)newSuperview{ + [super willMoveToSuperview:newSuperview]; +} + +- (void)setFrame:(CGRect)frame{ + [super setFrame:frame]; + [self.titleLabel setFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)]; + [self.cancelButton setFrame:CGRectMake(frame.size.width - 15 - 22, (frame.size.height - 22)/2, 22, 22)]; +} + +- (UILabel *)titleLabel{ + if (nil == _titleLabel){ + _titleLabel = [[UILabel alloc] initWithFrame:CGRectZero]; + _titleLabel.font = FCStyle.headline; + _titleLabel.textAlignment = NSTextAlignmentCenter; + _titleLabel.textColor = FCStyle.fcBlack; + [self addSubview:_titleLabel]; + } + + return _titleLabel; +} + +- (UIButton *)cancelButton{ + if (nil == _cancelButton){ + _cancelButton = [[UIButton alloc] initWithFrame:CGRectZero]; + UIImage *image = [UIImage systemImageNamed:@"multiply.circle.fill" withConfiguration:[UIImageSymbolConfiguration configurationWithFont:[UIFont systemFontOfSize:22]]]; + + [_cancelButton setImage:[image imageWithTintColor:FCStyle.fcSecondaryBlack renderingMode:UIImageRenderingModeAlwaysOriginal] forState:UIControlStateNormal]; + [_cancelButton addTarget:self action:@selector(cancelAction:) forControlEvents:UIControlEventTouchUpInside]; + [self addSubview:_cancelButton]; + _cancelButton.hidden = YES; + + } + + return _cancelButton; +} + +- (void)cancelAction:(id)sender{ + if ([self.delegate respondsToSelector:@selector(navigationBarDidClickCancelButton)]){ + [self.delegate navigationBarDidClickCancelButton]; + } +} + +- (void)setShowCancel:(BOOL)showCancel{ + self.cancelButton.hidden = !showCancel; +} + +- (CGFloat)height{ + return 44; +} + +@end diff --git a/Stay/Main/Controls/Modal/ModalNavigationController.h b/Stay/Main/Controls/Modal/ModalNavigationController.h new file mode 100644 index 00000000..fc7959f6 --- /dev/null +++ b/Stay/Main/Controls/Modal/ModalNavigationController.h @@ -0,0 +1,34 @@ +// +// ModalNavigationController.h +// FastClip-iOS +// +// Created by ris on 2022/2/7. +// + +#import +#import +#import "FCSlideController.h" +NS_ASSUME_NONNULL_BEGIN +@class ModalViewController,FCRoundedShadowView; +@interface ModalNavigationController : NSObject{ +} + +@property (nonatomic, strong) ModalViewController *rootModalViewController; +@property (readonly) NSMutableArray *controllers; +@property (readonly) FCRoundedShadowView *view; +@property (nonatomic, weak) FCSlideController *slideController; + +- (instancetype)initWithRootModalViewController:(ModalViewController *)modalViewController; +- (instancetype)initWithRootModalViewController:(ModalViewController *)modalViewController radius:(CGFloat)radius; +- (instancetype)initWithRootModalViewControllerAndNoRoundShadow:(ModalViewController *)modalViewController; + +- (void)pushModalViewController:(ModalViewController *)modalViewController; +- (void)popModalViewController; +- (void)popModalViewControllerWithCompletion:(nullable void(^)(void))completionHandler; +- (void)popToRootController; +- (void)popToRootControllerWithDismiss; + + +@end + +NS_ASSUME_NONNULL_END diff --git a/Stay/Main/Controls/Modal/ModalNavigationController.m b/Stay/Main/Controls/Modal/ModalNavigationController.m new file mode 100644 index 00000000..262e63b3 --- /dev/null +++ b/Stay/Main/Controls/Modal/ModalNavigationController.m @@ -0,0 +1,203 @@ +// +// ModalNavigationController.m +// FastClip-iOS +// +// Created by ris on 2022/2/7. +// + +#import "ModalNavigationController.h" +#import "ModalViewController.h" +#import "FCRoundedShadowView.h" +#import "FCApp.h" + +@interface ModalNavigationController(){ + ModalViewController *_currentModalViewController; + NSMutableArray *_controllers; +} + +@property (nonatomic, strong) ModalViewController *currentModalViewController; +@property (nonatomic, strong) NSMutableArray *controllers; +@property (nonatomic, strong) FCRoundedShadowView *view; +@property (nonatomic, assign) CGFloat radius; +@property (nonatomic, assign) BOOL noRoundShadow; +@end + +@implementation ModalNavigationController + +- (instancetype)initWithRootModalViewController:(ModalViewController *)modalViewController radius:(CGFloat)radius{ + if (self = [super init]){ + self.radius = radius; + [self view]; + self.rootModalViewController = modalViewController; + modalViewController.navigationController = self; + modalViewController.isRoot = YES; + [self pushModalViewController:modalViewController]; + } + + return self; +} + +- (instancetype)initWithRootModalViewController:(ModalViewController *)modalViewController{ + if (self = [super init]){ + [self view]; + self.rootModalViewController = modalViewController; + modalViewController.navigationController = self; + modalViewController.isRoot = YES; + [self pushModalViewController:modalViewController]; + } + + return self; +} + +- (instancetype)initWithRootModalViewControllerAndNoRoundShadow:(ModalViewController *)modalViewController{ + if (self = [super init]){ + self.noRoundShadow = YES; + [self view]; + self.rootModalViewController = modalViewController; + modalViewController.navigationController = self; + modalViewController.isRoot = YES; + [self pushModalViewController:modalViewController]; + } + + return self; +} + +- (void)pushModalViewController:(ModalViewController *)modalViewController{ + modalViewController.navigationController = self; + [modalViewController willSee]; + [modalViewController viewDidLoad]; + + [modalViewController viewWillAppear]; + [self.currentModalViewController viewWillDisappear]; + [self.controllers addObject:modalViewController]; + + [self _pushView:[modalViewController getMainView]]; + +} + +- (void)popModalViewControllerWithCompletion:(nullable void(^)(void))completionHandler{ + [self.currentModalViewController viewWillDisappear]; + ModalViewController *willSeeController = [_controllers objectAtIndex:_controllers.count - 2]; + [willSeeController willSee]; + [self _popView:[willSeeController getMainView] toRoot:NO completion:completionHandler]; + [willSeeController viewWillAppear]; +} + +- (void)popModalViewController{ + [self popModalViewControllerWithCompletion:nil]; +} + +- (void)popToRootControllerWithDismiss{ + if (self.currentModalViewController.isRoot){ + [self.currentModalViewController viewWillDisappear]; + [self.currentModalViewController viewDidDisappear]; + return; + } + [self.currentModalViewController viewWillDisappear]; + + [self _popView:[[_controllers firstObject] getMainView] toRoot:YES completion:nil]; + [[self.controllers firstObject] viewWillAppear]; +} + +- (void)popToRootController{ + if (self.currentModalViewController.isRoot){ + return; + } + [self.currentModalViewController viewWillDisappear]; + + [self _popView:[[_controllers firstObject] getMainView] toRoot:YES completion:nil]; + [[self.controllers firstObject] viewWillAppear]; +} + +- (void)_pushView:(UIView *)pushedView{ + if (nil == self.currentModalViewController){ + [self.view.containerView addSubview:pushedView]; + [self.view setFrame:CGRectMake(0, 0, pushedView.frame.size.width, pushedView.frame.size.height)]; + [[self.controllers lastObject] viewDidAppear]; + self.currentModalViewController = [self.controllers lastObject]; + } + else{ + [self.view.containerView addSubview:pushedView]; + [self.currentModalViewController getMainView].hidden = YES; + CGRect oldFrame = self.view.frame; + CGSize newSize = pushedView.frame.size; + pushedView.transform = CGAffineTransformMakeScale(0.9, 0.9); + + [UIView animateWithDuration:0.3 + delay:0 + options:UIViewAnimationOptionCurveEaseOut + animations:^{ + pushedView.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1, 1); + CGPoint newOrigin = CGPointMake(oldFrame.origin.x - (newSize.width - oldFrame.size.width), oldFrame.origin.y - (newSize.height - oldFrame.size.height)); + [self.view setFrame:CGRectMake(newOrigin.x, newOrigin.y, pushedView.frame.size.width, pushedView.frame.size.height)]; + } + completion:^(BOOL finished) { + [self.currentModalViewController viewDidDisappear]; + [[self.currentModalViewController getMainView] removeFromSuperview]; + [self.currentModalViewController getMainView].hidden = NO; + [[self.controllers lastObject] viewDidAppear]; + self.currentModalViewController = [self.controllers lastObject]; + }]; + } + +} + +- (void)_popView:(UIView *)incomingView toRoot:(BOOL)toRoot completion:(void(^)(void))completionHandler{ + [self.view.containerView insertSubview:incomingView belowSubview:[self.currentModalViewController getMainView]]; + + CGRect oldFrame = self.view.frame; + CGSize newSize = incomingView.frame.size; + incomingView.transform = CGAffineTransformMakeScale(1.1, 1.1); + [UIView animateWithDuration:0.3 + delay:0 + options:UIViewAnimationOptionCurveEaseOut + animations:^{ + incomingView.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1, 1); + [[self.currentModalViewController getMainView] removeFromSuperview]; + CGPoint newOrigin = CGPointMake(oldFrame.origin.x - (newSize.width - oldFrame.size.width), (self.view.frame.origin.y - oldFrame.origin.y)+oldFrame.origin.y - (newSize.height - oldFrame.size.height)); + [self.view setFrame:CGRectMake(newOrigin.x, newOrigin.y, newSize.width, newSize.height)]; + } completion:^(BOOL finished) { + [self.currentModalViewController viewDidDisappear]; + [self.currentModalViewController getMainView].transform = CGAffineTransformIdentity; + + if (completionHandler){ + completionHandler(); + } + + [self.controllers removeLastObject]; + if (toRoot){ + for (int i = 1; i < self.controllers.count; i++){ + [[[self.controllers objectAtIndex:i] getMainView] removeFromSuperview]; + } + [self.controllers removeObjectsInRange:NSMakeRange(1, self.controllers.count-1)]; + } + [[self.controllers lastObject] viewDidAppear]; + self.currentModalViewController = [self.controllers lastObject]; + }]; +} + + +- (FCRoundedShadowView *)view{ + if (nil == _view){ + if (self.noRoundShadow){ + _view = [[FCRoundedShadowView alloc] init]; + } + else{ + _view = [[FCRoundedShadowView alloc] initWithRadius:self.radius]; + } + + } + + return _view; +} + + +- (NSMutableArray *)controllers{ + if (nil == _controllers){ + _controllers = [[NSMutableArray alloc] init]; + } + + return _controllers; +} + +@end diff --git a/Stay/Main/Controls/Modal/ModalNavigationItem.h b/Stay/Main/Controls/Modal/ModalNavigationItem.h new file mode 100644 index 00000000..cb8f0d5c --- /dev/null +++ b/Stay/Main/Controls/Modal/ModalNavigationItem.h @@ -0,0 +1,16 @@ +// +// ModalNavigationItem.h +// FastClip-iOS +// +// Created by ris on 2022/2/7. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface ModalNavigationItem : NSObject + +@end + +NS_ASSUME_NONNULL_END diff --git a/Stay/Main/Controls/Modal/ModalNavigationItem.m b/Stay/Main/Controls/Modal/ModalNavigationItem.m new file mode 100644 index 00000000..1bf63b24 --- /dev/null +++ b/Stay/Main/Controls/Modal/ModalNavigationItem.m @@ -0,0 +1,12 @@ +// +// ModalNavigationItem.m +// FastClip-iOS +// +// Created by ris on 2022/2/7. +// + +#import "ModalNavigationItem.h" + +@implementation ModalNavigationItem + +@end diff --git a/Stay/Main/Controls/Modal/ModalViewController.h b/Stay/Main/Controls/Modal/ModalViewController.h new file mode 100644 index 00000000..7711217f --- /dev/null +++ b/Stay/Main/Controls/Modal/ModalViewController.h @@ -0,0 +1,42 @@ +// +// ModalViewController.h +// FastClip-iOS +// +// Created by ris on 2022/2/7. +// + +#import +#import "ModalNavigationBar.h" +#import "ModalNavigationController.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface ModalResponder : UIViewController +@end + +@interface ModalViewController : NSObject + +@property (nonatomic, weak) ModalNavigationController *navigationController; +@property (nonatomic, strong, readonly) ModalNavigationBar *navigationBar; +@property (nonatomic, strong, readonly) UIView *view; +@property (nonatomic, assign) BOOL isRoot; +@property (nonatomic, strong) NSString *title; +// Set on create to hide the navigation bar or not. +@property (nonatomic, assign) BOOL hideNavigationBar; + +- (void)viewDidLoad; +- (void)viewWillAppear; +- (void)viewDidAppear; +- (void)viewWillDisappear; +- (void)viewDidDisappear; + +// Main View contains navigationBar & view. +- (CGSize)mainViewSize; +- (CGFloat)maxViewWidth; + +- (UIView *)getMainView; + +- (void)willSee; +@end + +NS_ASSUME_NONNULL_END diff --git a/Stay/Main/Controls/Modal/ModalViewController.m b/Stay/Main/Controls/Modal/ModalViewController.m new file mode 100644 index 00000000..c0f09445 --- /dev/null +++ b/Stay/Main/Controls/Modal/ModalViewController.m @@ -0,0 +1,93 @@ +// +// ModalViewController.m +// FastClip-iOS +// +// Created by ris on 2022/2/7. +// + +#import "ModalViewController.h" +#import "FCApp.h" + +@interface ModalViewController()< + ModalNavigationBarDelegate +>{ + UIView *_view; + ModalNavigationBar *_navigationBar; +} + +@property (nonatomic, strong) UIView *mainView; +@end + +@implementation ModalViewController + +- (instancetype)init{ + if (self = [super init]){ + [self mainView]; + [self.mainView addSubview:[self navigationBar]]; + [self.mainView addSubview:[self view]]; + } + + return self; +} + +- (void)setTitle:(NSString *)title{ + self.navigationBar.title = title; +} + +- (void)willSee{ + CGSize size = [self mainViewSize]; + [self.mainView setFrame:CGRectMake(0, 0, size.width, size.height)]; + [self.navigationBar setFrame:CGRectMake(0, 0, size.width, self.hideNavigationBar ? 0 : self.navigationBar.frame.size.height)]; + [self.view setFrame:CGRectMake(0, self.navigationBar.frame.size.height, size.width, size.height - self.navigationBar.frame.size.height)]; +} + + + +- (ModalNavigationBar *)navigationBar{ + if (nil == _navigationBar){ + _navigationBar = [[ModalNavigationBar alloc] init]; + _navigationBar.delegate = self; + } + + return _navigationBar; +} + +- (UIView *)view{ + if (nil == _view){ + _view = [[UIView alloc] init]; + } + + return _view; +} + +- (UIView *)mainView{ + if (nil == _mainView){ + _mainView = [[UIView alloc] initWithFrame:CGRectZero]; + } + + return _mainView; +} + +- (UIView *)getMainView{ + return self.mainView; +} + +- (CGSize)mainViewSize{ + return CGSizeZero; +} + +- (CGFloat)maxViewWidth{ + return MIN(FCApp.keyWindow.frame.size.width, 392); +} + +- (void)navigationBarDidClickCancelButton{ + [self.navigationController popModalViewController]; +} + + +- (void)viewWillAppear{} +- (void)viewDidAppear{} +- (void)viewWillDisappear{} +- (void)viewDidDisappear{} +- (void)viewDidLoad{} +@end diff --git a/Stay/Main/Controls/SlideController/FCBlockView.h b/Stay/Main/Controls/SlideController/FCBlockView.h new file mode 100644 index 00000000..c614c8a3 --- /dev/null +++ b/Stay/Main/Controls/SlideController/FCBlockView.h @@ -0,0 +1,23 @@ +// +// FCBlockView.h +// FastClip-iOS +// +// Created by ris on 2022/2/8. +// + +#import "FCView.h" + +NS_ASSUME_NONNULL_BEGIN + +@protocol FCBlockViewDelegate + +- (void)touched; + +@end + +@interface FCBlockView : FCView + +@property (nonatomic, assign) id delegate; +@end + +NS_ASSUME_NONNULL_END diff --git a/Stay/Main/Controls/SlideController/FCBlockView.m b/Stay/Main/Controls/SlideController/FCBlockView.m new file mode 100644 index 00000000..c8e45d87 --- /dev/null +++ b/Stay/Main/Controls/SlideController/FCBlockView.m @@ -0,0 +1,26 @@ +// +// FCBlockView.m +// FastClip-iOS +// +// Created by ris on 2022/2/8. +// + +#import "FCBlockView.h" + +@implementation FCBlockView + +- (instancetype)init{ + if (self = [super init]){ + self.backgroundColor = [UIColor colorWithWhite:0 alpha:0.1]; + } + + return self; +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{ + if (self.delegate && [self.delegate respondsToSelector:@selector(touched)]){ + [self.delegate touched]; + } +} + +@end diff --git a/Stay/Main/Controls/SlideController/FCPresenting.h b/Stay/Main/Controls/SlideController/FCPresenting.h new file mode 100644 index 00000000..522716bf --- /dev/null +++ b/Stay/Main/Controls/SlideController/FCPresenting.h @@ -0,0 +1,34 @@ +// +// FCPresenting.h +// FastClip-iOS +// +// Created by ris on 2022/2/7. +// + +#import + +NS_ASSUME_NONNULL_BEGIN +@class ModalNavigationController; +typedef enum { + FCPresentingFromBottom, + FCPresentingFromTop, +}FCPresentingFrom; + +@protocol FCPresenting + +- (void)show; +- (void)showWithParams:(NSArray *)params; +- (void)dismiss; +- (BOOL)isShown; +- (FCPresentingFrom)from; +- (CGFloat)marginToFrom; +- (CGFloat)keyboardMargin; +- (BOOL)blockAction; +- (ModalNavigationController *)modalNavigationController; +- (BOOL)disableRoundShadow; +- (BOOL)preventShortcuts; +- (BOOL)dismissable; +- (CGFloat)maxHeight; +@end + +NS_ASSUME_NONNULL_END diff --git a/Stay/Main/Controls/SlideController/FCRoundedShadowView.h b/Stay/Main/Controls/SlideController/FCRoundedShadowView.h new file mode 100644 index 00000000..ae9cc5bf --- /dev/null +++ b/Stay/Main/Controls/SlideController/FCRoundedShadowView.h @@ -0,0 +1,20 @@ +// +// FCRoundedShadowView.h +// FastClip-iOS +// +// Created by ris on 2022/2/9. +// + +#import "FCView.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface FCRoundedShadowView : FCView + +@property (readonly) FCView *containerView; + + +- (instancetype)initWithRadius:(CGFloat)radius; +@end + +NS_ASSUME_NONNULL_END diff --git a/Stay/Main/Controls/SlideController/FCRoundedShadowView.m b/Stay/Main/Controls/SlideController/FCRoundedShadowView.m new file mode 100644 index 00000000..1827ce50 --- /dev/null +++ b/Stay/Main/Controls/SlideController/FCRoundedShadowView.m @@ -0,0 +1,68 @@ +// +// FCRoundedShadowView.m +// FastClip-iOS +// +// Created by ris on 2022/2/9. +// + +#import "FCRoundedShadowView.h" +#import "FCStyle.h" + +@interface FCRoundedShadowView() + +@property (nonatomic, strong) FCView *containerView; +@property (nonatomic, assign) CGFloat radius; +@end + +@implementation FCRoundedShadowView + +- (instancetype)initWithRadius:(CGFloat)radius{ + if (self = [super init]){ + self.radius = radius; + self.layer.backgroundColor = [UIColor clearColor].CGColor; + self.layer.shadowColor = [UIColor blackColor].CGColor; + self.layer.shadowOffset = CGSizeMake(0, 1.0); + self.layer.shadowOpacity = 0.2; + self.layer.shadowRadius = MAX(10,self.radius); + + [self containerView]; + self.containerView.layer.cornerRadius = MAX(10,self.radius); + } + + return self; +} + +- (instancetype)init{ + if (self = [super init]){ + [self containerView]; + self.containerView.layer.cornerRadius = 0; + self.containerView.layer.borderWidth = 0; + } + + return self; +} + +- (void)setFrame:(CGRect)frame{ + [super setFrame:frame]; + [self.containerView setFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)]; +} + +- (FCView *)containerView{ + if (nil == _containerView){ + _containerView = [[FCView alloc] init]; + _containerView.backgroundColor = FCStyle.popup; + _containerView.layer.cornerRadius = MAX(10,self.radius); + _containerView.layer.borderColor = FCStyle.fcSeparator.CGColor; + _containerView.layer.borderWidth = 1; + _containerView.clipsToBounds = YES; + [self addSubview:_containerView]; + } + + return _containerView; +} + +- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { + self.containerView.layer.borderColor = FCStyle.fcSeparator.CGColor; +} + +@end diff --git a/Stay/Main/Controls/SlideController/FCSlideController.h b/Stay/Main/Controls/SlideController/FCSlideController.h new file mode 100644 index 00000000..e70b608b --- /dev/null +++ b/Stay/Main/Controls/SlideController/FCSlideController.h @@ -0,0 +1,26 @@ +// +// FCSlideController.h +// FastClip-iOS +// +// Created by ris on 2022/2/7. +// + +#import "FCView.h" + +#import "FCPresenting.h" +#import "FCBlockView.h" +#import "FCRoundedShadowView.h" + +NS_ASSUME_NONNULL_BEGIN + +extern NSNotificationName const _Nonnull FCSlideControllerDidDismissNotification; + +@interface FCSlideController : NSObject + +@property (nonatomic, strong) FCBlockView *blockView; +@property (nonatomic, weak) FCRoundedShadowView *navView; +- (void)layoutSubviews; +@end + +NS_ASSUME_NONNULL_END + diff --git a/Stay/Main/Controls/SlideController/FCSlideController.m b/Stay/Main/Controls/SlideController/FCSlideController.m new file mode 100644 index 00000000..a1abf644 --- /dev/null +++ b/Stay/Main/Controls/SlideController/FCSlideController.m @@ -0,0 +1,268 @@ +// +// FCSlideController.m +// FastClip-iOS +// +// Created by ris on 2022/2/7. +// + +#import "FCSlideController.h" + +#import "FCApp.h" +#import "ModalViewController.h" +#import "ModalNavigationController.h" + +NSNotificationName const _Nonnull FCSlideControllerDidDismissNotification = @"app.notification.FCSlideControllerDidDismissNotification"; + +@interface FCSlideController()< + CAAnimationDelegate, + FCBlockViewDelegate +>{ + +} + + +@property (nonatomic, assign) BOOL selfDismiss; +@property (nonatomic, strong) UISwipeGestureRecognizer *swipeGestureRecognizer; +@end + +@implementation FCSlideController + +- (instancetype)init{ + if (self = [super init]){ + [self swipeGestureRecognizer]; + } + + return self; +} + +- (UISwipeGestureRecognizer *)swipeGestureRecognizer{ + if (nil == _swipeGestureRecognizer){ + _swipeGestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self.navView action:@selector(swipeDown)]; + _swipeGestureRecognizer.direction = UISwipeGestureRecognizerDirectionDown; + + } + + return _swipeGestureRecognizer; +} + +- (void)swipeDown{ + if ([self dismissable]){ + [self dismiss]; + } +} + +- (void)show{ + self.selfDismiss = NO; + if ([self preventShortcuts]){} + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(adjustForKeyboard:) + name:UIKeyboardWillChangeFrameNotification + object:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(adjustForKeyboard:) + name:UIKeyboardDidChangeFrameNotification + object:nil]; + + UIWindow *keyWindow = FCApp.keyWindow; + CGSize windowSize = keyWindow.frame.size; + if ([self blockAction]){ + [[self blockView] setFrame:CGRectMake(0, 0, keyWindow.frame.size.width, keyWindow.frame.size.height)]; + [keyWindow addSubview:self.blockView]; + } + + self.navView = [self modalNavigationController].view; + + CGFloat navViewHeight = MIN([self maxHeight],self.navView.frame.size.height); + + + if ([self from] == FCPresentingFromBottom){ + [self.navView setFrame:CGRectMake((windowSize.width - self.navView.frame.size.width)/2, + windowSize.height + navViewHeight, + self.navView.frame.size.width, + navViewHeight)]; + } + else if ([self from] == FCPresentingFromTop){ + [self.navView setFrame:CGRectMake((windowSize.width - self.navView.frame.size.width)/2, + -navViewHeight, + self.navView.frame.size.width, + navViewHeight)]; + } + [[self modalNavigationController].rootModalViewController willSee]; + [[self modalNavigationController].rootModalViewController viewWillAppear]; + + [keyWindow addSubview:self.navView]; + + + [UIView animateWithDuration:0.3 + delay:0 + options:UIViewAnimationOptionCurveEaseOut + animations:^{ + if ([self from] == FCPresentingFromBottom){ + CGRect frame = self.navView.frame; + frame.origin.y -= (frame.size.height * 2 + [self marginToFrom]); + self.navView.frame = frame; + } + else if ([self from] == FCPresentingFromTop){ + CGRect frame = self.navView.frame; + frame.origin.y = [self marginToFrom]; + self.navView.frame = frame; + } + + } completion:^(BOOL finished) { + [keyWindow bringSubviewToFront:self.navView]; + }]; +} + +- (void)layoutSubviews{ + UIWindow *keyWindow = FCApp.keyWindow; + CGSize windowSize = keyWindow.frame.size; + [[self blockView] setFrame:CGRectMake(0, 0, windowSize.width, windowSize.height)]; + + CGFloat y = 0; + if ([self from] == FCPresentingFromBottom){ + y = windowSize.height - self.navView.frame.size.height - [self marginToFrom]; + } + else if ([self from] == FCPresentingFromTop){ + y = [self marginToFrom]; + } + + [self.navView setFrame:CGRectMake((windowSize.width - self.navView.frame.size.width)/2, y, self.navView.frame.size.width, self.navView.frame.size.height)]; +} + +- (ModalNavigationController *)modalNavigationController{ + return nil; +} + +- (BOOL)blockAction{ + return YES; +} + +- (FCPresentingFrom)from{ + return FCPresentingFromBottom; +} + +- (CGFloat)marginToFrom{ + return 0; +} + +- (CGFloat)keyboardMargin{ + return 10; +} + +- (void)dismiss { + self.selfDismiss = YES; + if ([self preventShortcuts]){} + [FCApp.keyWindow endEditing:YES]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillChangeFrameNotification object:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidChangeFrameNotification object:nil]; + UIWindow *keyWindow = FCApp.keyWindow; + CGSize windowSize = keyWindow.frame.size; + if ([self blockAction]){ + [[self blockView] removeFromSuperview]; + } + [UIView animateWithDuration:0.3 + delay:0 + options:UIViewAnimationOptionCurveEaseOut + animations:^{ + if ([self from] == FCPresentingFromBottom){ + [self.navView setFrame:CGRectMake(self.navView.frame.origin.x, windowSize.height + self.navView.frame.size.height, self.navView.frame.size.width, self.navView.frame.size.height)]; + } + else if ([self from] == FCPresentingFromTop){ + [self.navView setFrame:CGRectMake(self.navView.frame.origin.x, -self.navView.frame.size.height, self.navView.frame.size.width, self.navView.frame.size.height)]; + } + + } completion:^(BOOL finished) { + [self.navView removeFromSuperview]; + [[self modalNavigationController] popToRootControllerWithDismiss]; + [[NSNotificationCenter defaultCenter] postNotificationName:FCSlideControllerDidDismissNotification + object:self]; + }]; + +} + + +- (BOOL)isShown { + return self.navView && CGRectIntersectsRect(FCApp.keyWindow.frame, self.navView.frame); +} + + +- (void)showWithParams:(nonnull NSArray *)params { + +} + + +- (FCBlockView *)blockView{ + if (nil == _blockView){ + _blockView = [[FCBlockView alloc] init]; + NSLog(@"create block %@,%@",_blockView,self); + _blockView.delegate = self; + } + + return _blockView; +} + +- (void)touched{ + if ([self dismissable]){ + [self dismiss]; + } +} + +- (void)adjustForKeyboard:(NSNotification *)note{ + NSDictionary *info = [note userInfo]; + CGRect endRect = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; + if ([note.name isEqualToString:UIKeyboardWillChangeFrameNotification] && !self.selfDismiss){ + CGFloat newOriginY = endRect.origin.y - [self keyboardMargin] - self.navView.frame.size.height; + BOOL dismissKeyboardFirst = endRect.origin.y >= FCApp.keyWindow.frame.size.height; + if (dismissKeyboardFirst){ + newOriginY = FCApp.keyWindow.frame.size.height - self.navView.frame.size.height - [self marginToFrom]; + } + + [UIView animateWithDuration:0.3 + delay:0 + options:UIViewAnimationOptionCurveEaseOut + animations:^{ + if ([self from] == FCPresentingFromBottom){ + self.navView.frame = CGRectMake(self.navView.frame.origin.x, newOriginY, self.navView.frame.size.width, self.navView.frame.size.height); + } + + } completion:^(BOOL finished) { + }]; + } + else if ([note.name isEqualToString:UIKeyboardDidChangeFrameNotification] && self.selfDismiss){ + CGFloat newOriginY = endRect.origin.y - [self marginToFrom] - self.navView.frame.size.height; + [UIView animateWithDuration:0.3 + delay:0 + options:UIViewAnimationOptionCurveEaseOut + animations:^{ + if ([self from] == FCPresentingFromBottom){ + self.navView.frame = CGRectMake(self.navView.frame.origin.x, newOriginY, self.navView.frame.size.width, self.navView.frame.size.height); + } + + } completion:^(BOOL finished) { + }]; + } + +} + +- (BOOL)disableRoundShadow{ + return NO; +} + +- (BOOL)preventShortcuts{ + return YES; +} + +- (BOOL)dismissable{ + return YES; +} + +- (CGFloat)maxHeight{ + return CGFLOAT_MAX; +} + +- (void)dealloc{ + +} + +@end diff --git a/Stay/Main/Data/ActivateChanged.h b/Stay/Main/Data/ActivateChanged.h new file mode 100644 index 00000000..8100570b --- /dev/null +++ b/Stay/Main/Data/ActivateChanged.h @@ -0,0 +1,17 @@ +// +// ActivateChanged.h +// Stay +// +// Created by ris on 2022/5/31. +// + +#import "FCDisk.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface ActivateChanged : FCDisk + +@property (nonatomic, strong) NSDictionary *content; +@end + +NS_ASSUME_NONNULL_END diff --git a/Stay/Main/Data/ActivateChanged.m b/Stay/Main/Data/ActivateChanged.m new file mode 100644 index 00000000..726bb70a --- /dev/null +++ b/Stay/Main/Data/ActivateChanged.m @@ -0,0 +1,25 @@ +// +// ActivateChanged.m +// Stay +// +// Created by ris on 2022/5/31. +// + +#import "ActivateChanged.h" + +@implementation ActivateChanged + +- (void)unarchiveData:(NSData *)data{ + self.content = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; +} + +- (NSData *)archiveData{ + return [NSJSONSerialization dataWithJSONObject:self.content options:0 error:nil]; +} + + +- (dispatch_queue_t _Nonnull)dispatchQueue { + return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); +} + +@end diff --git a/Stay/Main/Data/DataManager/DataManager.h b/Stay/Main/Data/DataManager/DataManager.h index 31afe4d1..a313e202 100755 --- a/Stay/Main/Data/DataManager/DataManager.h +++ b/Stay/Main/Data/DataManager/DataManager.h @@ -16,27 +16,21 @@ - (NSArray *)findScript:(int)condition; -- (NSArray *)findScriptInLib; - - (void)updateScrpitStatus:(int)status numberId:(NSString *)uuid; - (UserScript *)selectScriptByUuid:(NSString *)uuid; - (NSArray *)selectScriptByKeywordByAdded:(NSString *)keyword; -- (NSArray *)selectScriptByKeywordByLib:(NSString *)keyword; - (void)insertToUserScriptnumberId:(NSString *)uuid; -- (void)updateLibScrpitStatus:(int)status numberId:(NSString *)uuid; - - (void)deleteScriptInUserScriptByNumberId:(NSString *)uuid; - (void)insertUserConfigByUserScript:(UserScript *)scrpitDetail; - (void)updateUserScript:(UserScript *)scrpitDetail; -- (void)updateScriptConfigByUserScript:(UserScript *)scrpitDetail; - (void)updateScriptConfigAutoupdate:(int)status numberId:(NSString *)uuid; @end diff --git a/Stay/Main/Data/DataManager/DataManager.m b/Stay/Main/Data/DataManager/DataManager.m index 574b0adf..f3dc6210 100644 --- a/Stay/Main/Data/DataManager/DataManager.m +++ b/Stay/Main/Data/DataManager/DataManager.m @@ -41,7 +41,6 @@ -(NSString*) copyFile2Documents:(NSString*)fileName // 如果目标目录也就是(Documents)目录没有数据库文件的时候,才会复制一份,否则不复制 if(![fileManager fileExistsAtPath:destPath]){ - NSString* sourcePath =[[NSBundle mainBundle] pathForResource:@"scriptManager" ofType:@"sqlite"]; if ([fileManager fileExistsAtPath:sourcePath]){ [fileManager copyItemAtPath:sourcePath toPath:destPath error:&error]; @@ -71,20 +70,6 @@ - (void)recoverBadData{ [self addColumn:@"script_config" column:@"notes"]; } - NSArray *list = [self findProjectSearchLib]; - for(int i = 0; i < list.count; i++) { - UserScript *scrpitDetail = list[i]; - UserScript *old = [self selectScriptConfigFromUUid:scrpitDetail.uuid]; - if(old == nil) { - [self insertScriptConfigByUserScript:scrpitDetail]; - } else { - NSInteger status = [SYVersionUtils compareVersion:scrpitDetail.version toVersion:old.version]; - if(status == 1) { - [self updateScriptConfigByUserScript:scrpitDetail]; - } - - } - } return; } @@ -194,121 +179,6 @@ - (BOOL)isExitedColumn:(NSString *)column { return activite == 0? false:true; } -//根据条件查询一组用户,模糊查询 DQL -- (NSArray *)findProjectSearchLib { - - NSMutableArray *scriptList = [NSMutableArray array]; - - //打开数据库 - sqlite3 *sqliteHandle = NULL; - int result = 0; - - NSString* destPath =[[NSBundle mainBundle] pathForResource:@"scriptManager" ofType:@"sqlite"]; - result = sqlite3_open([destPath - UTF8String], &sqliteHandle); - - if (result != SQLITE_OK) { - - NSLog(@"数据库文件打开失败"); - - return scriptList; - } - - //构造SQL语句 - - NSString *sql = @"SELECT * FROM script_config"; - - sqlite3_stmt *stmt = NULL; - result = sqlite3_prepare(sqliteHandle, [sql UTF8String], -1, &stmt, NULL); - if (result != SQLITE_OK) { - NSLog(@"Error %s while preparing statement", sqlite3_errmsg(sqliteHandle)); - NSLog(@"编译sql失败"); - sqlite3_close(sqliteHandle); - return scriptList; - } - -// 绑定占位符 -// NSString *queryCondition = [NSString stringWithFormat:@"%d", condition]; - //执行SQL语句,代表找到一条符合条件的数据,如果有多条数据符合条件,则要循环调用 - while(sqlite3_step(stmt) == SQLITE_ROW) { - - UserScript *scrpitDetail = [[UserScript alloc] init]; - - //第几列字段是从0开始 - scrpitDetail.uuid = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 1)== NULL?"":(const char *)sqlite3_column_text(stmt, 1)]; - scrpitDetail.name = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 2)== NULL?"":(const char *)sqlite3_column_text(stmt, 2)]; - scrpitDetail.namespace = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 3) == NULL?"":(const char *)sqlite3_column_text(stmt, 3)]; - scrpitDetail.author = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 4)== NULL?"":(const char *)sqlite3_column_text(stmt, 4)]; - scrpitDetail.version = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 5)== NULL?"":(const char *)sqlite3_column_text(stmt, 5)]; - scrpitDetail.desc = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 6)== NULL?"":(const char *)sqlite3_column_text(stmt, 6)]; - scrpitDetail.homepage = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 7)== NULL?"":(const char *)sqlite3_column_text(stmt, 7)]; - scrpitDetail.icon = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 8)== NULL?"":(const char *)sqlite3_column_text(stmt, 8)]; - - NSString * includesStr = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 9)== NULL?"":(const char *)sqlite3_column_text(stmt, 9)]; - if (includesStr != NULL && includesStr.length > 0) { - scrpitDetail.includes = [includesStr componentsSeparatedByString:@","]; - } else { - scrpitDetail.includes = @[]; - } - NSString * mathesStr = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 10)== NULL?"":(const char *)sqlite3_column_text(stmt, 10)]; - if (mathesStr != NULL && mathesStr.length > 0) { - scrpitDetail.mathes = [mathesStr componentsSeparatedByString:@","]; - } else { - scrpitDetail.mathes = @[]; - } - NSString * excludesStr = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 11)== NULL?"":(const char *)sqlite3_column_text(stmt, 11)]; - if (excludesStr != NULL && excludesStr.length > 0) { - scrpitDetail.excludes = [excludesStr componentsSeparatedByString:@","]; - } else { - scrpitDetail.excludes = @[]; - } - - scrpitDetail.runAt = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 12)== NULL?"":(const char *)sqlite3_column_text(stmt, 12)]; - - NSString * grantsStr = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 13)== NULL?"":(const char *)sqlite3_column_text(stmt, 13)]; - if (grantsStr != NULL && grantsStr.length > 0) { - scrpitDetail.grants = [grantsStr componentsSeparatedByString:@","]; - } else { - scrpitDetail.grants = @[]; - } - - - int noframes = sqlite3_column_int(stmt, 14); - scrpitDetail.noFrames = noframes == 0? false:true; - - scrpitDetail.content = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 15)== NULL?"":(const char *)sqlite3_column_text(stmt, 15)]; - - int activite = sqlite3_column_int(stmt, 16); - scrpitDetail.active = activite == 0? false:true; - - NSString * requiresUrlStr = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 17)== NULL?"":(const char *)sqlite3_column_text(stmt, 17)]; - if (requiresUrlStr != NULL && requiresUrlStr.length > 0) { - scrpitDetail.requireUrls = [requiresUrlStr componentsSeparatedByString:@","]; - } else { - scrpitDetail.requireUrls = @[]; - } - - - NSString *sourcePage = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 18)== NULL?"":(const char *)sqlite3_column_text(stmt, 18)]; - - scrpitDetail.sourcePage = sourcePage; - - NSString *updateUrl = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 19)== NULL?"":(const char *)sqlite3_column_text(stmt, 19)]; - scrpitDetail.updateUrl = updateUrl; - NSString *downloadUrl = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 20)== NULL?"":(const char *)sqlite3_column_text(stmt, 20)]; - scrpitDetail.downloadUrl = downloadUrl; - [[Tampermonkey shared] conventScriptContent:scrpitDetail]; - - [scriptList addObject:scrpitDetail]; - } - - sqlite3_finalize(stmt); - sqlite3_close(sqliteHandle); - - return scriptList; -} - - //根据条件查询一组用户,模糊查询 DQL - (NSArray *)findScript:(int)condition { @@ -337,7 +207,7 @@ - (NSArray *)findScript:(int)condition { //构造SQL语句 - NSString *sql = @"SELECT * FROM user_config_script"; + NSString *sql = @"SELECT * FROM user_config_script order by update_time desc"; sqlite3_stmt *stmt = NULL; result = sqlite3_prepare(sqliteHandle, [sql UTF8String], -1, &stmt, NULL); @@ -416,13 +286,16 @@ - (NSArray *)findScript:(int)condition { scrpitDetail.sourcePage = sourcePage; + double updateTime = sqlite3_column_double(stmt, 19); + scrpitDetail.updateTime = [NSString stringWithFormat:@"%f", updateTime]; + NSString *updateUrl = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 20)== NULL?"":(const char *)sqlite3_column_text(stmt, 20)]; scrpitDetail.updateUrl = updateUrl; NSString *downloadUrl = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 21)== NULL?"":(const char *)sqlite3_column_text(stmt, 21)]; scrpitDetail.downloadUrl = downloadUrl; int updateSwitch = sqlite3_column_int(stmt, 22); - scrpitDetail.updateSwitch = updateSwitch == 0? false:true; + scrpitDetail.updateSwitch = updateSwitch == 1? true:false; NSString * resourceUrl = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 23)== NULL?"":(const char *)sqlite3_column_text(stmt, 23)]; if (resourceUrl != NULL && resourceUrl.length > 0) { @@ -450,125 +323,6 @@ - (NSArray *)findScript:(int)condition { return scriptList; } -//根据条件查询一组用户,模糊查询 DQL -- (NSArray *)findScriptInLib{ - - NSMutableArray *scriptList = [NSMutableArray array]; - - //打开数据库 - sqlite3 *sqliteHandle = NULL; - int result = 0; - - NSArray *paths =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES); - NSString*documentsDirectory =[paths objectAtIndex:0]; - - NSString *destPath =[documentsDirectory stringByAppendingPathComponent:@"syScript.sqlite"]; - - - result = sqlite3_open([destPath - UTF8String], &sqliteHandle); - - if (result != SQLITE_OK) { - - NSLog(@"数据库文件打开失败"); - - return scriptList; - } - - //构造SQL语句 - - NSString *sql = @"SELECT * FROM script_config"; - - sqlite3_stmt *stmt = NULL; - result = sqlite3_prepare(sqliteHandle, [sql UTF8String], -1, &stmt, NULL); - if (result != SQLITE_OK) { - NSLog(@"Error %s while preparing statement", sqlite3_errmsg(sqliteHandle)); - NSLog(@"编译sql失败"); - sqlite3_close(sqliteHandle); - return scriptList; - - } - -// 绑定占位符 -// NSString *queryCondition = [NSString stringWithFormat:@"%d", condition]; - //执行SQL语句,代表找到一条符合条件的数据,如果有多条数据符合条件,则要循环调用 - while(sqlite3_step(stmt) == SQLITE_ROW) { - - UserScript *scrpitDetail = [[UserScript alloc] init]; - - //第几列字段是从0开始 - scrpitDetail.uuid = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 1)== NULL?"":(const char *)sqlite3_column_text(stmt, 1)]; - scrpitDetail.name = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 2)== NULL?"":(const char *)sqlite3_column_text(stmt, 2)]; - scrpitDetail.namespace = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 3) == NULL?"":(const char *)sqlite3_column_text(stmt, 3)]; - scrpitDetail.author = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 4)== NULL?"":(const char *)sqlite3_column_text(stmt, 4)]; - scrpitDetail.version = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 5)== NULL?"":(const char *)sqlite3_column_text(stmt, 5)]; - scrpitDetail.desc = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 6)== NULL?"":(const char *)sqlite3_column_text(stmt, 6)]; - scrpitDetail.homepage = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 7)== NULL?"":(const char *)sqlite3_column_text(stmt, 7)]; - scrpitDetail.icon = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 8)== NULL?"":(const char *)sqlite3_column_text(stmt, 8)]; - - NSString * includesStr = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 9)== NULL?"":(const char *)sqlite3_column_text(stmt, 9)]; - if (includesStr != NULL && includesStr.length > 0) { - scrpitDetail.includes = [includesStr componentsSeparatedByString:@","]; - } else { - scrpitDetail.includes = @[]; - } - NSString * mathesStr = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 10)== NULL?"":(const char *)sqlite3_column_text(stmt, 10)]; - if (mathesStr != NULL && mathesStr.length > 0) { - scrpitDetail.mathes = [mathesStr componentsSeparatedByString:@","]; - } else { - scrpitDetail.mathes = @[]; - } - NSString * excludesStr = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 11)== NULL?"":(const char *)sqlite3_column_text(stmt, 11)]; - if (excludesStr != NULL && excludesStr.length > 0) { - scrpitDetail.excludes = [excludesStr componentsSeparatedByString:@","]; - } else { - scrpitDetail.excludes = @[]; - } - - scrpitDetail.runAt = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 12)== NULL?"":(const char *)sqlite3_column_text(stmt, 12)]; - - NSString * grantsStr = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 13)== NULL?"":(const char *)sqlite3_column_text(stmt, 13)]; - if (grantsStr != NULL && grantsStr.length > 0) { - scrpitDetail.grants = [grantsStr componentsSeparatedByString:@","]; - } else { - scrpitDetail.grants = @[]; - } - - - int noframes = sqlite3_column_int(stmt, 14); - scrpitDetail.noFrames = noframes == 0? false:true; - - scrpitDetail.content = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 15)== NULL?"":(const char *)sqlite3_column_text(stmt, 15)]; - - int activite = sqlite3_column_int(stmt, 16); - scrpitDetail.active = activite == 0? false:true; - - NSString * requiresUrlStr = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 17)== NULL?"":(const char *)sqlite3_column_text(stmt, 17)]; - if (requiresUrlStr != NULL && requiresUrlStr.length > 0) { - scrpitDetail.requireUrls = [requiresUrlStr componentsSeparatedByString:@","]; - } else { - scrpitDetail.requireUrls = @[]; - } - - - NSString *sourcePage = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 18)== NULL?"":(const char *)sqlite3_column_text(stmt, 18)]; - - scrpitDetail.sourcePage = sourcePage; - NSString *updateUrl = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 19)== NULL?"":(const char *)sqlite3_column_text(stmt, 19)]; - scrpitDetail.updateUrl = updateUrl; - NSString *downloadUrl = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 20)== NULL?"":(const char *)sqlite3_column_text(stmt, 20)]; - scrpitDetail.downloadUrl = downloadUrl; - [[Tampermonkey shared] conventScriptContent:scrpitDetail]; - - [scriptList addObject:scrpitDetail]; - } - - sqlite3_finalize(stmt); - sqlite3_close(sqliteHandle); - - return scriptList; -} - - (void)updateScrpitStatus:(int)status numberId:(NSString *)uuid { //打开数据库 sqlite3 *sqliteHandle = NULL; @@ -593,159 +347,30 @@ - (void)updateScrpitStatus:(int)status numberId:(NSString *)uuid { //构造SQL语句 NSString *sql = @"UPDATE user_config_script SET active = ? WHERE uuid = ? "; - - sqlite3_stmt *stmt = NULL; - result = sqlite3_prepare(sqliteHandle, [sql UTF8String], -1, &stmt, NULL); - if (result != SQLITE_OK) { - NSLog(@"Error %s while preparing statement", sqlite3_errmsg(sqliteHandle)); - NSLog(@"编译sql失败"); - sqlite3_close(sqliteHandle); - return; - } -// 绑定占位符 - sqlite3_bind_int(stmt, 1, status); - sqlite3_bind_text(stmt, 2, [uuid UTF8String], -1, NULL); -// if (sqlite3_prepare_v2(sqliteHandle, [sql UTF8String], -1, &stmt, nil) == SQLITE_OK) -// { -// } - //执行SQL语句,代表找到一条符合条件的数据,如果有多条数据符合条件,则要循环调用 - if (sqlite3_step(stmt) != SQLITE_DONE) { - sqlite3_finalize(stmt); - } - sqlite3_close(sqliteHandle); - -} - -- (NSArray *)selectScriptByKeywordByAdded:(NSString *)keyword { - NSMutableArray *scriptList = [NSMutableArray array]; - - //打开数据库 - sqlite3 *sqliteHandle = NULL; - int result = 0; - - NSArray *paths =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES); - NSString*documentsDirectory =[paths objectAtIndex:0]; - - NSString *destPath =[documentsDirectory stringByAppendingPathComponent:@"syScript.sqlite"]; - - - result = sqlite3_open([destPath - UTF8String], &sqliteHandle); - - if (result != SQLITE_OK) { - - NSLog(@"数据库文件打开失败"); - - return scriptList; - } - - //构造SQL语句 - - NSString *sql= [NSString stringWithFormat:@"SELECT * FROM user_config_script WHERE name like '%%%@%%';",keyword]; - - - - sqlite3_stmt *stmt = NULL; - - - result = sqlite3_prepare(sqliteHandle, [sql UTF8String], -1, &stmt, NULL); - if (result != SQLITE_OK) { - NSLog(@"Error %s while preparing statement", sqlite3_errmsg(sqliteHandle)); - NSLog(@"编译sql失败"); - sqlite3_close(sqliteHandle); - return scriptList; - - } - -// sqlite3_bind_text(stmt, 1, [keyword UTF8String], -1, NULL); -// sqlite3_bind_text(stmt, 2, [keyword UTF8String], -1, NULL); -// sqlite3_bind_text(stmt, 3, [keyword UTF8String], -1, NULL); - -// 绑定占位符 -// NSString *queryCondition = [NSString stringWithFormat:@"%d", condition]; - //执行SQL语句,代表找到一条符合条件的数据,如果有多条数据符合条件,则要循环调用 - while(sqlite3_step(stmt) == SQLITE_ROW) { - - UserScript *scrpitDetail = [[UserScript alloc] init]; - - //第几列字段是从0开始 - scrpitDetail.uuid = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 1)== NULL?"":(const char *)sqlite3_column_text(stmt, 1)]; - scrpitDetail.name = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 2)== NULL?"":(const char *)sqlite3_column_text(stmt, 2)]; - scrpitDetail.namespace = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 3) == NULL?"":(const char *)sqlite3_column_text(stmt, 3)]; - scrpitDetail.author = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 4)== NULL?"":(const char *)sqlite3_column_text(stmt, 4)]; - scrpitDetail.version = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 5)== NULL?"":(const char *)sqlite3_column_text(stmt, 5)]; - scrpitDetail.desc = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 6)== NULL?"":(const char *)sqlite3_column_text(stmt, 6)]; - scrpitDetail.homepage = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 7)== NULL?"":(const char *)sqlite3_column_text(stmt, 7)]; - scrpitDetail.icon = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 8)== NULL?"":(const char *)sqlite3_column_text(stmt, 8)]; - - NSString * includesStr = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 9)== NULL?"":(const char *)sqlite3_column_text(stmt, 9)]; - if (includesStr != NULL && includesStr.length > 0) { - scrpitDetail.includes = [includesStr componentsSeparatedByString:@","]; - } else { - scrpitDetail.includes = @[]; - } - NSString * mathesStr = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 10)== NULL?"":(const char *)sqlite3_column_text(stmt, 10)]; - if (mathesStr != NULL && mathesStr.length > 0) { - scrpitDetail.mathes = [mathesStr componentsSeparatedByString:@","]; - } else { - scrpitDetail.mathes = @[]; - } - NSString * excludesStr = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 11)== NULL?"":(const char *)sqlite3_column_text(stmt, 11)]; - if (excludesStr != NULL && excludesStr.length > 0) { - scrpitDetail.excludes = [excludesStr componentsSeparatedByString:@","]; - } else { - scrpitDetail.excludes = @[]; - } - - scrpitDetail.runAt = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 12)== NULL?"":(const char *)sqlite3_column_text(stmt, 12)]; - - NSString * grantsStr = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 13)== NULL?"":(const char *)sqlite3_column_text(stmt, 13)]; - if (grantsStr != NULL && grantsStr.length > 0) { - scrpitDetail.grants = [grantsStr componentsSeparatedByString:@","]; - } else { - scrpitDetail.grants = @[]; - } - - - int noframes = sqlite3_column_int(stmt, 14); - scrpitDetail.noFrames = noframes == 0? false:true; - - scrpitDetail.content = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 15)== NULL?"":(const char *)sqlite3_column_text(stmt, 15)]; - - int activite = sqlite3_column_int(stmt, 16); - scrpitDetail.active = activite == 0? false:true; - - NSString * requiresUrlStr = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 17)== NULL?"":(const char *)sqlite3_column_text(stmt, 17)]; - if (requiresUrlStr != NULL && requiresUrlStr.length > 0) { - scrpitDetail.requireUrls = [requiresUrlStr componentsSeparatedByString:@","]; - } else { - scrpitDetail.requireUrls = @[]; - } - - - NSString *sourcePage = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 18)== NULL?"":(const char *)sqlite3_column_text(stmt, 18)]; - - scrpitDetail.sourcePage = sourcePage; - - NSString *updateUrl = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 20)== NULL?"":(const char *)sqlite3_column_text(stmt, 20)]; - scrpitDetail.updateUrl = updateUrl; - NSString *downloadUrl = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 21)== NULL?"":(const char *)sqlite3_column_text(stmt, 21)]; - scrpitDetail.downloadUrl = downloadUrl; - int updateSwitch = sqlite3_column_int(stmt, 22); - scrpitDetail.updateSwitch = updateSwitch == 0? false:true; - - [[Tampermonkey shared] conventScriptContent:scrpitDetail]; - - [scriptList addObject:scrpitDetail]; + + sqlite3_stmt *stmt = NULL; + result = sqlite3_prepare(sqliteHandle, [sql UTF8String], -1, &stmt, NULL); + if (result != SQLITE_OK) { + NSLog(@"Error %s while preparing statement", sqlite3_errmsg(sqliteHandle)); + NSLog(@"编译sql失败"); + sqlite3_close(sqliteHandle); + return; + } +// 绑定占位符 + sqlite3_bind_int(stmt, 1, status); + sqlite3_bind_text(stmt, 2, [uuid UTF8String], -1, NULL); +// if (sqlite3_prepare_v2(sqliteHandle, [sql UTF8String], -1, &stmt, nil) == SQLITE_OK) +// { +// } + //执行SQL语句,代表找到一条符合条件的数据,如果有多条数据符合条件,则要循环调用 + if (sqlite3_step(stmt) != SQLITE_DONE) { + sqlite3_finalize(stmt); } - - sqlite3_finalize(stmt); sqlite3_close(sqliteHandle); - return scriptList; } -- (NSArray *)selectScriptByKeywordByLib:(NSString *)keyword { +- (NSArray *)selectScriptByKeywordByAdded:(NSString *)keyword { NSMutableArray *scriptList = [NSMutableArray array]; //打开数据库 @@ -770,7 +395,9 @@ - (NSArray *)selectScriptByKeywordByLib:(NSString *)keyword { //构造SQL语句 - NSString *sql= [NSString stringWithFormat:@"SELECT * FROM script_config WHERE name like '%%%@%%';",keyword]; + NSString *sql= [NSString stringWithFormat:@"SELECT * FROM user_config_script WHERE name like '%%%@%%';",keyword]; + + sqlite3_stmt *stmt = NULL; @@ -853,11 +480,14 @@ - (NSArray *)selectScriptByKeywordByLib:(NSString *)keyword { NSString *sourcePage = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 18)== NULL?"":(const char *)sqlite3_column_text(stmt, 18)]; scrpitDetail.sourcePage = sourcePage; - NSString *updateUrl = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 20)== NULL?"":(const char *)sqlite3_column_text(stmt, 19)]; + + NSString *updateUrl = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 20)== NULL?"":(const char *)sqlite3_column_text(stmt, 20)]; scrpitDetail.updateUrl = updateUrl; - NSString *downloadUrl = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 21)== NULL?"":(const char *)sqlite3_column_text(stmt, 20)]; + NSString *downloadUrl = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 21)== NULL?"":(const char *)sqlite3_column_text(stmt, 21)]; scrpitDetail.downloadUrl = downloadUrl; - + int updateSwitch = sqlite3_column_int(stmt, 22); + scrpitDetail.updateSwitch = updateSwitch == 1? true:false; + [[Tampermonkey shared] conventScriptContent:scrpitDetail]; [scriptList addObject:scrpitDetail]; @@ -867,9 +497,9 @@ - (NSArray *)selectScriptByKeywordByLib:(NSString *)keyword { sqlite3_close(sqliteHandle); return scriptList; - } + - (void)insertToUserScriptnumberId:(NSString *)uuid { //打开数据库 @@ -998,7 +628,7 @@ - (void)insertUserConfigByUserScript:(UserScript *)scrpitDetail { return; } - NSString *sql = @"INSERT INTO user_config_script (uuid, name, namespace, author, version, desc, homepage, icon, includes,maches,excludes,runAt,grants,noFrames,content,active,requireUrls,sourcePage,updateUrl,downloadUrl,notes,resourceUrl) VALUES (?, ?, ?, ?, ?, ?, ?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; + NSString *sql = @"INSERT INTO user_config_script (uuid, name, namespace, author, version, desc, homepage, icon, includes,maches,excludes,runAt,grants,noFrames,content,active,requireUrls,sourcePage,updateUrl,downloadUrl,notes,resourceUrl,update_time,switch) VALUES (?, ?, ?, ?, ?, ?, ?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; sqlite3_stmt *statement; @@ -1061,6 +691,12 @@ - (void)insertUserConfigByUserScript:(UserScript *)scrpitDetail { } else { sqlite3_bind_text(statement, 22, NULL, -1,NULL); } + + NSDate* date = [NSDate dateWithTimeIntervalSinceNow:0]; + NSTimeInterval a=[date timeIntervalSince1970]*1000; // *1000 是精确到毫秒,不乘就是精确到秒 + NSString *timeString = [NSString stringWithFormat:@"%.0f", a]; + sqlite3_bind_double(statement, 23, timeString.doubleValue); + sqlite3_bind_int(statement, 24, 0); } @@ -1072,50 +708,6 @@ - (void)insertUserConfigByUserScript:(UserScript *)scrpitDetail { } -- (void)updateLibScrpitStatus:(int)status numberId:(NSString *)uuid{ - //打开数据库 - sqlite3 *sqliteHandle = NULL; - int result = 0; - - NSArray *paths =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES); - NSString*documentsDirectory =[paths objectAtIndex:0]; - - NSString *destPath =[documentsDirectory stringByAppendingPathComponent:@"syScript.sqlite"]; - - - result = sqlite3_open([destPath - UTF8String], &sqliteHandle); - - if (result != SQLITE_OK) { - - NSLog(@"数据库文件打开失败"); - - return; - } - - //构造SQL语句 - - NSString *sql = @"UPDATE script_config SET active = ? WHERE uuid = ? "; - - sqlite3_stmt *stmt = NULL; - result = sqlite3_prepare(sqliteHandle, [sql UTF8String], -1, &stmt, NULL); - if (result != SQLITE_OK) { - NSLog(@"Error %s while preparing statement", sqlite3_errmsg(sqliteHandle)); - NSLog(@"编译sql失败"); - sqlite3_close(sqliteHandle); - return; - } -// 绑定占位符 - sqlite3_bind_int(stmt, 1, status); - sqlite3_bind_text(stmt, 2, [uuid UTF8String], -1, NULL); - - //执行SQL语句,代表找到一条符合条件的数据,如果有多条数据符合条件,则要循环调用 - if (sqlite3_step(stmt) != SQLITE_DONE) { - sqlite3_finalize(stmt); - } - sqlite3_close(sqliteHandle); -} - - (void)deleteScriptInUserScriptByNumberId:(NSString *)uuid{ @@ -1181,7 +773,7 @@ - (void)updateUserScript:(UserScript *)scrpitDetail { return; } - NSString *sql = @"UPDATE user_config_script set name = ?, namespace = ?, author = ?, version = ?, desc = ?, homepage = ?, icon = ?, includes= ?,maches= ?,excludes= ?,runAt= ?,grants= ?,noFrames= ?,content= ?,active= ?,requireUrls= ?,sourcePage= ?,updateUrl = ?,downloadUrl = ?,notes = ?,resourceUrl = ? where uuid = ?"; + NSString *sql = @"UPDATE user_config_script set name = ?, namespace = ?, author = ?, version = ?, desc = ?, homepage = ?, icon = ?, includes= ?,maches= ?,excludes= ?,runAt= ?,grants= ?,noFrames= ?,content= ?,active= ?,requireUrls= ?,sourcePage= ?,updateUrl = ?,downloadUrl = ?,notes = ?,resourceUrl = ?, update_time = ? where uuid = ?"; sqlite3_stmt *statement; @@ -1242,7 +834,12 @@ - (void)updateUserScript:(UserScript *)scrpitDetail { } else { sqlite3_bind_text(statement, 21, NULL, -1,NULL); } - sqlite3_bind_text(statement, 22,scrpitDetail.uuid != NULL? [scrpitDetail.uuid UTF8String]:[[[NSUUID UUID] UUIDString] UTF8String], -1,NULL); + NSDate* date = [NSDate dateWithTimeIntervalSinceNow:0]; + NSTimeInterval a=[date timeIntervalSince1970]*1000; // *1000 是精确到毫秒,不乘就是精确到秒 + NSString *timeString = [NSString stringWithFormat:@"%.0f", a]; + sqlite3_bind_double(statement, 22, timeString.doubleValue); + + sqlite3_bind_text(statement, 23,scrpitDetail.uuid != NULL? [scrpitDetail.uuid UTF8String]:[[[NSUUID UUID] UUIDString] UTF8String], -1,NULL); } @@ -1347,13 +944,15 @@ - (UserScript *)selectScriptByUuid:(NSString *)uuid { NSString *sourcePage = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 18)== NULL?"":(const char *)sqlite3_column_text(stmt, 18)]; scrpitDetail.sourcePage = sourcePage; + double updateTime = sqlite3_column_double(stmt, 19); + scrpitDetail.updateTime = [NSString stringWithFormat:@"%f", updateTime]; NSString *updateUrl = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 20)== NULL?"":(const char *)sqlite3_column_text(stmt, 20)]; scrpitDetail.updateUrl = updateUrl; NSString *downloadUrl = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 21)== NULL?"":(const char *)sqlite3_column_text(stmt, 21)]; scrpitDetail.downloadUrl = downloadUrl; int updateSwitch = sqlite3_column_int(stmt, 22); - scrpitDetail.updateSwitch = updateSwitch == 0? false:true; + scrpitDetail.updateSwitch = updateSwitch == 1? true:false; NSString * resourceUrl = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 23)== NULL?"":(const char *)sqlite3_column_text(stmt, 23)]; if (resourceUrl != NULL && resourceUrl.length > 0) { @@ -1379,188 +978,6 @@ - (UserScript *)selectScriptByUuid:(NSString *)uuid { return scrpitDetail; } - -- (UserScript *)selectScriptConfigFromUUid:(NSString *)uuid { - sqlite3 *sqliteHandle = NULL; - int result = 0; - - NSArray *paths =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES); - NSString*documentsDirectory =[paths objectAtIndex:0]; - - NSString *destPath =[documentsDirectory stringByAppendingPathComponent:@"syScript.sqlite"]; - - result = sqlite3_open([destPath - UTF8String], &sqliteHandle); - - if (result != SQLITE_OK) { - NSLog(@"数据库文件打开失败"); - return nil; - } - - NSString *sql= @"SELECT * FROM script_config WHERE uuid = ?"; - sqlite3_stmt *stmt = NULL; - result = sqlite3_prepare(sqliteHandle, [sql UTF8String], -1, &stmt, NULL); - if (result != SQLITE_OK) { - NSLog(@"Error %s while preparing statement", sqlite3_errmsg(sqliteHandle)); - NSLog(@"编译sql失败"); - sqlite3_close(sqliteHandle); - return nil; - - } - sqlite3_bind_text(stmt, 1, [uuid UTF8String], -1, NULL); - - UserScript *scrpitDetail = [[UserScript alloc] init]; - - while(sqlite3_step(stmt) == SQLITE_ROW) { - - //第几列字段是从0开始 - scrpitDetail.uuid = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 1)== NULL?"":(const char *)sqlite3_column_text(stmt, 1)]; - scrpitDetail.name = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 2)== NULL?"":(const char *)sqlite3_column_text(stmt, 2)]; - scrpitDetail.namespace = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 3) == NULL?"":(const char *)sqlite3_column_text(stmt, 3)]; - scrpitDetail.author = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 4)== NULL?"":(const char *)sqlite3_column_text(stmt, 4)]; - scrpitDetail.version = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 5)== NULL?"":(const char *)sqlite3_column_text(stmt, 5)]; - scrpitDetail.desc = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 6)== NULL?"":(const char *)sqlite3_column_text(stmt, 6)]; - scrpitDetail.homepage = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 7)== NULL?"":(const char *)sqlite3_column_text(stmt, 7)]; - scrpitDetail.icon = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 8)== NULL?"":(const char *)sqlite3_column_text(stmt, 8)]; - - NSString * includesStr = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 9)== NULL?"":(const char *)sqlite3_column_text(stmt, 9)]; - if (includesStr != NULL && includesStr.length > 0) { - scrpitDetail.includes = [includesStr componentsSeparatedByString:@","]; - } else { - scrpitDetail.includes = @[]; - } - NSString * mathesStr = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 10)== NULL?"":(const char *)sqlite3_column_text(stmt, 10)]; - if (mathesStr != NULL && mathesStr.length > 0) { - scrpitDetail.mathes = [mathesStr componentsSeparatedByString:@","]; - } else { - scrpitDetail.mathes = @[]; - } - NSString * excludesStr = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 11)== NULL?"":(const char *)sqlite3_column_text(stmt, 11)]; - if (excludesStr != NULL && excludesStr.length > 0) { - scrpitDetail.excludes = [excludesStr componentsSeparatedByString:@","]; - } else { - scrpitDetail.excludes = @[]; - } - - scrpitDetail.runAt = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 12)== NULL?"":(const char *)sqlite3_column_text(stmt, 12)]; - - NSString * grantsStr = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 13)== NULL?"":(const char *)sqlite3_column_text(stmt, 13)]; - if (grantsStr != NULL && grantsStr.length > 0) { - scrpitDetail.grants = [grantsStr componentsSeparatedByString:@","]; - } else { - scrpitDetail.grants = @[]; - } - - - int noframes = sqlite3_column_int(stmt, 14); - scrpitDetail.noFrames = noframes == 0? false:true; - - scrpitDetail.content = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 15)== NULL?"":(const char *)sqlite3_column_text(stmt, 15)]; - - int activite = sqlite3_column_int(stmt, 16); - scrpitDetail.active = activite == 0? false:true; - - NSString * requiresUrlStr = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 17)== NULL?"":(const char *)sqlite3_column_text(stmt, 17)]; - if (requiresUrlStr != NULL && requiresUrlStr.length > 0) { - scrpitDetail.requireUrls = [requiresUrlStr componentsSeparatedByString:@","]; - } else { - scrpitDetail.requireUrls = @[]; - } - - NSString *sourcePage = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 18)== NULL?"":(const char *)sqlite3_column_text(stmt, 18)]; - - NSString *updateUrl = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 19)== NULL?"":(const char *)sqlite3_column_text(stmt, 19)]; - scrpitDetail.updateUrl = updateUrl; - NSString *downloadUrl = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 20)== NULL?"":(const char *)sqlite3_column_text(stmt, 20)]; - scrpitDetail.downloadUrl = downloadUrl; - - scrpitDetail.sourcePage = sourcePage; - } - sqlite3_finalize(stmt); - sqlite3_close(sqliteHandle); - return scrpitDetail; -} - -- (void)updateScriptConfigByUserScript:(UserScript *)scrpitDetail { - //打开数据库 - sqlite3 *sqliteHandle = NULL; - int result = 0; - - NSArray *paths =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES); - NSString*documentsDirectory =[paths objectAtIndex:0]; - - NSString *destPath =[documentsDirectory stringByAppendingPathComponent:@"syScript.sqlite"]; - - - result = sqlite3_open_v2([destPath UTF8String], &sqliteHandle, SQLITE_OPEN_READWRITE, NULL); - - if (result != SQLITE_OK) { - - NSLog(@"数据库文件打开失败"); - return; - } - - NSString *sql = @"UPDATE script_config set name = ?, namespace = ?, author = ?, version = ?, desc = ?, homepage = ?, icon = ?, includes = ? ,maches= ?,excludes = ?,runAt = ?,grants = ?,noFrames = ?,content = ?,active = ?,requireUrls = ?,sourcePage = ?,updateUrl= ?,downloadUrl = ? WHERE uuid = ?"; - - sqlite3_stmt *statement; - - if (sqlite3_prepare_v2(sqliteHandle, [sql UTF8String], -1, &statement, nil) == SQLITE_OK) { - sqlite3_bind_text(statement, 20,scrpitDetail.uuid != NULL? [scrpitDetail.uuid UTF8String]:[[[NSUUID UUID] UUIDString] UTF8String], -1,NULL); - sqlite3_bind_text(statement, 1,scrpitDetail.name != NULL? [scrpitDetail.name UTF8String]:NULL, -1,NULL); - sqlite3_bind_text(statement, 2,scrpitDetail.namespace !=NULL? [scrpitDetail.namespace UTF8String]:NULL, -1,NULL); - sqlite3_bind_text(statement, 3,scrpitDetail.author != NULL? [scrpitDetail.author UTF8String]:NULL, -1,NULL); - sqlite3_bind_text(statement, 4,scrpitDetail.version != NULL? [scrpitDetail.version UTF8String]:NULL, -1,NULL); - sqlite3_bind_text(statement, 5, [scrpitDetail.desc UTF8String], -1,NULL); - sqlite3_bind_text(statement, 6, [scrpitDetail.homepage UTF8String], -1,NULL); - sqlite3_bind_text(statement, 7, [scrpitDetail.icon UTF8String], -1,NULL); - if(scrpitDetail.includes.count > 0) { - sqlite3_bind_text(statement, 8, [[scrpitDetail.includes componentsJoinedByString:@","] UTF8String], -1,NULL); - } else { - sqlite3_bind_text(statement, 8, NULL, -1,NULL); - } - - if(scrpitDetail.mathes.count > 0) { - sqlite3_bind_text(statement, 9, [[scrpitDetail.mathes componentsJoinedByString:@","] UTF8String], -1,NULL); - } else { - sqlite3_bind_text(statement, 9, NULL, -1,NULL); - } - if(scrpitDetail.excludes.count > 0) { - sqlite3_bind_text(statement, 10, [[scrpitDetail.excludes componentsJoinedByString:@","] UTF8String], -1,NULL); - } else { - sqlite3_bind_text(statement, 10, NULL, -1,NULL); - } - - sqlite3_bind_text(statement, 11, [scrpitDetail.runAt UTF8String], -1,NULL); - - if(scrpitDetail.grants.count > 0) { - sqlite3_bind_text(statement, 12, [[scrpitDetail.grants componentsJoinedByString:@","] UTF8String], -1,NULL); - } else { - sqlite3_bind_text(statement, 12, NULL, -1,NULL); - } - sqlite3_bind_int(statement, 13, scrpitDetail.noFrames?1:0); - sqlite3_bind_text(statement, 14, [scrpitDetail.content UTF8String], -1,NULL); - sqlite3_bind_int(statement, 15, scrpitDetail.active?1:0); - if(scrpitDetail.requireUrls.count > 0) { - sqlite3_bind_text(statement, 16, [[scrpitDetail.requireUrls componentsJoinedByString:@","] UTF8String], -1,NULL); - } else { - sqlite3_bind_text(statement, 16, NULL, -1,NULL); - } - sqlite3_bind_text(statement, 17, [scrpitDetail.sourcePage UTF8String], -1,NULL); - - sqlite3_bind_text(statement, 18, [scrpitDetail.updateUrl UTF8String], -1,NULL); - - sqlite3_bind_text(statement, 19, [scrpitDetail.downloadUrl UTF8String], -1,NULL); - } - - NSInteger resultCode = sqlite3_step(statement); - if (resultCode != SQLITE_DONE) { - sqlite3_finalize(statement); - } - sqlite3_close(sqliteHandle); - -} - - - (void)insertScriptConfigByUserScript:(UserScript *)scrpitDetail { //打开数据库 sqlite3 *sqliteHandle = NULL; @@ -1682,4 +1099,5 @@ - (void)updateScriptConfigAutoupdate:(int)status numberId:(NSString *)uuid { } + @end diff --git a/Stay/Main/Data/SharedStorageManager.h b/Stay/Main/Data/SharedStorageManager.h new file mode 100644 index 00000000..af475584 --- /dev/null +++ b/Stay/Main/Data/SharedStorageManager.h @@ -0,0 +1,33 @@ +// +// SharedStorageManager.h +// Stay +// +// Created by ris on 2022/5/31. +// + +#import +#import "UserscriptHeaders.h" +#import "UserscriptInfo.h" +#import "ActivateChanged.h" + +NS_ASSUME_NONNULL_BEGIN + +static inline NSString * _Nonnull FCSharedDirectory(void){ + return [[[NSFileManager defaultManager] + containerURLForSecurityApplicationGroupIdentifier: + @"group.com.dajiu.stay.pro"] path]; +} + +static inline NSString * _Nonnull FCDataDirectory(void){ + return [FCSharedDirectory() stringByAppendingPathComponent:@".stay/data"]; +} + +@interface SharedStorageManager : NSObject + ++ (instancetype)shared; +@property (nonatomic, strong, nullable) UserscriptHeaders *userscriptHeaders; +@property (nonatomic, strong, nullable) ActivateChanged *activateChanged; +- (UserscriptInfo *)getInfoOfUUID:(NSString *)uuid; +@end + +NS_ASSUME_NONNULL_END diff --git a/Stay/Main/Data/SharedStorageManager.m b/Stay/Main/Data/SharedStorageManager.m new file mode 100644 index 00000000..280426b2 --- /dev/null +++ b/Stay/Main/Data/SharedStorageManager.m @@ -0,0 +1,50 @@ +// +// SharedStorageManager.m +// Stay +// +// Created by ris on 2022/5/31. +// + +#import "SharedStorageManager.h" + +@implementation SharedStorageManager + +static SharedStorageManager *_instance = nil; ++ (SharedStorageManager *)shared{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + if (nil == _instance){ + _instance = [[SharedStorageManager alloc] init]; + } + }); + return _instance; +} + +- (UserscriptInfo *)getInfoOfUUID:(NSString *)uuid{ + UserscriptInfo *userscriptInfo = [[UserscriptInfo alloc] initWithPath:[FCDataDirectory() + stringByAppendingPathComponent:uuid] + isDirectory:NO]; + return userscriptInfo; +} + +- (UserscriptHeaders *)userscriptHeaders{ + if (nil == _userscriptHeaders){ + _userscriptHeaders = [[UserscriptHeaders alloc] initWithPath:[FCDataDirectory() + stringByAppendingPathComponent:@"userscriptHeaders"] + isDirectory:NO]; + } + + return _userscriptHeaders; +} + +- (ActivateChanged *)activateChanged{ + if (nil == _activateChanged){ + _activateChanged = [[ActivateChanged alloc] initWithPath:[FCDataDirectory() + stringByAppendingPathComponent:@"activateChanged"] + isDirectory:NO]; + } + + return _activateChanged; +} + +@end diff --git a/Stay/Main/Data/UserscriptHeaders.h b/Stay/Main/Data/UserscriptHeaders.h new file mode 100644 index 00000000..a6734dcd --- /dev/null +++ b/Stay/Main/Data/UserscriptHeaders.h @@ -0,0 +1,17 @@ +// +// UserscriptHeaders.h +// Stay +// +// Created by ris on 2022/5/31. +// + +#import "FCDisk.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface UserscriptHeaders : FCDisk + +@property (nonatomic, strong) NSArray *content; +@end + +NS_ASSUME_NONNULL_END diff --git a/Stay/Main/Data/UserscriptHeaders.m b/Stay/Main/Data/UserscriptHeaders.m new file mode 100644 index 00000000..1a431000 --- /dev/null +++ b/Stay/Main/Data/UserscriptHeaders.m @@ -0,0 +1,38 @@ +// +// UserscriptHeaders.m +// Stay +// +// Created by ris on 2022/5/31. +// + +#import "UserscriptHeaders.h" + +@interface UserscriptHeaders(){ + dispatch_queue_t _userscriptQueue; +} +@end + +@implementation UserscriptHeaders + +- (instancetype)initWithPath:(NSString *)path isDirectory:(BOOL)isDirectory{ + if (self = [super initWithPath:path isDirectory:isDirectory]){ + _userscriptQueue = dispatch_queue_create([[self queueName:@"userscriptHeaders"] UTF8String], + DISPATCH_QUEUE_SERIAL); + } + + return self; +} + +- (void)unarchiveData:(NSData *)data{ + self.content = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; +} + +- (NSData *)archiveData{ + return [NSJSONSerialization dataWithJSONObject:self.content options:0 error:nil]; +} + +- (dispatch_queue_t)dispatchQueue{ + return _userscriptQueue; +} + +@end diff --git a/Stay/Main/Data/UserscriptInfo.h b/Stay/Main/Data/UserscriptInfo.h new file mode 100644 index 00000000..76fbcdc8 --- /dev/null +++ b/Stay/Main/Data/UserscriptInfo.h @@ -0,0 +1,17 @@ +// +// UserscriptInfo.h +// Stay +// +// Created by ris on 2022/5/31. +// + +#import "FCDisk.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface UserscriptInfo : FCDisk + +@property (nonatomic, strong) NSDictionary *content; +@end + +NS_ASSUME_NONNULL_END diff --git a/Stay/Main/Data/UserscriptInfo.m b/Stay/Main/Data/UserscriptInfo.m new file mode 100644 index 00000000..0fe1c125 --- /dev/null +++ b/Stay/Main/Data/UserscriptInfo.m @@ -0,0 +1,25 @@ +// +// UserscriptInfo.m +// Stay +// +// Created by ris on 2022/5/31. +// + +#import "UserscriptInfo.h" + +@implementation UserscriptInfo + +- (void)unarchiveData:(NSData *)data{ + self.content = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; +} + +- (NSData *)archiveData{ + return [NSJSONSerialization dataWithJSONObject:self.content options:0 error:nil]; +} + + +- (dispatch_queue_t _Nonnull)dispatchQueue { + return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); +} + +@end diff --git a/Stay/Main/SYDetail/Controller/SYDetailViewController.m b/Stay/Main/SYDetail/Controller/SYDetailViewController.m index a23defb3..59d5d51a 100644 --- a/Stay/Main/SYDetail/Controller/SYDetailViewController.m +++ b/Stay/Main/SYDetail/Controller/SYDetailViewController.m @@ -10,7 +10,8 @@ #import "SYEditViewController.h" #import "UserscriptUpdateManager.h" #import "SYNotesViewController.h" - +#import "ScriptMananger.h" +#import "SharedStorageManager.h" @interface SYDetailViewController () @@ -29,7 +30,7 @@ - (void)viewDidLoad { } }]; - self.view.backgroundColor = [self createBgColor]; + self.view.backgroundColor = DynamicColor(RGB(20, 20, 20),RGB(246, 246, 246)); UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(0.0,0.0,200,44.0)]; [label setBackgroundColor:[UIColor clearColor]]; [label setNumberOfLines:0]; @@ -82,6 +83,7 @@ - (void)createDetailView{ [scrollView addSubview:detailView]; UILabel *nameLabel = [self createDefaultLabelWithText:self.script.name]; + nameLabel.width = kScreenWidth - 60 - 48 - 42; nameLabel.top = 13; nameLabel.left = 17; [detailView addSubview:nameLabel]; @@ -136,7 +138,7 @@ - (void)createDetailView{ [detailView addSubview:scriptLabel]; - NSString *imageName = CGColorEqualToColor([[self createBgColor] CGColor],[[UIColor blackColor] CGColor])?@"arrow-dark":@"arrow"; + NSString *imageName = CGColorEqualToColor([[self createBgColor] CGColor],[RGB(20, 20, 20) CGColor])?@"arrow-dark":@"arrow"; UIImageView *scriptIconLabel = [[UIImageView alloc] initWithImage:[UIImage imageNamed:imageName]]; scriptIconLabel.right = kScreenWidth - 48; scriptIconLabel.centerY = scriptLabel.centerY; @@ -194,7 +196,7 @@ - (void)createDetailView{ notesLabel.left = 17; [detailView addSubview:notesLabel]; - NSString *imageName = CGColorEqualToColor([[self createBgColor] CGColor],[[UIColor blackColor] CGColor])?@"arrow-dark":@"arrow"; + NSString *imageName = CGColorEqualToColor([[self createBgColor] CGColor],[RGB(20, 20, 20) CGColor])?@"arrow-dark":@"arrow"; UIImageView *noteIconLabel = [[UIImageView alloc] initWithImage:[UIImage imageNamed:imageName]]; noteIconLabel.right = kScreenWidth - 48; noteIconLabel.centerY = notesLabel.centerY; @@ -434,7 +436,6 @@ - (void)createDetailView{ - (void)deleteScript:(id)sender { self.isSearch = true; [[DataManager shareManager] deleteScriptInUserScriptByNumberId: self.script.uuid]; - [[DataManager shareManager] updateLibScrpitStatus:0 numberId:self.script.uuid]; for (UIView *subView in self.view.subviews) { [subView removeFromSuperview]; } @@ -444,7 +445,6 @@ - (void)deleteScript:(id)sender { - (void)addScript:(id)sender { self.isSearch = false; [[DataManager shareManager] insertToUserScriptnumberId: self.script.uuid]; - [[DataManager shareManager] updateLibScrpitStatus:1 numberId: self.script.uuid]; for (UIView *subView in self.view.subviews) { [subView removeFromSuperview]; } @@ -509,16 +509,21 @@ - (UILabel *)createDefaultLabelWithText:(NSString *)text { - (void)initScrpitContent{ - NSUserDefaults *groupUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.dajiu.stay.pro"]; NSMutableArray *array = [[NSMutableArray alloc] init]; NSArray *datas = [[DataManager shareManager] findScript:1]; - if(datas != NULL && datas.count > 0) { + if(datas.count > 0) { for(int i = 0; i < datas.count; i++) { UserScript *scrpit = datas[i]; + UserscriptInfo *info = [[SharedStorageManager shared] getInfoOfUUID:scrpit.uuid]; + info.content = [scrpit toDictionary]; + [info flush]; + scrpit.parsedContent = @""; [array addObject: [scrpit toDictionary]]; } - [groupUserDefaults setObject:array forKey:@"ACTIVE_SCRIPTS"]; - [groupUserDefaults synchronize]; + [SharedStorageManager shared].userscriptHeaders.content = array; + [[SharedStorageManager shared].userscriptHeaders flush]; + [[ScriptMananger shareManager] buildData]; + } } @@ -533,14 +538,7 @@ - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { */ - (UIColor *)createBgColor { - UIColor *viewBgColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull trainCollection) { - if ([trainCollection userInterfaceStyle] == UIUserInterfaceStyleLight) { - return RGB(242, 242, 246); - } - else { - return [UIColor blackColor]; - } - }]; + UIColor *viewBgColor = DynamicColor(RGB(20, 20, 20),RGB(246, 246, 246)); return viewBgColor; } diff --git a/Stay/Main/SYEdit/controller/SYEditViewController.h b/Stay/Main/SYEdit/controller/SYEditViewController.h index cf4d16dd..1c8f8727 100644 --- a/Stay/Main/SYEdit/controller/SYEditViewController.h +++ b/Stay/Main/SYEdit/controller/SYEditViewController.h @@ -17,6 +17,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, strong) UserScript *userScript; @property (nonatomic, assign) bool isEdit; @property (nonatomic, assign) bool isSearch; +@property (nonatomic, strong) NSString *downloadUrl; @end NS_ASSUME_NONNULL_END diff --git a/Stay/Main/SYEdit/controller/SYEditViewController.m b/Stay/Main/SYEdit/controller/SYEditViewController.m index e6a9e0b5..981fa189 100644 --- a/Stay/Main/SYEdit/controller/SYEditViewController.m +++ b/Stay/Main/SYEdit/controller/SYEditViewController.m @@ -10,7 +10,8 @@ #import "Tampermonkey.h" #import "DataManager.h" #import "UserscriptUpdateManager.h" - +#import "ScriptMananger.h" +#import "SharedStorageManager.h" @interface SYEditViewController () @property (nonatomic, strong) UIBarButtonItem *rightIcon; @@ -65,8 +66,9 @@ - (void)viewDidLoad { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardHideAction:) name:UIKeyboardWillHideNotification object:nil]; [self.view addSubview:self.syCodeMirrorView]; + self.syCodeMirrorView.downloadUrl = self.downloadUrl; [self.view addSubview:self.componetView]; - self.componetView.bottom = kScreenHeight - 45; + self.componetView.bottom = kScreenHeight - 20; if(!self.isSearch) { self.navigationItem.rightBarButtonItem = [self rightIcon]; } @@ -147,12 +149,12 @@ - (void)htmlLoadSuccess:(NSNotification*) notification{ } - (void)keyboardShowAction:(NSNotification*)sender{ - NSValue *endFrameValue = sender.userInfo[UIKeyboardFrameEndUserInfoKey]; - CGRect endFrame = [endFrameValue CGRectValue]; - self.componetView.bottom = endFrame.origin.y - 10; +// NSValue *endFrameValue = sender.userInfo[UIKeyboardFrameEndUserInfoKey]; +// CGRect endFrame = [endFrameValue CGRectValue]; +// self.componetView.bottom = endFrame.origin.y - 10; } - (void)keyboardHideAction:(NSNotification*)sender{ - self.componetView.bottom = kScreenHeight - 45; + self.componetView.bottom = kScreenHeight - 20; } - (void)saveSuccess:(id)sender{ @@ -192,7 +194,7 @@ - (void)viewWillAppear:(BOOL)animated{ - (void)viewWillDisappear:(BOOL)animated{ [super viewWillDisappear:animated]; self.tabBarController.tabBar.hidden = NO; - self.componetView.bottom = kScreenHeight - 45; + self.componetView.bottom = kScreenHeight - 20; [[[UIApplication sharedApplication] keyWindow] endEditing:YES]; } @@ -219,9 +221,9 @@ - (UIBarButtonItem *)rightIcon { - (UIView *)componetView { if (nil == _componetView){ - _componetView = [[UIView alloc] initWithFrame:CGRectMake(10,0.0,kScreenWidth - 20,45)]; - _componetView.backgroundColor = [self createBgColor]; - _componetView.layer.cornerRadius = 12; + _componetView = [[UIView alloc] initWithFrame:CGRectMake(0,0.0,kScreenWidth,60)]; + _componetView.backgroundColor = DynamicColor([UIColor blackColor],[UIColor whiteColor]); +// _componetView.layer.cornerRadius = 12; _componetView.layer.borderWidth = 0.5; UIColor *borderColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull trainCollection) { if ([trainCollection userInterfaceStyle] == UIUserInterfaceStyleLight) { @@ -232,42 +234,50 @@ - (UIView *)componetView { } }]; _componetView.layer.borderColor = [borderColor CGColor]; - _backBtn = [UIButton buttonWithType:UIButtonTypeCustom]; - _backBtn.frame = CGRectMake(0, 0, 31, 23); + CGFloat width = (kScreenWidth - 57 - 56 * 4) / 3; + + UIImage *image = [UIImage systemImageNamed:@"arrow.uturn.backward" + withConfiguration:[UIImageSymbolConfiguration configurationWithFont:[UIFont systemFontOfSize:23]]]; + image = [image imageWithTintColor:DynamicColor([UIColor whiteColor],[UIColor blackColor]) renderingMode:UIImageRenderingModeAlwaysOriginal]; + + _backBtn = [self createBtn:image text:NSLocalizedString(@"Undo", @"")]; _backBtn.enabled = false; - [_backBtn setImage:[UIImage imageNamed:@"back"] forState:UIControlStateNormal]; [_backBtn addTarget:self action:@selector(editerCancel:) forControlEvents:UIControlEventTouchUpInside]; - _backBtn.centerY = 22.5; - _backBtn.left = 31; + _backBtn.centerY = 30; + _backBtn.left = 28.5; [_componetView addSubview:_backBtn]; - _onBtn = [UIButton buttonWithType:UIButtonTypeCustom]; - _onBtn.frame = CGRectMake(0, 0, 31, 23); + + UIImage *onImage = [UIImage systemImageNamed:@"arrow.uturn.forward" + withConfiguration:[UIImageSymbolConfiguration configurationWithFont:[UIFont systemFontOfSize:23]]]; + onImage = [onImage imageWithTintColor: DynamicColor([UIColor whiteColor],[UIColor blackColor]) renderingMode:UIImageRenderingModeAlwaysOriginal]; + + _onBtn = [self createBtn:onImage text:NSLocalizedString(@"Redo", @"")]; _onBtn.enabled = false; - [_onBtn setImage:[UIImage imageNamed:@"on"] forState:UIControlStateNormal]; [_onBtn addTarget:self action:@selector(editerOn:) forControlEvents:UIControlEventTouchUpInside]; - _onBtn.centerY = 22.5; - _onBtn.left = 83; + _onBtn.centerY = 30; + _onBtn.left = 56 + width + 28.5; [_componetView addSubview:_onBtn]; - UIButton *pasteLabelBtn = [UIButton buttonWithType:UIButtonTypeCustom]; - pasteLabelBtn.frame = CGRectMake(0, 0, 120, 24); - [pasteLabelBtn setTitle:@"从剪贴板粘贴" forState:UIControlStateNormal]; - pasteLabelBtn.titleLabel.font = [UIFont systemFontOfSize:17]; - [pasteLabelBtn setTitleColor:RGB(182, 32, 224) forState:UIControlStateNormal]; - [pasteLabelBtn addTarget:self action:@selector(copyPasteBoard:) forControlEvents:UIControlEventTouchUpInside]; - pasteLabelBtn.centerY = 22.5; - pasteLabelBtn.right = kScreenWidth - 31; - [_componetView addSubview:pasteLabelBtn]; + UIImage *clearImage = [UIImage systemImageNamed:@"trash" + withConfiguration:[UIImageSymbolConfiguration configurationWithFont:[UIFont systemFontOfSize:23]]]; + clearImage = [clearImage imageWithTintColor: DynamicColor([UIColor whiteColor],[UIColor blackColor]) renderingMode:UIImageRenderingModeAlwaysOriginal]; - UIButton *clearBtn = [UIButton buttonWithType:UIButtonTypeCustom]; - clearBtn.frame = CGRectMake(0, 0, 50, 24); - [clearBtn setTitle:@"清空" forState:UIControlStateNormal]; - clearBtn.titleLabel.font = [UIFont systemFontOfSize:17]; - [clearBtn setTitleColor:RGB(182, 32, 224) forState:UIControlStateNormal]; + UIButton *clearBtn = [self createBtn:clearImage text:NSLocalizedString(@"Clear", @"")]; [clearBtn addTarget:self action:@selector(clearContext:) forControlEvents:UIControlEventTouchUpInside]; - clearBtn.centerY = 22.5; - clearBtn.right = pasteLabelBtn.left - 25; + clearBtn.centerY = 30; + clearBtn.left = 56 * 2 + width * 2 + 28.5; [_componetView addSubview:clearBtn]; + + UIImage *pasteImage = [UIImage systemImageNamed:@"arrow.up.doc.on.clipboard" + withConfiguration:[UIImageSymbolConfiguration configurationWithFont:[UIFont systemFontOfSize:23]]]; + pasteImage = [pasteImage imageWithTintColor: DynamicColor([UIColor whiteColor],[UIColor blackColor]) renderingMode:UIImageRenderingModeAlwaysOriginal]; + + UIButton *pasteLabelBtn = [self createBtn:pasteImage text:NSLocalizedString(@"Clipboard", @"")]; + + [pasteLabelBtn addTarget:self action:@selector(copyPasteBoard:) forControlEvents:UIControlEventTouchUpInside]; + pasteLabelBtn.centerY = 30; + pasteLabelBtn.left = 56 * 3 + width * 3 + 28.5; + [_componetView addSubview:pasteLabelBtn]; } return _componetView; @@ -303,23 +313,46 @@ - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { */ - (void)initScrpitContent{ - NSUserDefaults *groupUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.dajiu.stay.pro"]; +// NSUserDefaults *groupUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.dajiu.stay.pro"]; +// NSMutableArray *array = [[NSMutableArray alloc] init]; +// NSArray *datas = [[DataManager shareManager] findScript:1]; +// if(datas != NULL && datas.count > 0) { +// for(int i = 0; i < datas.count; i++) { +// UserScript *scrpit = datas[i]; +// [groupUserDefaults setObject:[scrpit toDictionary] forKey:[NSString stringWithFormat:@"STAY_SCRIPTS_%@",scrpit.uuid]]; +// scrpit.parsedContent = @""; +// scrpit.icon = @""; +// [array addObject: [scrpit toDictionary]]; +// } +// [groupUserDefaults setObject:array forKey:@"STAY_SCRIPTS"]; +// [groupUserDefaults synchronize]; +// [[ScriptMananger shareManager] buildData]; +// } + NSMutableArray *array = [[NSMutableArray alloc] init]; NSArray *datas = [[DataManager shareManager] findScript:1]; - if(datas != NULL && datas.count > 0) { + if(datas.count > 0) { for(int i = 0; i < datas.count; i++) { UserScript *scrpit = datas[i]; + UserscriptInfo *info = [[SharedStorageManager shared] getInfoOfUUID:scrpit.uuid]; + info.content = [scrpit toDictionary]; + [info flush]; + scrpit.parsedContent = @""; [array addObject: [scrpit toDictionary]]; } - [groupUserDefaults setObject:array forKey:@"ACTIVE_SCRIPTS"]; - [groupUserDefaults synchronize]; + [SharedStorageManager shared].userscriptHeaders.content = array; + [[SharedStorageManager shared].userscriptHeaders flush]; + [[ScriptMananger shareManager] buildData]; } + } - (UIColor *)createBgColor { UIColor *viewBgColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull trainCollection) { if ([trainCollection userInterfaceStyle] == UIUserInterfaceStyleLight) { return RGB(242, 242, 246); + + return RGB(242, 242, 246); } else { return [UIColor blackColor]; @@ -330,7 +363,7 @@ - (UIColor *)createBgColor { - (SYCodeMirrorView *)syCodeMirrorView { if (_syCodeMirrorView == nil) { - _syCodeMirrorView = [[SYCodeMirrorView alloc] initWithFrame:CGRectMake(0, StatusBarHeight, kScreenWidth, kScreenHeight - StatusBarHeight)]; + _syCodeMirrorView = [[SYCodeMirrorView alloc] initWithFrame:CGRectMake(0, StatusBarHeight, kScreenWidth, kScreenHeight - StatusBarHeight - 70)]; } return _syCodeMirrorView; } @@ -369,4 +402,28 @@ - (UILabel *)countLabel { return _countLabel; } +- (UIButton *)createBtn:(UIImage *)image text:(NSString *)text { + + UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom]; + + [btn setTitle:text forState:UIControlStateNormal]; + [btn setImage:image forState:UIControlStateNormal]; + + btn.frame = CGRectMake(0, 0, 56, 45); + [btn setTitleColor:DynamicColor([UIColor whiteColor],[UIColor blackColor]) forState:UIControlStateNormal]; + [btn.titleLabel setFont:[UIFont systemFontOfSize:13]]; + [btn setTitleEdgeInsets: + UIEdgeInsetsMake(btn.frame.size.height/2 + 10, + (btn.frame.size.width-btn.titleLabel.intrinsicContentSize.width)/2-btn.imageView.frame.size.width, + 0, + (btn.frame.size.width-btn.titleLabel.intrinsicContentSize.width)/2)]; + [btn setImageEdgeInsets: + UIEdgeInsetsMake( + 0, + (btn.frame.size.width-btn.imageView.frame.size.width)/2, + btn.titleLabel.intrinsicContentSize.height, + (btn.frame.size.width-btn.imageView.frame.size.width)/2)]; + return btn; +} + @end diff --git a/Stay/Main/SYEdit/view/SYCodeMirrorView.h b/Stay/Main/SYEdit/view/SYCodeMirrorView.h index c00f0466..02494125 100644 --- a/Stay/Main/SYEdit/view/SYCodeMirrorView.h +++ b/Stay/Main/SYEdit/view/SYCodeMirrorView.h @@ -15,6 +15,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, strong) WKWebView *wkwebView; @property (nonatomic, strong) NSString *content; @property (nonatomic, strong) NSString *uuid; +@property (nonatomic, strong) NSString *downloadUrl; @property (nonatomic, assign) BOOL active; diff --git a/Stay/Main/SYEdit/view/SYCodeMirrorView.m b/Stay/Main/SYEdit/view/SYCodeMirrorView.m index 38de02ce..febd43e2 100644 --- a/Stay/Main/SYEdit/view/SYCodeMirrorView.m +++ b/Stay/Main/SYEdit/view/SYCodeMirrorView.m @@ -115,6 +115,10 @@ - (void)insertContent{ [self saveError:@"resourceUrl下载失败,请检查后重试"]; return; } + + if((userScript.downloadUrl == NULL || userScript.downloadUrl.length <= 0)&&(self.downloadUrl != NULL && self.downloadUrl.length >= 0)) { + userScript.downloadUrl = self.downloadUrl; + } [[UserscriptUpdateManager shareManager] saveIcon:userScript]; @@ -122,6 +126,10 @@ - (void)insertContent{ if(tmpScript != nil && tmpScript.uuid != nil) { + if((userScript.downloadUrl == NULL || userScript.downloadUrl.length <= 0)&&(tmpScript.downloadUrl != NULL && tmpScript.downloadUrl.length >= 0)) { + userScript.downloadUrl = tmpScript.downloadUrl; + } + [[DataManager shareManager] updateUserScript:userScript]; } else { [[DataManager shareManager] insertUserConfigByUserScript:userScript]; @@ -171,6 +179,16 @@ - (void)updateContent{ return; } + UserScript *tmpScript = [[DataManager shareManager] selectScriptByUuid:userScript.uuid ]; + + if((userScript.downloadUrl == NULL || userScript.downloadUrl.length <= 0)&&(tmpScript.downloadUrl != NULL && tmpScript.downloadUrl.length >= 0)) { + userScript.downloadUrl = tmpScript.downloadUrl; + } + + if((userScript.downloadUrl == NULL || userScript.downloadUrl.length <= 0)&&(self.downloadUrl != NULL && self.downloadUrl.length >= 0)) { + userScript.downloadUrl = self.downloadUrl; + } + if(userScript != nil && userScript.errorMessage != nil && userScript.errorMessage.length <= 0) { [[DataManager shareManager] updateUserScript:userScript]; diff --git a/Stay/Main/SYHome/Controller/SYHomeViewController.m b/Stay/Main/SYHome/Controller/SYHomeViewController.m index b9787977..7cc46832 100644 --- a/Stay/Main/SYHome/Controller/SYHomeViewController.m +++ b/Stay/Main/SYHome/Controller/SYHomeViewController.m @@ -18,9 +18,16 @@ #import "UserscriptUpdateManager.h" #import "SYAddScriptController.h" #import "SYWebScriptViewController.h" +#import "ScriptMananger.h" +#import "ScriptEntity.h" +#import +#import "UIImageView+WebCache.h" +#import "MatchPattern.h" #import +#import "SharedStorageManager.h" + @interface SYHomeViewController ()< UITableViewDelegate, UITableViewDataSource, @@ -52,12 +59,13 @@ @implementation SYHomeViewController - (void)viewDidLoad { [super viewDidLoad]; +// [ScriptMananger shareManager]; self.loadingView.center = self.view.center; self.loadingView.hidden = YES; // [SYCodeMirrorView shareCodeView]; self.navigationItem.leftBarButtonItem = [self leftIcon]; self.navigationItem.rightBarButtonItem = [self rightIcon]; - self.view.backgroundColor = [UIColor whiteColor]; + self.view.backgroundColor = DynamicColor(RGB(28, 28, 28),[UIColor whiteColor]); UISearchController *search = [[UISearchController alloc]initWithSearchResultsController:nil]; // 设置结果更新代理 // search.searchResultsUpdater = self; @@ -83,6 +91,7 @@ - (void)viewDidLoad { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarChange) name:UIDeviceOrientationDidChangeNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(tableDidSelected:) name:@"addScriptClick" object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarChange) name:@"needUpdate" object:nil]; } - (void)tableDidSelected:(NSNotification *)notification { @@ -112,6 +121,7 @@ - (void)tableDidSelected:(NSNotification *)notification { NSString *str = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]; SYEditViewController *cer = [[SYEditViewController alloc] init]; cer.content = str; + cer.downloadUrl = url; [self.navigationController pushViewController:cer animated:true]; }else { NSString *content = @"下载脚本失败"; @@ -199,23 +209,23 @@ - (void)statusBarChange{ //后台唤起时处理与插件交互 - (void)onBecomeActive{ [self checkShowTips]; - NSUserDefaults *groupUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.dajiu.stay.pro"]; - if([groupUserDefaults arrayForKey:@"ACTIVE_CHANGE"] != NULL && [groupUserDefaults arrayForKey:@"ACTIVE_CHANGE"].count > 0){ - NSMutableArray *datas = [NSMutableArray arrayWithArray:[groupUserDefaults arrayForKey:@"ACTIVE_CHANGE"]]; - for(int i = 0; i < datas.count; i++) { - NSDictionary *dic = datas[i]; - [[DataManager shareManager] updateScrpitStatus:[dic[@"active"] intValue] numberId:dic[@"uuid"]]; + + [SharedStorageManager shared].activateChanged = nil; + NSDictionary *activateChanged = [SharedStorageManager shared].activateChanged.content; + if (activateChanged.count > 0){ + NSArray *uuidArrray = activateChanged.allKeys; + for (NSString *uuid in uuidArrray){ + [[DataManager shareManager] updateScrpitStatus:[activateChanged[uuid] boolValue] ? 1:0 numberId:uuid]; } } - [groupUserDefaults setObject:nil forKey:@"ACTIVE_CHANGE"]; - [groupUserDefaults synchronize]; + [SharedStorageManager shared].activateChanged.content = @{}; + [[SharedStorageManager shared].activateChanged flush]; [self reloadTableView]; [self.tableView reloadData]; [self initScrpitContent]; + //自动更新代码保留先注释 NSArray *array = [[DataManager shareManager] findScript:1]; [self updateScriptWhen:array type:false]; - NSArray *searchArray = [[DataManager shareManager] findScriptInLib]; - [self updateScriptWhen:searchArray type:true]; } - (void)updateScriptWhen:(NSArray *)array type:(bool)isSearch { @@ -224,7 +234,7 @@ - (void)updateScriptWhen:(NSArray *)array type:(bool)isSearch { if(!isSearch && !scrpit.updateSwitch) { continue; } - + if(scrpit.updateUrl != NULL && scrpit.updateUrl.length > 0) { [[SYNetworkUtils shareInstance] requestGET:scrpit.updateUrl params:NULL successBlock:^(NSString * _Nonnull responseObject) { if(responseObject != nil) { @@ -236,14 +246,9 @@ - (void)updateScriptWhen:(NSArray *)array type:(bool)isSearch { if(userScript.content != nil && userScript.content.length > 0) { userScript.uuid = scrpit.uuid; userScript.active = scrpit.active; - if(isSearch) { - [[DataManager shareManager] updateScriptConfigByUserScript:userScript]; - NSNotification *notification = [NSNotification notificationWithName:@"uploadScriptSuccess" object:nil]; - [[NSNotificationCenter defaultCenter]postNotification:notification]; - } else { - [[DataManager shareManager] updateUserScript:userScript]; - [self refreshScript]; - } + [[DataManager shareManager] updateUserScript:userScript]; + [self refreshScript]; + } } else { [[SYNetworkUtils shareInstance] requestGET:scrpit.downloadUrl params:nil successBlock:^(NSString * _Nonnull responseObject) { @@ -252,26 +257,20 @@ - (void)updateScriptWhen:(NSArray *)array type:(bool)isSearch { userScript.uuid = scrpit.uuid; userScript.active = scrpit.active; if(userScript != nil && userScript.errorMessage != nil && userScript.errorMessage.length <= 0) { - if(isSearch) { - [[DataManager shareManager] updateScriptConfigByUserScript:userScript]; - NSNotification *notification = [NSNotification notificationWithName:@"uploadScriptSuccess" object:nil]; - [[NSNotificationCenter defaultCenter]postNotification:notification]; - } else { - [[DataManager shareManager] updateUserScript:userScript]; - [self refreshScript]; - } + [[DataManager shareManager] updateUserScript:userScript]; + [self refreshScript]; } } } failBlock:^(NSError * _Nonnull error) { - + }]; } } } - + } } failBlock:^(NSError * _Nonnull error) { - + }]; } else if(scrpit.downloadUrl != NULL && scrpit.downloadUrl.length > 0) { [[SYNetworkUtils shareInstance] requestGET:scrpit.downloadUrl params:nil successBlock:^(NSString * _Nonnull responseObject) { @@ -282,21 +281,18 @@ - (void)updateScriptWhen:(NSArray *)array type:(bool)isSearch { if(status == 1) { userScript.uuid = scrpit.uuid; userScript.active = scrpit.active; + if(userScript.downloadUrl == NULL || userScript.downloadUrl.length <= 0) { + userScript.downloadUrl = scrpit.downloadUrl; + } if(userScript != nil && userScript.errorMessage != nil && userScript.errorMessage.length <= 0) { - if(isSearch) { - [[DataManager shareManager] updateScriptConfigByUserScript:userScript]; - NSNotification *notification = [NSNotification notificationWithName:@"uploadScriptSuccess" object:nil]; - [[NSNotificationCenter defaultCenter]postNotification:notification]; - } else { - [[DataManager shareManager] updateUserScript:userScript]; - [self refreshScript]; - } + [[DataManager shareManager] updateUserScript:userScript]; + [self refreshScript]; } } } } } failBlock:^(NSError * _Nonnull error) { - + }]; } } @@ -311,14 +307,33 @@ - (void)refreshScript{ } - (void)initScrpitContent{ - NSUserDefaults *groupUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.dajiu.stay.pro"]; NSMutableArray *array = [[NSMutableArray alloc] init]; for(int i = 0; i < self.datas.count; i++) { UserScript *scrpit = self.datas[i]; + UserscriptInfo *info = [[SharedStorageManager shared] getInfoOfUUID:scrpit.uuid]; + info.content = [scrpit toDictionary]; + [info flush]; + scrpit.parsedContent = @""; [array addObject: [scrpit toDictionary]]; } - [groupUserDefaults setObject:array forKey:@"ACTIVE_SCRIPTS"]; - [groupUserDefaults synchronize]; + [SharedStorageManager shared].userscriptHeaders.content = array; + [[SharedStorageManager shared].userscriptHeaders flush]; + + + +// NSUserDefaults *groupUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.dajiu.stay.pro"]; +// NSMutableArray *array = [[NSMutableArray alloc] init]; +// for(int i = 0; i < self.datas.count; i++) { +// UserScript *scrpit = self.datas[i]; +// [groupUserDefaults setObject:[scrpit toDictionary] forKey:[NSString stringWithFormat:@"STAY_SCRIPTS_%@",scrpit.uuid]]; +// scrpit.parsedContent = @""; +// [array addObject: [scrpit toDictionary]]; +// } +// [groupUserDefaults setObject:array forKey:@"STAY_SCRIPTS"]; +// [groupUserDefaults synchronize]; + + [[ScriptMananger shareManager] buildData]; + } #pragma mark -popover - (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller{ @@ -391,78 +406,148 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N } else { model = _datas[indexPath.row]; } + cell.backgroundColor = DynamicColor(RGB(28, 28, 28),[UIColor whiteColor]); + cell.contentView.backgroundColor = DynamicColor(RGB(28, 28, 28),[UIColor whiteColor]); - UIColor *bgColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull trainCollection) { - if ([trainCollection userInterfaceStyle] == UIUserInterfaceStyleLight) { - return [UIColor whiteColor]; - } - else { - return [UIColor blackColor]; - } - }]; + CGFloat leftWidth = kScreenWidth * 0.6 - 15; + + CGFloat titleLabelLeftSize = 0; + if(model.icon != NULL && model.icon.length > 0) { + UIImageView *imageview = [[UIImageView alloc] initWithFrame:CGRectMake(15,15,23,23)] ; + [imageview sd_setImageWithURL:[NSURL URLWithString: model.icon] ]; + [cell.contentView addSubview:imageview]; + titleLabelLeftSize = 27; + } - cell.contentView.backgroundColor = bgColor; - UILabel *titleLabel = [[UILabel alloc]initWithFrame:CGRectMake(15, 10, kScreenWidth / 3 * 2, 21)]; + UILabel *titleLabel = [[UILabel alloc]initWithFrame:CGRectMake(15 + titleLabelLeftSize , 15, leftWidth - titleLabelLeftSize, 45)]; titleLabel.font = [UIFont boldSystemFontOfSize:18]; titleLabel.textAlignment = NSTextAlignmentLeft; titleLabel.lineBreakMode= NSLineBreakByTruncatingTail; + titleLabel.numberOfLines = 2; titleLabel.text = model.name; [titleLabel sizeToFit]; - if(titleLabel.width > kScreenWidth / 3 * 2) { - titleLabel.width = kScreenWidth / 3 * 2; - } - [cell.contentView addSubview:titleLabel]; - - UILabel *versionLabel = [[UILabel alloc] initWithFrame:CGRectMake(15, 10, kScreenWidth / 2, 21)]; - versionLabel.font = [UIFont boldSystemFontOfSize:15]; - versionLabel.textAlignment = NSTextAlignmentLeft; - versionLabel.text = model.version; - versionLabel.textColor = RGB(182, 32, 224); - versionLabel.left = titleLabel.right + 5; - versionLabel.centerY = titleLabel.centerY; - [cell.contentView addSubview:versionLabel]; - UILabel *authorLabel = [[UILabel alloc]initWithFrame:CGRectMake(15, 5, kScreenWidth, 19)]; - authorLabel.font = [UIFont systemFontOfSize:16]; - authorLabel.textAlignment = NSTextAlignmentLeft; - authorLabel.text = model.author; - authorLabel.top = titleLabel.bottom + 10; - [authorLabel sizeToFit]; - [cell.contentView addSubview:authorLabel]; - UILabel *descLabel = [[UILabel alloc]initWithFrame:CGRectMake(15, 5, kScreenWidth - 30, 19)]; + UILabel *descLabel = [[UILabel alloc]initWithFrame:CGRectMake(15, 10, leftWidth, 40)]; descLabel.font = [UIFont systemFontOfSize:15]; descLabel.textAlignment = NSTextAlignmentLeft; descLabel.lineBreakMode= NSLineBreakByTruncatingTail; descLabel.text = model.desc; - descLabel.top = authorLabel.bottom + 5; + descLabel.numberOfLines = 2; + descLabel.bottom = 125; descLabel.textColor = [UIColor grayColor]; [cell.contentView addSubview:descLabel]; + UILabel *authorLabel = [[UILabel alloc]initWithFrame:CGRectMake(15, 10, leftWidth , 19)]; + authorLabel.font = [UIFont systemFontOfSize:16]; + authorLabel.textAlignment = NSTextAlignmentLeft; + authorLabel.text = model.author; + authorLabel.bottom = descLabel.top - 5; + [authorLabel sizeToFit]; + [cell.contentView addSubview:authorLabel]; + + UIView *verticalLine = [[UIView alloc] initWithFrame:CGRectMake(0.62 * kScreenWidth, 14, 1, 113)]; + verticalLine.backgroundColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull trainCollection) { + if ([trainCollection userInterfaceStyle] == UIUserInterfaceStyleLight) { + return RGBA(216, 216, 216, 0.3); + } + else { + return RGBA(37, 37, 40, 1); + } + }]; + [cell.contentView addSubview:verticalLine]; + + CGFloat left = 0.65 * kScreenWidth; + CGFloat width = 0.3 * kScreenWidth; + UILabel *version= [[UILabel alloc] initWithFrame:CGRectMake(left, 14, width, 15)]; + version.text = @"version"; + version.font = [UIFont systemFontOfSize:12]; + version.textColor = RGB(138, 138, 138); + [cell.contentView addSubview:version]; + + UILabel *versionLabel = [[UILabel alloc] initWithFrame:CGRectMake(15, 10, kScreenWidth / 2, 21)]; + versionLabel.font = [UIFont boldSystemFontOfSize:15]; + versionLabel.textAlignment = NSTextAlignmentLeft; + versionLabel.text = model.version; + versionLabel.left = left; + versionLabel.top = version.bottom + 2; + [cell.contentView addSubview:versionLabel]; + + UILabel *statusLab= [[UILabel alloc] initWithFrame:CGRectMake(left, 14, width, 15)]; + statusLab.text = @"status"; + statusLab.font = [UIFont systemFontOfSize:12]; + statusLab.textColor = RGB(138, 138, 138); + statusLab.top = versionLabel.bottom + 2; + statusLab.left = left; + [cell.contentView addSubview:statusLab]; + + UILabel *actLabel = [[UILabel alloc]init]; - actLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightLight]; - actLabel.textColor = RGB(138, 138, 138); + actLabel.font = [UIFont boldSystemFontOfSize:15]; if(model.active == 0) { actLabel.text = @"Stopped"; } else { actLabel.text = @"Activated"; } [actLabel sizeToFit]; - actLabel.right = kScreenWidth - 35; - actLabel.centerY = 47.5f; - + actLabel.top = statusLab.bottom + 2; + actLabel.left = left; [cell.contentView addSubview:actLabel]; - UIView *line = [[UIView alloc] initWithFrame:CGRectMake(15,94,kScreenWidth - 10,1)]; + + + UILabel *updateLab= [[UILabel alloc] initWithFrame:CGRectMake(left, 14, width, 15)]; + updateLab.text = @"Update time"; + updateLab.font = [UIFont systemFontOfSize:12]; + updateLab.textColor = RGB(138, 138, 138); + updateLab.top = actLabel.bottom + 2; + updateLab.left = left; + [cell.contentView addSubview:updateLab]; + + ScriptEntity *entity = [ScriptMananger shareManager].scriptDic[model.uuid]; + + if(entity != nil && entity.needUpdate){ + + UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom]; + btn.frame = CGRectMake(0, 0, 60, 20); + btn.backgroundColor = RGB(182,32,224); + [btn setTitle:NSLocalizedString(@"settings.update","update") forState:UIControlStateNormal]; + [btn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; + btn.titleLabel.font = [UIFont systemFontOfSize:12]; + btn.layer.cornerRadius = 4; + btn.top = updateLab.bottom + 2; + btn.left = left; + [btn addTarget:self action:@selector(updateScript:) forControlEvents:UIControlEventTouchUpInside]; + + objc_setAssociatedObject (btn , @"script", entity.updateScript.description, OBJC_ASSOCIATION_COPY_NONATOMIC); + objc_setAssociatedObject (btn , @"scriptContent", entity.updateScript.content, OBJC_ASSOCIATION_COPY_NONATOMIC); + objc_setAssociatedObject (btn , @"downloadUrl", entity.script.downloadUrl, OBJC_ASSOCIATION_COPY_NONATOMIC); + + [cell.contentView addSubview:btn]; + } else { + UILabel *updateLabel = [[UILabel alloc]init]; + updateLabel.font = [UIFont boldSystemFontOfSize:15]; + updateLabel.text = [self timeWithTimeIntervalString:model.updateTime]; + [updateLabel sizeToFit]; + updateLabel.top = updateLab.bottom + 2; + updateLabel.left = left; + [cell.contentView addSubview:updateLabel]; + } + + + + + UIView *line = [[UIView alloc] initWithFrame:CGRectMake(15,143,kScreenWidth - 10,1)]; UIColor *lineBgcolor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull trainCollection) { - if ([trainCollection userInterfaceStyle] == UIUserInterfaceStyleLight) { - return RGBA(216, 216, 216, 0.3); - } - else { - return RGBA(37, 37, 40, 1); - } - }]; + if ([trainCollection userInterfaceStyle] == UIUserInterfaceStyleLight) { + return RGBA(216, 216, 216, 0.3); + } + else { + return RGBA(37, 37, 40, 1); + } + }]; + [line setBackgroundColor:lineBgcolor]; [cell.contentView addSubview:line]; @@ -486,7 +571,7 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath } } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ - return 95.0f; + return 144.0f; } - (UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath { @@ -497,7 +582,6 @@ - (UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwip UserScript *model = weakSelf.results[indexPath.row]; [[DataManager shareManager] deleteScriptInUserScriptByNumberId: model.uuid]; - [[DataManager shareManager] updateLibScrpitStatus:0 numberId:model.uuid]; [tableView setEditing:NO animated:YES]; [self reloadTableView]; [tableView reloadData]; @@ -511,8 +595,6 @@ - (UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwip UIContextualAction *deleteAction = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleNormal title:@"" handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) { UserScript *model = weakSelf.datas[indexPath.row]; [[DataManager shareManager] deleteScriptInUserScriptByNumberId: model.uuid]; - [[DataManager shareManager] updateLibScrpitStatus:0 numberId:model.uuid]; - [tableView setEditing:NO animated:YES]; [self reloadTableView]; [tableView reloadData]; @@ -533,7 +615,6 @@ - (UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwip [weakSelf reloadTableView]; [weakSelf initScrpitContent]; [tableView reloadData]; - [[DataManager shareManager] updateLibScrpitStatus:1 numberId:model.uuid]; }]; UserScript *model = _datas[indexPath.row]; if (model.active) { @@ -553,8 +634,6 @@ - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *) } - (void)addBtnClick:(id)sender { -// SYEditViewController *cer = [[SYEditViewController alloc] init]; -// [self.navigationController pushViewController:cer animated:true]; self.itemPopVC = [[SYAddScriptController alloc] init]; self.itemPopVC.modalPresentationStyle = UIModalPresentationPopover; self.itemPopVC.preferredContentSize = self.itemPopVC.view.bounds.size; @@ -576,6 +655,38 @@ - (void)viewWillAppear:(BOOL)animated { } +- (void)updateScript:(UIButton *)sender { + NSString *script = objc_getAssociatedObject(sender,@"script"); + UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提示" message:script preferredStyle:UIAlertControllerStyleAlert]; + + NSString *scriptContent = objc_getAssociatedObject(sender,@"scriptContent"); + + NSString *downloadUrl = objc_getAssociatedObject(sender,@"downloadUrl"); + + UIAlertAction *conform = [UIAlertAction actionWithTitle:NSLocalizedString(@"settings.update","update") style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { + SYEditViewController *cer = [[SYEditViewController alloc] init]; + cer.content = scriptContent; + cer.downloadUrl = downloadUrl; + [self.navigationController pushViewController:cer animated:true]; + }]; + UIAlertAction *cancelconform = [UIAlertAction actionWithTitle:NSLocalizedString(@"cancel","Cancel") style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { + + }]; + + NSMutableParagraphStyle *paraStyle = [[NSMutableParagraphStyle alloc] init]; + paraStyle.alignment = NSTextAlignmentLeft; + + NSMutableAttributedString *atrStr = [[NSMutableAttributedString alloc] initWithString:script attributes:@{NSParagraphStyleAttributeName:paraStyle,NSFontAttributeName:[UIFont systemFontOfSize:13.0]}]; + + [alert setValue:atrStr forKey:@"attributedMessage"]; + [alert addAction:cancelconform]; + [alert addAction:conform]; + [self presentViewController:alert animated:YES completion:nil]; + +} + + + - (void) reloadTableView { [_datas removeAllObjects]; [_datas addObjectsFromArray:[[DataManager shareManager] findScript:1]]; @@ -587,6 +698,7 @@ - (UITableView *)tableView { _tableView.delegate = self; _tableView.dataSource = self; _tableView.separatorStyle = UITableViewCellSeparatorStyleNone; + _tableView.backgroundColor = DynamicColor(RGB(28, 28, 28),[UIColor whiteColor]); [self.view addSubview:_tableView]; } @@ -645,4 +757,30 @@ - (UIView *)loadingView { return _loadingView; } +- (NSString *)timeWithTimeIntervalString:(NSString *)timeString +{ + + if(timeString == NULL || [timeString doubleValue] < 20) { + timeString = [self getNowDate]; + } + // 格式化时间 + NSDateFormatter* formatter = [[NSDateFormatter alloc] init]; + formatter.timeZone = [NSTimeZone timeZoneWithName:@"shanghai"]; + [formatter setDateStyle:NSDateFormatterMediumStyle]; + [formatter setTimeStyle:NSDateFormatterShortStyle]; + [formatter setDateFormat:@"yyyy.MM.dd"]; + + // 毫秒值转化为秒 + NSDate* date = [NSDate dateWithTimeIntervalSince1970:[timeString doubleValue]/ 1000.0]; + NSString* dateString = [formatter stringFromDate:date]; + return dateString; +} + +- (NSString *)getNowDate { + NSDate* date = [NSDate dateWithTimeIntervalSinceNow:0]; + NSTimeInterval a=[date timeIntervalSince1970]*1000; // *1000 是精确到毫秒,不乘就是精确到秒 + NSString *timeString = [NSString stringWithFormat:@"%.0f", a]; + return timeString; +} + @end diff --git a/Stay/Main/SYHome/Controller/SYWebScriptViewController.m b/Stay/Main/SYHome/Controller/SYWebScriptViewController.m index 965567f5..a18ae6f5 100644 --- a/Stay/Main/SYHome/Controller/SYWebScriptViewController.m +++ b/Stay/Main/SYHome/Controller/SYWebScriptViewController.m @@ -119,6 +119,7 @@ - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspeci dispatch_async(dispatch_get_main_queue(),^{ SYEditViewController *cer = [[SYEditViewController alloc] init]; cer.content = str; + cer.downloadUrl = url; [self.navigationController pushViewController:cer animated:true]; }); } diff --git a/Stay/Main/SYMore/Controller/SYMoreViewController.m b/Stay/Main/SYMore/Controller/SYMoreViewController.m index f10e479d..d4068f75 100644 --- a/Stay/Main/SYMore/Controller/SYMoreViewController.m +++ b/Stay/Main/SYMore/Controller/SYMoreViewController.m @@ -23,9 +23,9 @@ @implementation SYMoreViewController - (void)testParseUserScript{ UserScript *userScript = [[Tampermonkey shared] parseScript:@"newuserscript.user"]; NSUserDefaults *groupUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.dajiu.stay.pro"]; - [groupUserDefaults setObject:@[[userScript toDictionary]] forKey:@"ACTIVE_SCRIPTS"]; + [groupUserDefaults setObject:@[[userScript toDictionary]] forKey:@"STAY_SCRIPTS"]; [groupUserDefaults synchronize]; -// NSMutableArray *datas = [NSMutableArray arrayWithArray:[groupUserDefaults arrayForKey:@"ACTIVE_SCRIPTS"]]; +// NSMutableArray *datas = [NSMutableArray arrayWithArray:[groupUserDefaults arrayForKey:@"STAY_SCRIPTS"]]; // NSLog([userScript toDictionary]); @@ -86,6 +86,17 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath } } + +- (BOOL)joinGroup:(NSString *)groupUin key:(NSString *)key{ + NSString *urlStr = [NSString stringWithFormat:@"mqqapi://card/show_pslcard?src_type=internal&version=1&uin=%@&key=%@&card_type=group&source=external&jump_from=webapi", @"714147685",@"c987123ea55d74e0b3fa84e3169d6be6d24fb1849e78f57c0f573e9d45e67217"]; + NSURL *url = [NSURL URLWithString:urlStr]; + if([[UIApplication sharedApplication] canOpenURL:url]){ + [[UIApplication sharedApplication] openURL:url]; + return YES; + } + else return NO; +} + - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{ return self.dataSource[section][@"section"]; } @@ -99,13 +110,9 @@ - (NSArray *)dataSource{ @"cells":@[ @{@"title":NSLocalizedString(@"settings.rateApp",""),@"url":@"https://apps.apple.com/app/id1591620171?action=write-review"}, @{@"title":NSLocalizedString(@"settings.openSource",""),@"url":@"https://github.com/shenruisi/Stay"}, - @{@"title":NSLocalizedString(@"settings.joinTelegram",""),@"url":@"https://t.me/fastclipchat"} - ] - }, - @{ - @"section":@"", - @"cells":@[ - @{@"title":NSLocalizedString(@"settings.enableStay",""),@"url":@"App-prefs:root=SAFARI"} + @{@"title":NSLocalizedString(@"settings.joinTelegram",""),@"url":@"https://t.me/fastclipchat"}, + @{@"title":NSLocalizedString(@"settings.joinQQ",""),@"url":@"mqqapi://card/show_pslcard?src_type=internal&version=1&uin=714147685&key=c987123ea55d74e0b3fa84e3169d6be6d24fb1849e78f57c0f573e9d45e67217&card_type=group&source=external&jump_from=webapi"}, + @{@"title":NSLocalizedString(@"settings.joinTwitter","joinTwitter"),@"url":@"https://twitter.com/fastclip1"} ] }, @{ @@ -126,6 +133,7 @@ - (UITableView *)tableView{ _tableView.contentInset = UIEdgeInsetsMake(20, 0, 0, 0); _tableView.dataSource = self; _tableView.delegate = self; + _tableView.backgroundColor = DynamicColor(RGB(20, 20, 20),RGB(246, 246, 246)); [self.view addSubview:_tableView]; } diff --git a/Stay/Main/SYSearch/Controller/SYSearchViewController.m b/Stay/Main/SYSearch/Controller/SYSearchViewController.m index 2505800b..8147f46a 100644 --- a/Stay/Main/SYSearch/Controller/SYSearchViewController.m +++ b/Stay/Main/SYSearch/Controller/SYSearchViewController.m @@ -10,311 +10,248 @@ #import "SYDetailViewController.h" #import "JSDetailCell.h" #import "UserscriptUpdateManager.h" +#import "BrowseView.h" +#import "ScriptMananger.h" +#import "FCStyle.h" +@interface SimpleLoadingView : UIView -@interface SYSearchViewController () +@property (nonatomic, strong) UIActivityIndicatorView *indicator; +@property (nonatomic, strong) UILabel *label; +- (void)start; +- (void)stop; +@end + +@implementation SimpleLoadingView + +- (instancetype)initWithFrame:(CGRect)frame{ + if (self = [super initWithFrame:frame]){ + [self indicator]; + [self label]; + } + + return self; +} + +- (void)start{ + [self.superview bringSubviewToFront:self]; + self.hidden = NO; + [self.indicator startAnimating]; +} + +- (void)stop{ + [self.superview sendSubviewToBack:self]; + self.hidden = YES; + [self.indicator stopAnimating]; +} + +- (void)willMoveToSuperview:(UIView *)newSuperview{ + [super willMoveToSuperview:newSuperview]; + [self.label sizeToFit]; + CGFloat width = self.indicator.frame.size.width + self.label.frame.size.width; + CGFloat left = (self.frame.size.width - width) / 2; + [self.indicator setFrame:CGRectMake(left, + (self.frame.size.height - self.indicator.frame.size.height)/2, + self.indicator.frame.size.width, + self.indicator.frame.size.height)]; + [self.label setFrame:CGRectMake(self.indicator.frame.origin.x + self.indicator.frame.size.width + 15, + (self.frame.size.height - self.label.frame.size.height)/2, + self.label.frame.size.width, + self.label.frame.size.height)]; + [self.indicator startAnimating]; +} + +- (UIActivityIndicatorView *)indicator{ + if (nil == _indicator){ + _indicator = [[UIActivityIndicatorView alloc] init]; + [self addSubview:_indicator]; + } + + return _indicator; +} + +- (UILabel *)label{ + if (nil == _label){ + _label = [[UILabel alloc] initWithFrame:CGRectZero]; + _label.font = FCStyle.body; + _label.textColor = FCStyle.fcBlack; + _label.text = NSLocalizedString(@"Loading", @""); + [self addSubview:_label]; + } + + return _label; +} + + +- (void)setHidden:(BOOL)hidden{ + [super setHidden:hidden]; + +} + +@end + + +@interface SYSearchViewController () -@property (nonatomic, strong) UISearchController *searchController; @property (nonatomic, strong) UITableView *tableView; // 数据源数组 @property (nonatomic, strong) NSMutableArray *datas; -// 搜索结果数组 -@property (nonatomic, strong) NSMutableArray *results; - +@property (nonatomic, strong) SimpleLoadingView *simpleLoadingView; @end @implementation SYSearchViewController - (void)viewDidLoad { [super viewDidLoad]; + [self simpleLoadingView]; // Do any additional setup after loading the view. - self.view.backgroundColor = [UIColor whiteColor]; - UISearchController *search = [[UISearchController alloc]initWithSearchResultsController:nil]; - // 设置结果更新代理 -// search.searchResultsUpdater = self; - self.navigationItem.searchController = search; - self.navigationItem.searchController.obscuresBackgroundDuringPresentation = false; - self.navigationItem.hidesSearchBarWhenScrolling = false; - self.searchController = search; - self.searchController.delegate = self; - self.searchController.searchBar.delegate = self; - [self.searchController.searchBar setTintColor:RGB(182, 32, 224)]; - search.searchBar.placeholder = @"All userscripts"; - [_datas removeAllObjects]; - [_datas addObjectsFromArray:[[DataManager shareManager] findScriptInLib]]; - [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(uploadScriptSuccess:) name:@"uploadScriptSuccess" object:nil]; - - [self.tableView reloadData]; + self.view.backgroundColor = DynamicColor(RGB(28, 28, 28),RGB(240, 240, 245)); + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarChange) name:UIDeviceOrientationDidChangeNotification object:nil]; + [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(statusBarChange) name:@"scriptSaveSuccess" object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil]; } -- (void)uploadScriptSuccess:(id)sender{ - [_datas removeAllObjects]; - [_datas addObjectsFromArray:[[DataManager shareManager] findScriptInLib]]; - dispatch_async(dispatch_get_main_queue(), ^{ - [self.tableView reloadData]; + +- (void)queryData{ + if (self.datas.count == 0){ + [self.simpleLoadingView start]; + } + dispatch_async(dispatch_get_global_queue(0, DISPATCH_QUEUE_PRIORITY_DEFAULT),^{ + + NSMutableCharacterSet *set = [[NSCharacterSet URLFragmentAllowedCharacterSet] mutableCopy]; + [set addCharactersInString:@"#"]; + NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:[@"https://fastclip.app/stay/browser.json" stringByAddingPercentEncodingWithAllowedCharacters:set]]]; + + if (data.length > 0) { + NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil]; + self.datas = dic[@"categories"]; + } + + dispatch_async(dispatch_get_main_queue(),^{ + [self.simpleLoadingView stop]; + [self.tableView reloadData]; + }); }); } +- (void)onBecomeActive { + [self queryData]; +} + + - (void)statusBarChange{ dispatch_async(dispatch_get_main_queue(), ^{ + [[ScriptMananger shareManager] refreshData]; self.tableView.frame = self.view.bounds; [self.tableView reloadData]; }); } -#pragma mark - UISearchResultsUpdating -- (void)updateSearchResultsForSearchController:(nonnull UISearchController *)searchController { - NSString *inputStr = searchController.searchBar.text; - return; -} - - -#pragma mark -searchBarDelegate - -- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar { - self.searchController.searchBar.showsScopeBar = false; -// [searchBar resignFirstResponder]; - [self.searchController setActive:NO]; - [self.tableView reloadData]; - [_results removeAllObjects]; -} -- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar { - [self.searchController setActive:YES]; - [self.tableView reloadData]; -// self.searchController.searchBar.showsCancelButton = true; - for (UIView *view in [[ self.searchController.searchBar.subviews lastObject] subviews]) { - if ([view isKindOfClass:[UIButton class]]) { - UIButton *cancelBtn = (UIButton *)view; - [cancelBtn setTitle:@"取消" forState:UIControlStateNormal]; - [cancelBtn setTitleColor:RGB(182, 32, 224) forState:UIControlStateNormal]; - } - - } - return YES; -} - -- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText { - [_results removeAllObjects]; - if(searchText.length > 0) { - [_results addObjectsFromArray:[[DataManager shareManager] selectScriptByKeywordByLib:searchText]]; - } - [self.tableView reloadData]; - -} #pragma mark - UITableViewDelegate - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - if (self.searchController.active) { - return self.results.count ; - } - + [tableView setSeparatorStyle:UITableViewCellSeparatorStyleNone]; return self.datas.count; } +//-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section +//{ +// return 30; +//} +// +//- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { +// return self.datas.count; +//} + +//- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section { +// UITableViewHeaderFooterView *header = (UITableViewHeaderFooterView *)view; +// [header.textLabel setFont:[UIFont boldSystemFontOfSize:20]]; +// [header.textLabel setTextColor:DynamicColor([UIColor whiteColor],[UIColor blackColor])]; +// +// header.contentView.backgroundColor = DynamicColor(RGB(20, 20, 20),RGB(246, 246, 246)); +// +// +//} + + - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cellID"]; if (cell == nil) { cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cellID"]; cell.selectionStyle = UITableViewCellSelectionStyleNone; - cell.accessoryType=UITableViewCellAccessoryDisclosureIndicator; } + cell.backgroundColor = DynamicColor(RGB(20, 20, 20),RGB(246, 246, 246)); + cell.contentView.backgroundColor =DynamicColor(RGB(20, 20, 20),RGB(246, 246, 246)); for (UIView *subView in cell.contentView.subviews) { [subView removeFromSuperview]; } - UserScript *model = nil; - - if (self.searchController.active ) { - model = _results[indexPath.row]; - } else { - model = _datas[indexPath.row]; - } - - UIColor *bgColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull trainCollection) { - if ([trainCollection userInterfaceStyle] == UIUserInterfaceStyleLight) { - return [UIColor whiteColor]; - } - else { - return [UIColor blackColor]; - } - }]; - - cell.contentView.backgroundColor = bgColor; + UILabel *lab = [[UILabel alloc] initWithFrame:CGRectMake(15, 15, kScreenWidth - 30, 30)]; + lab.text = self.datas[indexPath.row][@"name"]; + lab.font = [UIFont boldSystemFontOfSize:20]; - UILabel *titleLabel = [[UILabel alloc]initWithFrame:CGRectMake(15, 10, kScreenWidth / 3 * 2, 21)]; - titleLabel.font = [UIFont boldSystemFontOfSize:18]; - titleLabel.textAlignment = NSTextAlignmentLeft; - titleLabel.lineBreakMode= NSLineBreakByTruncatingTail; - titleLabel.text = model.name; - [titleLabel sizeToFit]; - [cell.contentView addSubview:titleLabel]; + [cell.contentView addSubview:lab]; - UILabel *versionLabel = [[UILabel alloc] initWithFrame:CGRectMake(15, 10, kScreenWidth / 2, 21)]; - versionLabel.font = [UIFont boldSystemFontOfSize:15]; - versionLabel.textAlignment = NSTextAlignmentLeft; - versionLabel.text = model.version; - versionLabel.textColor = RGB(182, 32, 224); - versionLabel.left = titleLabel.right + 5; - versionLabel.centerY = titleLabel.centerY; - [cell.contentView addSubview:versionLabel]; - - UILabel *authorLabel = [[UILabel alloc]initWithFrame:CGRectMake(15, 5, kScreenWidth, 19)]; - authorLabel.font = [UIFont systemFontOfSize:16]; - authorLabel.textAlignment = NSTextAlignmentLeft; - authorLabel.text = model.author; - authorLabel.top = titleLabel.bottom + 10; - [authorLabel sizeToFit]; - [cell.contentView addSubview:authorLabel]; + UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 55, kScreenWidth, 145)]; + scrollView.showsHorizontalScrollIndicator = FALSE; + NSArray *array = self.datas[indexPath.row][@"userscripts"]; + + if(array != nil && array.count > 0) { + CGFloat width = 15; + + for (int i = 0;i < array.count; i++) { + BrowseView *browseView = [[BrowseView alloc] initWithFrame:CGRectMake(width, 0, 230, 145)]; + browseView.navigationController = self.navigationController; + browseView.layer.cornerRadius = 12; + [browseView loadView:array[i]]; + [scrollView addSubview:browseView]; + width = width + 245; + } + scrollView.contentSize = CGSizeMake(width, 145); + } - UILabel *descLabel = [[UILabel alloc]initWithFrame:CGRectMake(15, 5, kScreenWidth, 19)]; - descLabel.font = [UIFont systemFontOfSize:15]; - descLabel.textAlignment = NSTextAlignmentLeft; - descLabel.text = model.desc; - descLabel.top = authorLabel.bottom + 5; - descLabel.textColor = [UIColor grayColor]; - [cell.contentView addSubview:descLabel]; + [cell.contentView addSubview:scrollView]; - UILabel *actLabel = [[UILabel alloc]init]; - actLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightLight]; - actLabel.textColor = RGB(138, 138, 138); - if(model.active == 0) { - actLabel.text = @""; - } else { - actLabel.text = @"Added"; - } - [actLabel sizeToFit]; - actLabel.right = kScreenWidth - 35; - actLabel.centerY = 47.5f; - - [cell.contentView addSubview:actLabel]; - UIView *line = [[UIView alloc] initWithFrame:CGRectMake(15,94,kScreenWidth - 10,1)]; - UIColor *lineBgcolor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull trainCollection) { - if ([trainCollection userInterfaceStyle] == UIUserInterfaceStyleLight) { - return RGBA(216, 216, 216, 0.3); - } - else { - return RGBA(37, 37, 40, 1); - } - }]; - [line setBackgroundColor:lineBgcolor]; - [cell.contentView addSubview:line]; return cell; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ - return 95.0f; + return 200.0f; } -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - if (self.searchController.active) { -// [self.searchControler.searchBar resignFirstResponder]; - UserScript *model = _results[indexPath.row]; - SYDetailViewController *cer = [[SYDetailViewController alloc] init]; - cer.script = model; - cer.isSearch = true; - [self.navigationController pushViewController:cer animated:true]; - } else { - UserScript *model = _datas[indexPath.row]; - SYDetailViewController *cer = [[SYDetailViewController alloc] init]; - cer.script = model; - cer.isSearch = true; - [self.navigationController pushViewController:cer animated:true]; - } -} - -- (UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath { - - if (self.searchController.active) { - UserScript *model = _results[indexPath.row]; - if (model.active) { - return nil; - } - UIContextualAction *addAction = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleNormal title:@"" handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) { - UserScript *model = _results[indexPath.row]; - [[DataManager shareManager] insertToUserScriptnumberId:model.uuid]; - [[DataManager shareManager] updateLibScrpitStatus:1 numberId:model.uuid]; - [tableView setEditing:NO animated:YES]; - [self.tableView reloadData]; - [self initScrpitContent]; - [self addScriptTips]; - }]; - [addAction setTitle:@"Add"]; - addAction.backgroundColor = RGB(182, 32, 224); - return [UISwipeActionsConfiguration configurationWithActions:@[addAction]]; - - } else { - UserScript *model = _datas[indexPath.row]; - if (model.active) { - return nil; - } - UIContextualAction *addAction = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleNormal title:@"" handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) { - UserScript *model = _datas[indexPath.row]; - [[DataManager shareManager] insertToUserScriptnumberId:model.uuid]; - [[DataManager shareManager] updateLibScrpitStatus:1 numberId:model.uuid]; - [tableView setEditing:NO animated:YES]; - [self reloadTableView]; - [self initScrpitContent]; - [self.tableView reloadData]; - [self addScriptTips]; - - }]; - [addAction setTitle:@"Add"]; - addAction.backgroundColor = RGB(182, 32, 224); - return [UISwipeActionsConfiguration configurationWithActions:@[addAction]]; - } -} - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { return YES; } -- (void)addScriptTips{ - NSString *content = @"添加到资料库成功"; - UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提示" message:content preferredStyle:UIAlertControllerStyleAlert]; - UIAlertAction *conform = [UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { - NSLog(@"点击了确认按钮"); - }]; - [alert addAction:conform]; - [self presentViewController:alert animated:YES completion:nil]; -} - -- (void)initScrpitContent{ - NSUserDefaults *groupUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.dajiu.stay.pro"]; - - NSMutableArray *array = [[NSMutableArray alloc] init]; - - for(int i = 0; i < self.datas.count; i++) { - UserScript *scrpit = self.datas[i]; - [array addObject: [scrpit toDictionary]]; - } - [groupUserDefaults setObject:array forKey:@"ACTIVE_SCRIPTS"]; - [groupUserDefaults synchronize]; -} - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; - [self reloadTableView]; + [[ScriptMananger shareManager] refreshData]; dispatch_async(dispatch_get_main_queue(), ^{ - self.tableView.frame = self.view.bounds; - [self.tableView reloadData]; + if (self.datas.count > 0){ + self.tableView.frame = self.view.bounds; + [self.tableView reloadData]; + } + }); -} -- (void) reloadTableView { - [_datas removeAllObjects]; - [_datas addObjectsFromArray:[[DataManager shareManager] findScriptInLib]]; + + [self queryData]; } + - (UITableView *)tableView { if (_tableView == nil) { _tableView = [[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStylePlain]; _tableView.delegate = self; _tableView.dataSource = self; + _tableView.backgroundColor = DynamicColor(RGB(20, 20, 20),RGB(246, 246, 246)); [self.view addSubview:_tableView]; } @@ -329,22 +266,16 @@ - (NSMutableArray *)datas { return _datas; } -- (NSMutableArray *)results { - if (_results == nil) { - _results = [NSMutableArray arrayWithCapacity:0]; +- (SimpleLoadingView *)simpleLoadingView{ + if (nil == _simpleLoadingView){ + _simpleLoadingView = [[SimpleLoadingView alloc] initWithFrame:CGRectMake(0, + (self.view.frame.size.height - 50) / 2, + self.view.frame.size.width, 50)]; + [self.view addSubview:_simpleLoadingView]; } - return _results; + return _simpleLoadingView; } -/* -#pragma mark - Navigation - -// In a storyboard-based application, you will often want to do a little preparation before navigation -- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { - // Get the new view controller using [segue destinationViewController]. - // Pass the selected object to the new view controller. -} -*/ @end diff --git a/Stay/Main/SYSearch/View/BrowseView.h b/Stay/Main/SYSearch/View/BrowseView.h new file mode 100644 index 00000000..e76107b3 --- /dev/null +++ b/Stay/Main/SYSearch/View/BrowseView.h @@ -0,0 +1,31 @@ +// +// BrowseView.h +// Stay +// +// Created by zly on 2022/5/10. +// + +#import +#import "UserScript.h" +NS_ASSUME_NONNULL_BEGIN + +@interface BrowseView : UIView +{ + NSString *downloadUrl; +} + +@property (nonatomic, strong) UILabel *titleLabel; +@property (nonatomic, strong) UILabel *authorLabel; +@property (nonatomic, strong) UILabel *descLabel; +@property (nonatomic, strong) UIButton *rightBtn; +@property (nonatomic, strong) UIButton *addBtn; +@property (nonatomic, strong) UINavigationController *navigationController; + + + +- (void)loadView:(NSDictionary *)dic; + + +@end + +NS_ASSUME_NONNULL_END diff --git a/Stay/Main/SYSearch/View/BrowseView.m b/Stay/Main/SYSearch/View/BrowseView.m new file mode 100644 index 00000000..42fac3d2 --- /dev/null +++ b/Stay/Main/SYSearch/View/BrowseView.m @@ -0,0 +1,179 @@ +// +// BrowseView.m +// Stay +// +// Created by zly on 2022/5/10. +// + +#import "BrowseView.h" +#import "SYEditViewController.h" +#import "ScriptMananger.h" +#import "ScriptEntity.h" +#import +#import "LoadingSlideController.h" + +@interface BrowseView() + +@property (nonatomic, strong) LoadingSlideController *loadingSlideController; +@end + +@implementation BrowseView + +- (instancetype)initWithFrame:(CGRect)frame{ + self = [super initWithFrame:frame]; + if (self){ + self.backgroundColor = DynamicColor(RGB(28, 28, 28),[UIColor whiteColor]); + [self setupSubviews]; + } + return self; +} + +- (void)setupSubviews{ + [self addSubview:self.titleLabel]; + [self addSubview:self.authorLabel]; + [self addSubview:self.descLabel]; + [self addSubview:self.addBtn]; + [self addSubview:self.rightBtn]; +} + +- (void)loadView:(NSDictionary *)dic { + self.titleLabel.text = dic[@"name"]; + self.authorLabel.text = dic[@"author"]; + self.descLabel.text = dic[@"description"]; + self.authorLabel.top = self.titleLabel.bottom + 5; + self.descLabel.top = self.authorLabel.bottom + 5; + self.addBtn.top = self.rightBtn.top = 8; + self.addBtn.right = self.rightBtn.right = 220; + NSString *uuidName = [NSString stringWithFormat:@"%@%@",dic[@"name"],dic[@"namespace"]]; + NSString *uuid = [self md5HexDigest:uuidName]; + ScriptEntity *entity = [ScriptMananger shareManager].scriptDic[uuid]; + + if(entity == nil) { + self.rightBtn.hidden = true; + } else { + self.addBtn.hidden = true; + } + downloadUrl = dic[@"downloadURL"]; + +} + +- (void)addScript:(id)sender { + self.loadingSlideController.originSubText = self.titleLabel.text; + [self.loadingSlideController show]; + + NSMutableCharacterSet *set = [[NSCharacterSet URLFragmentAllowedCharacterSet] mutableCopy]; + [set addCharactersInString:@"#"]; + dispatch_async(dispatch_get_global_queue(0, DISPATCH_QUEUE_PRIORITY_DEFAULT),^{ + NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:[downloadUrl stringByAddingPercentEncodingWithAllowedCharacters:set]]]; + dispatch_async(dispatch_get_main_queue(),^{ + if(data != nil ) { + + if (self.loadingSlideController.isShown){ + [self.loadingSlideController dismiss]; + self.loadingSlideController = nil; + } + + NSString *str = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]; + SYEditViewController *cer = [[SYEditViewController alloc] init]; + cer.content = str; + cer.downloadUrl = downloadUrl; + [self.navigationController pushViewController:cer animated:true]; + } + else{ + [self.loadingSlideController updateSubText:NSLocalizedString(@"Error", @"")]; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), + dispatch_get_main_queue(), ^{ + if (self.loadingSlideController.isShown){ + [self.loadingSlideController dismiss]; + self.loadingSlideController = nil; + } + }); + } + }); + }); +} + + +- (NSString* )md5HexDigest:(NSString* )input { + const char *cStr = [input UTF8String]; + unsigned char digest[CC_MD5_DIGEST_LENGTH]; + CC_MD5(cStr, (CC_LONG)strlen(cStr), digest); + NSMutableString *result = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2]; + for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) { + [result appendFormat:@"%02X", digest[i]]; + } + return result; +} + + +- (UILabel *)titleLabel { + if (_titleLabel == nil) { + _titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(15, 30, 200, 32)]; + _titleLabel.font = [UIFont boldSystemFontOfSize:17]; + _titleLabel.textAlignment = NSTextAlignmentLeft; + _titleLabel.numberOfLines = 2; + _titleLabel.textColor = DynamicColor([UIColor whiteColor],[UIColor blackColor]); + + } + return _titleLabel; +} + + +- (UILabel *)authorLabel { + if(_authorLabel == nil) { + _authorLabel = [[UILabel alloc] initWithFrame:CGRectMake(15, 0, 200, 18)]; + _authorLabel.font = [UIFont systemFontOfSize:16]; + _authorLabel.lineBreakMode= NSLineBreakByTruncatingTail; + _authorLabel.textColor = DynamicColor([UIColor whiteColor],[UIColor blackColor]); + } + return _authorLabel; +} + +- (UILabel *)descLabel { + if (_descLabel == nil) { + _descLabel = [[UILabel alloc] initWithFrame:CGRectMake(15, 0, 200, 42)]; + _descLabel.font = [UIFont systemFontOfSize:16]; + _descLabel.numberOfLines = 2; + _descLabel.lineBreakMode= NSLineBreakByTruncatingTail; + _descLabel.textColor = RGB(138, 138, 138); + } + return _descLabel; +} + +- (UIButton *)rightBtn { + if(_rightBtn == nil) { + _rightBtn = [UIButton buttonWithType:UIButtonTypeCustom]; + _rightBtn.frame = CGRectMake(0, 0, 26, 24); + + UIImage *image = [UIImage systemImageNamed:@"checkmark.circle.fill" + withConfiguration:[UIImageSymbolConfiguration configurationWithFont:[UIFont systemFontOfSize:15]]]; + image = [image imageWithTintColor: RGB(182,32,224) renderingMode:UIImageRenderingModeAlwaysOriginal]; + [_rightBtn setBackgroundImage:image forState:UIControlStateNormal]; + + } + return _rightBtn; +} + +- (UIButton *)addBtn { + if(_addBtn == nil) { + _addBtn = [UIButton buttonWithType:UIButtonTypeCustom]; + _addBtn.frame = CGRectMake(0, 0, 26, 24); + UIImage *image = [UIImage systemImageNamed:@"plus.circle.fill" + withConfiguration:[UIImageSymbolConfiguration configurationWithFont:[UIFont systemFontOfSize:15]]]; + image = [image imageWithTintColor: RGB(182,32,224) renderingMode:UIImageRenderingModeAlwaysOriginal]; + [_addBtn setBackgroundImage:image forState:UIControlStateNormal]; + [_addBtn addTarget:self action:@selector(addScript:) forControlEvents:UIControlEventTouchUpInside]; + } + return _addBtn; +} + +- (LoadingSlideController *)loadingSlideController{ + if (nil == _loadingSlideController){ + _loadingSlideController = [[LoadingSlideController alloc] init]; + _loadingSlideController.originMainText = NSLocalizedString(@"settings.downloadScript", @""); + } + + return _loadingSlideController; +} + +@end diff --git a/Stay/Main/Style/FCStyle.h b/Stay/Main/Style/FCStyle.h new file mode 100644 index 00000000..70f18316 --- /dev/null +++ b/Stay/Main/Style/FCStyle.h @@ -0,0 +1,49 @@ +// +// FCStyle.h +// FastClip-iOS +// +// Created by King on 22/2/2022. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface FCStyle : NSObject + +@property(class, nonatomic, readonly) UIColor *accent; +@property(class, nonatomic, readonly) UIColor *accentHighlight; +@property(class, nonatomic, readonly) UIColor *accentHover; +@property(class, nonatomic, readonly) UIColor *accentSelected; +@property(class, nonatomic, readonly) UIColor *background; +@property(class, nonatomic, readonly) UIColor *secondaryBackground; +@property(class, nonatomic, readonly) UIColor *tertiaryBackground; +@property(class, nonatomic, readonly) UIColor *popup; +@property(class, nonatomic, readonly) UIColor *secondaryPopup; +@property(class, nonatomic, readonly) UIColor *fcSeparator; +@property(class, nonatomic, readonly) UIColor *fcPlaceHolder; +@property(class, nonatomic, readonly) UIColor *fcBlack; +@property(class, nonatomic, readonly) UIColor *fcSecondaryBlack; +@property(class, nonatomic, readonly) UIColor *fcBlue; +@property(class, nonatomic, readonly) UIColor *fcGolden; +@property(class, nonatomic, readonly) UIColor *backgroundGolden; +@property(class, nonatomic, readonly) UIColor *borderGolden; + +@property(class, nonatomic, readonly) UIFont *headline; +@property(class, nonatomic, readonly) UIFont *headlineBold; +@property(class, nonatomic, readonly) UIFont *subHeadline; +@property(class, nonatomic, readonly) UIFont *subHeadlineBold; +@property(class, nonatomic, readonly) UIFont *body; +@property(class, nonatomic, readonly) UIFont *bodyBold; +@property(class, nonatomic, readonly) UIFont *footnote; +@property(class, nonatomic, readonly) UIFont *footnoteBold; +@property(class, nonatomic, readonly) UIFont *title3; + +@property(class, nonatomic, readonly) UIColor *fcShadowLine; +@property(class, nonatomic, readonly) UIFont *sfFootnote; +@property(class, nonatomic, readonly) UIFont *sfActbar; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Stay/Main/Style/FCStyle.m b/Stay/Main/Style/FCStyle.m new file mode 100644 index 00000000..85ee93a4 --- /dev/null +++ b/Stay/Main/Style/FCStyle.m @@ -0,0 +1,168 @@ +// +// FCStyle.m +// FastClip-iOS +// +// Created by King on 22/2/2022. +// + +#import "FCStyle.h" + +@implementation FCStyle + ++ (UIColor *)accent { + return [UIColor colorNamed: @"AccentClassicColor"]; +} + ++ (UIColor *)accentHighlight { + return [UIColor colorNamed: @"SecondaryAccentClassicColor"]; +} + ++ (UIColor *)accentHover { + return [UIColor colorNamed: @"TertiaryAccentClassicColor"]; +} + ++ (UIColor *)accentSelected { + return [UIColor colorNamed: @"SelectedAccentClassicColor"]; +} + ++ (UIColor *)background { + return [UIColor colorNamed: @"BackgroundColor"]; +} + ++ (UIColor *)secondaryBackground { + return [UIColor colorNamed: @"SecondaryBackgroundColor"]; +} + ++ (UIColor *)tertiaryBackground { + return [UIColor colorNamed: @"TertiaryBackgroundColor"]; +} + ++ (UIColor *)popup { + return [UIColor colorNamed: @"PopupColor"]; +} + ++ (UIColor *)secondaryPopup { + return [UIColor colorNamed: @"SecondaryPopupColor"]; +} + ++ (UIColor *)fcSeparator { + return [UIColor colorNamed: @"FCSeparatorColor"]; +} + ++ (UIColor *)fcPlaceHolder { + return UIColor.placeholderTextColor; +} + ++ (UIColor *)fcBlack { + return [UIColor colorNamed: @"FCBlackColor"]; +} + ++ (UIColor *)fcSecondaryBlack { + return [UIColor colorNamed: @"FCSecondaryBlackColor"]; +} + ++ (UIColor *)fcShadowLine { + return [UIColor colorNamed: @"FCShadowLineColor"]; +} + ++ (UIColor *)fcBlue { + return [UIColor colorNamed:@"FCBlueColor"]; +} + ++ (UIColor *)fcGolden { + return [UIColor colorNamed:@"FCGoldenColor"]; +} + ++ (UIColor *)backgroundGolden { + return [UIColor colorNamed:@"BackgroundGoldenColor"]; +} + ++ (UIColor *)borderGolden { + return [UIColor colorNamed:@"BorderGoldenColor"]; +} + ++ (UIFont *)title3{ + return [UIFont systemFontOfSize:20]; +} + ++ (UIFont *)headline { +#ifdef iOS + return [UIFont systemFontOfSize:17]; +#else + return [UIFont systemFontOfSize:15]; +#endif +} + ++ (UIFont *)headlineBold { +#ifdef iOS + return [UIFont boldSystemFontOfSize:17]; +#else + return [UIFont boldSystemFontOfSize:15]; +#endif +} + ++ (UIFont *)subHeadline { +#ifdef iOS + return [UIFont systemFontOfSize:15]; +#else + return [UIFont systemFontOfSize:13]; +#endif +} + ++ (UIFont *)subHeadlineBold { +#ifdef iOS + return [UIFont boldSystemFontOfSize:15]; +#else + return [UIFont boldSystemFontOfSize:13]; +#endif +} + ++ (UIFont *)body { +#ifdef iOS + return [UIFont systemFontOfSize:16]; +#else + return [UIFont systemFontOfSize:14]; +#endif +} + ++ (UIFont *)bodyBold { +#ifdef iOS + return [UIFont boldSystemFontOfSize:16]; +#else + return [UIFont boldSystemFontOfSize:14]; +#endif +} + ++ (UIFont *)footnote { +#ifdef iOS + return [UIFont systemFontOfSize:13]; +#else + return [UIFont systemFontOfSize:11]; +#endif +} + ++ (UIFont *)footnoteBold { +#ifdef iOS + return [UIFont boldSystemFontOfSize:13]; +#else + return [UIFont boldSystemFontOfSize:11]; +#endif +} + ++ (UIFont *)sfFootnote { +#ifdef iOS + return [UIFont systemFontOfSize:13]; +#else + return [UIFont systemFontOfSize:18]; +#endif +} + ++ (UIFont *)sfActbar { +#ifdef iOS + return [UIFont systemFontOfSize:17]; +#else + return [UIFont systemFontOfSize:18]; +#endif +} + +@end diff --git a/Stay/MainTabBarController.m b/Stay/MainTabBarController.m index a8ea6cc2..58cc164a 100644 --- a/Stay/MainTabBarController.m +++ b/Stay/MainTabBarController.m @@ -58,15 +58,19 @@ - (void)setUpOneChildViewController:(UIViewController *)viewController image:(UI NSDictionary *dictHome = [NSDictionary dictionaryWithObject:UIColorWithRGBA(185,101,223,1) forKey:NSForegroundColorAttributeName]; [navC.tabBarItem setTitleTextAttributes:dictHome forState:UIControlStateSelected]; navC.navigationBar.tintColor = RGB(182, 32, 224); - +// navC.navigationBar.barTintColor = RGB(138, 138, 138); UINavigationBarAppearance *appearance =[UINavigationBarAppearance new]; [appearance configureWithOpaqueBackground]; + appearance.backgroundColor = DynamicColor(RGB(20, 20, 20),RGB(246, 246, 246)); navC.navigationBar.standardAppearance = appearance; navC.navigationBar.scrollEdgeAppearance = appearance; UITabBarAppearance *tabbarAppearance = [[UITabBarAppearance alloc] init]; - [tabbarAppearance.stackedLayoutAppearance.selected setTitleTextAttributes:@{NSForegroundColorAttributeName: RGB(182, 32, 224)}]; - [tabbarAppearance.inlineLayoutAppearance.selected setTitleTextAttributes:@{NSForegroundColorAttributeName: RGB(182, 32, 224)}]; -// tabbarAppearance.stackedLayoutAppearance.selected.titleTextAttributes = @{NSForegroundColorAttributeName: RGB(182, 32, 224)}; + NSMutableParagraphStyle *paraStyle = [[NSMutableParagraphStyle alloc] init]; + paraStyle.alignment = NSTextAlignmentLeft; + [tabbarAppearance.stackedLayoutAppearance.selected setTitleTextAttributes:@{NSForegroundColorAttributeName: RGB(182, 32, 224),NSParagraphStyleAttributeName : paraStyle}]; + + [tabbarAppearance.inlineLayoutAppearance.selected setTitleTextAttributes:@{NSForegroundColorAttributeName: RGB(182, 32, 224),NSParagraphStyleAttributeName : paraStyle}]; + tabbarAppearance.backgroundColor = DynamicColor(RGB(20, 20, 20),RGB(246, 246, 246)); self.tabBar.scrollEdgeAppearance = tabbarAppearance; self.tabBar.standardAppearance = tabbarAppearance; [self addChildViewController:navC]; diff --git a/Stay/SceneDelegate.m b/Stay/SceneDelegate.m index 3808882f..6c1e9de5 100644 --- a/Stay/SceneDelegate.m +++ b/Stay/SceneDelegate.m @@ -9,6 +9,7 @@ #import "NavigationController.h" //#import "ViewController.h" #import "MainTabBarController.h" +#import "IACManager.h" @implementation SceneDelegate @@ -22,4 +23,12 @@ - (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session op [self.window makeKeyAndVisible]; } +- (void)scene:(UIScene *)scene openURLContexts:(NSSet *)URLContexts{ + UIOpenURLContext *context = URLContexts.anyObject; + if (context){ + [[IACManager sharedManager] handleOpenURL:context.URL]; + } +} + + @end diff --git a/Stay/en.lproj/Localizable.strings b/Stay/en.lproj/Localizable.strings index 47434971..4af427cd 100644 --- a/Stay/en.lproj/Localizable.strings +++ b/Stay/en.lproj/Localizable.strings @@ -8,8 +8,11 @@ //settings settings.rateApp="Rate app"; -settings.openSource="Open Source"; +settings.openSource="Open source"; settings.joinTelegram="Join Telegram"; +settings.joinQQ="Join QQ"; +settings.joinTwitter="Follow Twitter"; + settings.enableStay="Turn on Stay in Safari extensions"; @@ -20,7 +23,7 @@ settings.section.otherApps.fastclip="FastClip - Copy paste enhancer"; settings.save="Save"; settings.library="Library"; -settings.search="Search"; +settings.search="Browse"; settings.more="More"; settings.author="Author"; settings.version="Version"; @@ -44,6 +47,15 @@ settings.scriptError = "script error"; unsupportedFileFormat = "Unsupported file format."; ok = "OK"; +settings.update = "Update"; +cancel = "Cancel"; +Error="Error"; +Loading="Loading"; + +Undo="Undo"; +Redo="Redo"; +Clear="Clear"; +Clipboard="From clipboard"; diff --git a/Stay/ja.lproj/Localizable.strings b/Stay/ja.lproj/Localizable.strings new file mode 100644 index 00000000..2b5fe7dd --- /dev/null +++ b/Stay/ja.lproj/Localizable.strings @@ -0,0 +1,60 @@ +/* + Localizable.strings + Stay + + Created by ris on 2021/10/21. + +*/ + +//settings +settings.rateApp="アプリに評価を付ける"; +settings.openSource="オープンソース"; +settings.joinTelegram="Telegramに加入する"; +settings.joinQQ="QQに加入する"; +settings.joinTwitter="Twitterに加入する"; + +settings.enableStay="Turn on Stay in Safari extensions"; + + +settings.section.otherApps="Other applications"; + +settings.section.otherApps.fastclip="FastClip - Copy paste enhancer"; + +settings.save="保存"; + +settings.library="ライブラリー"; +settings.search="ブラウズ"; +settings.more="もっと"; +settings.author="作成者"; +settings.version="バージョン"; +settings.scriptContent="内容"; +settings.sourcePage="ソースページ"; +settings.descDetail="詳細"; +settings.autoUpdate="自動更新"; +settings.add="追加"; +settings.delete="削除"; +settings.create="作成"; +settings.newScript="新しいスクリプト"; +settings.notes="ノート"; +settings.uploadTips="ロード中"; +settings.addScript="スクリプトを書く"; +settings.addScriptFromUrl="リンクからインポートする"; +settings.addScriptFromWeb="GreasyForkから導入"; +settings.importFromFile = "ローカルファイルから導入"; +settings.downloadScript = "ダウンロード"; +settings.close = "閉じる"; +settings.scriptError = "エラー"; + +unsupportedFileFormat = "対応していないファイルタイプ"; +ok = "OK"; +settings.update = "更新"; +cancel = "キャンセル"; +Error="エラー"; +Loading="読み込み中"; + +Undo="Undo"; +Redo="Redo"; +Clear="Clear"; +Clipboard="From clipboard"; + + diff --git a/Stay/ja.lproj/Main.html b/Stay/ja.lproj/Main.html new file mode 100644 index 00000000..28cdd287 --- /dev/null +++ b/Stay/ja.lproj/Main.html @@ -0,0 +1,15 @@ + + + + + + + + + + + + Stay Icon +

You can turn on Stay’s Safari extension in Settings.

+ + diff --git a/Stay/tampermonkey/Tampermonkey.m b/Stay/tampermonkey/Tampermonkey.m index 6b145ff2..b94fb18b 100644 --- a/Stay/tampermonkey/Tampermonkey.m +++ b/Stay/tampermonkey/Tampermonkey.m @@ -36,6 +36,7 @@ - (UserScript *)parseScript:(NSString *)scriptName{ return [self parseWithScriptContent:scriptContent]; } +//Enter - (UserScript *)parseWithScriptContent:(NSString *)scriptContent{ JSValue *parseUserScript = [self.jsContext evaluateScript:@"window.parseUserScript"]; JSValue *userScriptHeader = [parseUserScript callWithArguments:@[scriptContent,@""]]; @@ -43,7 +44,35 @@ - (UserScript *)parseWithScriptContent:(NSString *)scriptContent{ UserScript *userScript = [UserScript ofDictionary:[userScriptHeader toDictionary]]; userScript.uuid = [[NSUUID UUID] UUIDString]; userScript.content = scriptContent; - + + //Check if unsupported grants api really used in content. + if (userScript.pass && userScript.unsupportedGrants.count > 0){ + NSString *scriptWithoutComment = [self _removeComment:userScript.content]; + NSMutableString *builder = [[NSMutableString alloc] initWithString:@"("]; + + for (int i = 0; i < userScript.unsupportedGrants.count; i++){ + NSString *unsupportedGrant = userScript.unsupportedGrants[i]; + if (i != userScript.unsupportedGrants.count - 1){ + [builder appendFormat:@"%@|",unsupportedGrant]; + } + else{ + [builder appendFormat:@"%@)",unsupportedGrant]; + } + } + + NSRegularExpression *unsupportedGrantsExpr = [[NSRegularExpression alloc] initWithPattern:builder options:0 error:nil]; + NSArray *results = [unsupportedGrantsExpr matchesInString:scriptWithoutComment options:0 range:NSMakeRange(0, scriptWithoutComment.length)]; + if (results.count > 0){ + userScript.pass = NO; + NSMutableString *errorMessage = [[NSMutableString alloc] initWithString:userScript.errorMessage ? userScript.errorMessage : @""]; + for (NSString *unsupportedGrant in userScript.unsupportedGrants){ + [errorMessage appendFormat:@"Unsupport grant api %@\n",unsupportedGrant]; + } + userScript.errorMessage = errorMessage; + } + + } + return userScript; } @@ -54,9 +83,10 @@ - (void)conventScriptContent:(UserScript *)userScript{ NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary]; NSString *appVersion = [infoDictionary objectForKey:@"CFBundleShortVersionString"]; - JSValue *gmApisSource = [createGMApisWithUserScript callWithArguments:@[userScript.toDictionary,userScript.uuid,appVersion,scriptWithoutComment]]; + BOOL pageMode = [userScript.grants containsObject:@"unsafeWindow"]; + userScript.installType = pageMode ? @"page" : @"content"; - userScript.installType = [userScript.grants containsObject:@"unsafeWindow"] ? @"page" : @"content"; + JSValue *gmApisSource = [createGMApisWithUserScript callWithArguments:@[userScript.toDictionary,userScript.uuid,appVersion,scriptWithoutComment,userScript.installType]]; if ([userScript.installType isEqualToString:@"page"]){ scriptWithoutComment = [NSString stringWithFormat: @@ -69,12 +99,6 @@ - (void)conventScriptContent:(UserScript *)userScript{ ,gmApisSource,scriptWithoutComment,userScript.uuid]; } - - - -// scriptWithoutComment = [NSString stringWithFormat: -// @"async function gm_init(){\n\t%@\n\t%@\n}\ngm_init();\n" -// ,gmApisSource,scriptWithoutComment]; userScript.parsedContent = scriptWithoutComment; } diff --git a/Stay/tampermonkey/UserScript.h b/Stay/tampermonkey/UserScript.h index af7b10de..3fb5db68 100644 --- a/Stay/tampermonkey/UserScript.h +++ b/Stay/tampermonkey/UserScript.h @@ -24,6 +24,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, copy) NSArray *excludes; @property (nonatomic, copy) NSString *runAt; @property (nonatomic, copy) NSArray *grants; +@property (nonatomic, copy) NSArray *unsupportedGrants; @property (nonatomic, assign) BOOL noFrames; @property (nonatomic, assign) BOOL pass; @property (nonatomic, copy) NSString *errorMessage; @@ -33,7 +34,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, copy) NSString *updateUrl; @property (nonatomic, copy) NSString *downloadUrl; -@property (nonatomic, copy) NSDate *updateTime; +@property (nonatomic, copy) NSString *updateTime; @property (nonatomic, assign) BOOL active; @property (nonatomic, assign) BOOL updateSwitch; @property (nonatomic, copy) NSArray *requireUrls; diff --git a/Stay/tampermonkey/UserScript.m b/Stay/tampermonkey/UserScript.m index 9467ebb5..30214d8c 100644 --- a/Stay/tampermonkey/UserScript.m +++ b/Stay/tampermonkey/UserScript.m @@ -17,13 +17,9 @@ + (instancetype)ofDictionary:(NSDictionary *)dic{ userScript.version = dic[@"version"]; userScript.desc = dic[@"description"]; userScript.homepage = dic[@"homepage"]; - userScript.icon = dic[@"icon"]; + userScript.icon = dic[@"iconUrl"]; userScript.includes = dic[@"includes"]; - NSArray *matches = dic[@"matches"]; - if (matches.count == 0){ - matches = @[@"*://*/*"]; - } - userScript.mathes = matches; + userScript.mathes = dic[@"matches"]; userScript.excludes = dic[@"excludes"]; userScript.runAt = dic[@"runAt"]; userScript.grants = dic[@"grants"]; @@ -37,6 +33,7 @@ + (instancetype)ofDictionary:(NSDictionary *)dic{ userScript.resourceUrls = dic[@"resourceUrls"]; userScript.notes = dic[@"notes"]; userScript.locales = dic[@"locales"]; + userScript.unsupportedGrants = dic[@"unsupportedGrants"]; return userScript; } @@ -65,8 +62,55 @@ - (NSDictionary *)toDictionary{ @"resourceUrls":self.resourceUrls ? self.resourceUrls: @{}, @"notes":self.notes ? self.notes: @[], @"locales":self.locales ? self.locales : @{}, - @"installType":self.installType ? self.installType : @"content" + @"installType":self.installType ? self.installType : @"content", + @"unsupportedGrants":self.unsupportedGrants ? self.unsupportedGrants : @[] }; } +- (NSString *)description +{ + NSMutableString *builder = [[NSMutableString alloc] init]; + if (self.name.length > 0){ + [builder appendFormat:@"name: %@\n",self.name]; + } + + if (self.namespace.length > 0){ + [builder appendFormat:@"namespace: %@\n",self.namespace]; + } + + if (self.author.length > 0){ + [builder appendFormat:@"author: %@\n",self.author]; + } + + if (self.version.length > 0){ + [builder appendFormat:@"version: %@\n",self.version]; + } + + if (self.desc.length > 0){ + [builder appendFormat:@"description: %@\n",self.desc]; + } + + if (self.runAt.length > 0){ + [builder appendFormat:@"runAt: %@\n",self.runAt]; + } + + for (NSString *match in self.mathes){ + [builder appendFormat:@"match: %@\n",match]; + } + + for (NSString *include in self.includes){ + [builder appendFormat:@"include: %@\n",include]; + } + + for (NSString *exclude in self.excludes){ + [builder appendFormat:@"exclude: %@\n",exclude]; + } + + for (NSString *grant in self.grants){ + [builder appendFormat:@"grant: %@\n",grant]; + } + + return builder; +} + @end diff --git a/Stay/tampermonkey/lib/gm-api-create.js b/Stay/tampermonkey/lib/gm-api-create.js index b4bcf9d0..dee87987 100644 --- a/Stay/tampermonkey/lib/gm-api-create.js +++ b/Stay/tampermonkey/lib/gm-api-create.js @@ -7,11 +7,15 @@ (function () { - function createGMApisWithUserScript(userscript, uuid, version) { + function createGMApisWithUserScript(userscript, uuid, version, scriptWithoutComment, installType) { let grants = userscript.grants; let source = 'const _uuid = "' + uuid + '";\n\n'; + source += 'const iconUrl = "' + userscript.icon + '";\n\n'; + source += 'const usName = "' + userscript.name + '";\n\n'; source += 'const _version = "' + version + '";\n\n'; - if (grants.includes('unsafeWindow')) { + native.nslog("createGMApisWithUserScripte-- " + installType); + if (grants.includes('unsafeWindow') || installType == 'page') { + native.nslog("page create"); source += 'const _userscript = ' + JSON.stringify(userscript) +';\n'; source += injectJavaScript(userscript, version); // source 为 window.addEventListener(), move to bootstrap @@ -19,7 +23,7 @@ return source; } source += 'let GM = {};\n\n'; - source += 'let GM_info=' + GM_info(userscript, version) + ';\n'; + source += 'let GM_info=' + GM_info(userscript, version) + '\n'; source += 'GM.info = GM_info;\n'; source += 'let __stroge = await _fillStroge();\n\n'; source += 'let __resourceTextStroge = await _fillAllResourceTextStroge();\n\n'; @@ -38,111 +42,120 @@ source += '});\n\n'; if (grants.includes('GM_listValues')) { - source += 'function GM_listValues (){ return __stroge};\n\n'; + source += 'function GM_listValues (){ return __stroge}\n\n'; } if (grants.includes('GM.listValues')) { - source += 'GM.listValues = ' + _fillStroge.toString() + ';\n\n'; + source += 'GM.listValues = ' + _fillStroge.toString() + '\n\n'; } if (grants.includes('GM_deleteValue')) { - source += GM_deleteValue.toString() + ';\n\n'; + source += GM_deleteValue.toString() + '\n\n'; } if (grants.includes('GM.deleteValue')) { - source += 'GM.deleteValue = ' + deleteValue_p.toString() + ';\n\n'; + source += 'GM.deleteValue = ' + deleteValue_p.toString() + '\n\n'; } if (grants.includes('GM_setValue')) { - source += GM_setValue.toString() + ';\n\n'; + source += GM_setValue.toString() + '\n\n'; } if (grants.includes('GM.setValue')) { - source += 'GM.setValue = ' + setValue_p.toString() + ';\n\n'; + source += 'GM.setValue = ' + setValue_p.toString() + '\n\n'; } if (grants.includes('GM_getValue')) { - source += GM_getValue.toString() + ';\n\n'; + source += GM_getValue.toString() + '\n\n'; } if (grants.includes('GM.getValue')) { - source += 'GM.getValue = ' + getValue_p.toString() + ';\n\n'; + source += 'GM.getValue = ' + getValue_p.toString() + '\n\n'; } if (grants.includes('GM.registerMenuCommand')) { - source += 'GM.registerMenuCommand = ' + GM_registerMenuCommand.toString() + ';\n\n'; + source += 'GM.registerMenuCommand = ' + GM_registerMenuCommand.toString() + '\n\n'; } if (grants.includes('GM_registerMenuCommand')) { - source += GM_registerMenuCommand.toString() + ';\n\n'; + source += GM_registerMenuCommand.toString() + '\n\n'; } if (grants.includes('GM.unregisterMenuCommand')) { - source += 'GM.unregisterMenuCommand = ' + GM_unregisterMenuCommand.toString() + ';\n\n'; + source += 'GM.unregisterMenuCommand = ' + GM_unregisterMenuCommand.toString() + '\n\n'; } if (grants.includes('GM_unregisterMenuCommand')) { - source += GM_unregisterMenuCommand.toString() + ';\n\n'; + source += GM_unregisterMenuCommand.toString() + '\n\n'; } if (grants.includes('GM_addStyle')) { - source += GM_addStyle.toString() + ';\n\n'; + source += GM_addStyle.toString() + '\n\n'; } if (grants.includes('GM.addStyle')) { - source += 'GM.addStyle = ' + GM_addStyle.toString() + ';\n\n'; + source += 'GM.addStyle = ' + GM_addStyle.toString() + '\n\n'; } if (grants.includes('GM_openInTab')) { - source += GM_openInTab.toString() + ';\n\n'; + source += GM_openInTab.toString() + '\n\n'; } if (grants.includes('GM.openInTab')) { - source += 'GM.openInTab = ' + GM_openInTab.toString() + ';\n\n'; + source += 'GM.openInTab = ' + GM_openInTab.toString() + '\n\n'; } if (grants.includes('GM_getResourceURL')) { - source += GM_getResourceURL.toString() + '; \n\n'; + source += GM_getResourceURL.toString() + '\n\n'; } if (grants.includes('GM_getResourceUrl')) { - source += 'GM_getResourceUrl =' + GM_getResourceURL.toString() + '; \n\n'; + source += 'GM_getResourceUrl =' + GM_getResourceURL.toString() + '\n\n'; } if (grants.includes('GM.getResourceURL') || grants.includes('GM.getResourceUrl')) { - source += 'GM.getResourceURL = ' + getResourceURL_p.toString() + '; \n\n'; - source += 'GM.getResourceUrl = ' + getResourceURL_p.toString() + '; \n\n'; + source += 'GM.getResourceURL = ' + getResourceURL_p.toString() + '\n\n'; + source += 'GM.getResourceUrl = ' + getResourceURL_p.toString() + '\n\n'; } if (grants.includes('GM.getResourceText')) { - source += 'GM.getResourceText = ' + getResourceText_p.toString() + '; \n\n'; + source += 'GM.getResourceText = ' + getResourceText_p.toString() + '\n\n'; } if (grants.includes('GM_getResourceText')) { - source += GM_getResourceText.toString() + '; \n\n'; + source += GM_getResourceText.toString() + '\n\n'; } if (grants.includes('GM_xmlhttpRequest')) { - source += GM_xmlhttpRequest.toString() + ';\n\n'; + source += GM_xmlhttpRequest.toString() + '\n\n'; } if (grants.includes('GM.xmlHttpRequest')) { - source += 'GM.xmlHttpRequest = ' + GM_xmlhttpRequest.toString() + ';\n\n'; + source += 'GM.xmlHttpRequest = ' + GM_xmlhttpRequest.toString() + '\n\n'; } if (grants.includes('GM_notification') || grants.includes('GM.notification') ) { - source += GM_notification.toString() + ';\n\n'; + source += GM_notification.toString() + '\n\n'; + source += "GM.notification = " + GM_notification.toString() + '\n\n'; + } + if (grants.includes('GM_download') || grants.includes('GM.download')) { + source += GM_download.toString() + '\n\n'; + source += "GM.download = " + GM_download.toString() + '\n\n'; + } + if (grants.includes('GM_setClipboard') || grants.includes('GM.setClipboard')) { + source += GM_setClipboard.toString() + '\n\n'; + source += "GM.setClipboard = " + GM_setClipboard.toString() + '\n\n'; } //add GM_log by default - source += GM_log.toString() + ';\n\n'; + source += GM_log.toString() + '\n\n'; // source += injectJavaScript.toString() + ';\n\ninjectJavaScript();\n'; - source += _fillStroge.toString() + ';\n\n'; + source += _fillStroge.toString() + '\n\n'; - source += _fillAllResourceTextStroge.toString() + ';\n\n'; + source += _fillAllResourceTextStroge.toString() + '\n\n'; - source += _fillAllResourceUrlStroge.toString() + ';\n\n'; - native.nslog("native-source" + source); + source += _fillAllResourceUrlStroge.toString() + '\n\n'; +// native.nslog("native-source" + source); return source; } @@ -237,11 +250,156 @@ }); } - function GM_notification(params) { - // todo + + function GM_setClipboard(data, info) { } + function GM_download(url, name) { + let downloadStyle = "width: 270px;"; + if (is_iPad()) { + downloadStyle = "width: 320px;" + } + let bg = "background: #fff;"; + let fontColor = "color: #000000;" + if (is_dark()) { + bg = "background: #000;"; + fontColor = "color: #F3F3F3;" + } + let title = "You have source to download..." + let text = 'Allow to download "' + name+ '"'; + let popToastTemp = [ + '
', + '
' + usName+'
', + '
' + text +'
', + '
' + url + '
', + '
', + '
Cancel
', + 'Allow', + '
', + '
' + ]; + + let temp = popToastTemp.join(""); + let tempDom = document.createElement("div"); + tempDom.id = "downloadContainer" + tempDom.innerHTML = temp; + document.body.appendChild(tempDom); + + let downloadCancelDom = document.getElementById("gm_downloadCancel"); + downloadCancelDom.addEventListener("click", function (e) { + tempDom.remove(); + }) + + let downloadLinkDom = document.getElementById("downloadLink"); + downloadLinkDom.href = "data:application/octet-stream," + encodeURIComponent(url); + downloadLinkDom.download = name; + downloadLinkDom.addEventListener("click", function (e) { + tempDom.remove(); + }) + + function is_dark() { + return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; + } + + function is_iPad() { + var ua = navigator.userAgent.toLowerCase(); + if (ua.match(/iPad/i) == "ipad") { + return true; + } else { + return false; + } + } + + } + + /** + * 1、text, title, image, onclick + * 2、details, ondone + * @param {*} param1 + * @param {*} param2 + * @param {string} param3 + * @param {function} param4 + */ + function GM_notification(param1, param2, param3, param4) { + let details = {}; + let ondone = null; + if (typeof param1 === "object" || typeof param2 === "function") { + details = param1; + ondone = param2; + } else { + let detail = { text: param1, title: param2, image: param3, onclick: param4 } + details = detail; + } + + let text = details.text ? details.text : ""; + let title = details.title ? details.title : ""; + let image = details.image ? details.image : ""; + let timeout = details.timeout || 8000; + let onclick = details.onclick; + + // let stayImg = browser.runtime.getURL("images/icon-256.png"); + let notificationStyle = "width: 270px;height: 57px;transform: translateX(-50%);left: 50%;"; + if (is_iPad()) { + notificationStyle = "width: 320px;height: 72px; right: 10px;" + } + let bg = "background: #fff;"; + let fontColor = "color: #000000;" + if (is_dark()) { + bg = "background: #000;"; + fontColor = "color: #F3F3F3;" + } + let popToastTemp = [ + '
', + '
', + '
', + '
' + title + '
', + '
' + text + '
', + '
', + '
' + ]; + let temp = popToastTemp.join(""); + let tempDom = document.createElement("div"); + tempDom.id = "notificationContainer" + tempDom.innerHTML = temp; + document.body.appendChild(tempDom); + let notificationDom = document.getElementById("notificationContainer"); + notificationDom.addEventListener("click", () => { + if (onclick) { + onclick(); + } + }) + var clearFlag = 0; + function autoClose() { + if (timeout > 0) { + timeout = timeout - 500; + } else { + window.clearInterval(clearFlag); + // notificationDom.removeEventListener("click"); + notificationDom.remove(); + if (ondone) { + ondone(); + } + } + } + clearFlag = window.setInterval(() => { + autoClose(); + }, 500); + + function is_dark() { + return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; + } + + function is_iPad() { + var ua = navigator.userAgent.toLowerCase(); + if (ua.match(/iPad/i) == "ipad") { + return true; + } else { + return false; + } + } + } + function GM_registerMenuCommand(caption, commandFunc, accessKey) { const pid = Math.random().toString(36).substring(1, 9); let userInfo = {}; @@ -401,8 +559,8 @@ api += 'let __RMC_CONTEXT = [];\n'; api += 'let GM_info =' + GM_info(userscript, version) + ';\n'; api += `${GM_log}\n`; - api += `${clear_GM_log}\nclear_GM_log();`; - + api += `${clear_GM_log}\nclear_GM_log();\n`; + api += `${__xhr}\n` gmFunVals.push("info: GM_info"); grants.forEach(grant => { @@ -480,7 +638,7 @@ gmFunVals.push("closeTab: GM_closeTab"); } else if ("GM_openInTab" === grant) { - api += `${GM_openInTab}\n;`; + api += `${GM_openInTab}\n`; } else if ("GM.closeTab" === grant || "GM_closeTab" === grant) { api += `${GM_closeTab}\n`; @@ -490,13 +648,19 @@ api += `${GM_notification}\n`; gmFunVals.push("notification: GM_notification"); } - else if (grant === "GM_xmlhttpRequest" || grant === "GM.xmlHttpRequest") { - api += `${xhr}\n`; - if (grant === "GM_xmlhttpRequest") { - api += "\nconst GM_xmlhttpRequest = xhr;\n"; - } else if (grant === "GM.xmlHttpRequest") { - gmFunVals.push("xmlHttpRequest: xhr"); - } + else if ("GM.setClipboard" === grant || "GM_setClipboard" === grant) { + api += `${GM_setClipboard}\n`; + gmFunVals.push("setClipboard: GM_setClipboard"); + } + else if ("GM.download" === grant || "GM_download" === grant) { + api += `${GM_download}\n`; + gmFunVals.push("download: GM_download"); + } + else if (grant === "GM_xmlhttpRequest"){ + api += "\nconst GM_xmlhttpRequest = __xhr;\n"; + } + else if (grant === "GM.xmlHttpRequest") { + gmFunVals.push("xmlHttpRequest: __xhr"); } }) @@ -821,7 +985,7 @@ } - function xhr(details) { + function __xhr(details) { // if details didn't include url, do nothing if (!details.url) return; // create unique id for the xhr diff --git a/Stay/tampermonkey/lib/parse-user-script.js b/Stay/tampermonkey/lib/parse-user-script.js index 8e54f2a2..c2655727 100644 --- a/Stay/tampermonkey/lib/parse-user-script.js +++ b/Stay/tampermonkey/lib/parse-user-script.js @@ -70,6 +70,7 @@ window.parseUserScript = function(content, url, failWhenMissing=false) { 'updateUrl': '', 'excludes': [], 'grants': [], + 'unsupportedGrants': [], 'homePageUrl': '', 'author': 'Unnamed Author', 'includes': [], @@ -83,7 +84,8 @@ window.parseUserScript = function(content, url, failWhenMissing=false) { 'notes':[], 'runAt': 'end', 'pass':true, - 'errorMessage':'' + 'errorMessage':'', + 'iconUrl':'' }; let meta = extractMeta(content).match(/.+/g); @@ -107,14 +109,14 @@ window.parseUserScript = function(content, url, failWhenMissing=false) { // Ignore invalid/unsupported meta lines. continue; } - native.nslog(data.keyword); +// native.nslog(data.keyword); if (UserScriptUnsupport_TAGS.has(data.keyword)){ details.pass = false; details.errorMessage += "Unsupport tag: "+data.keyword+"\n"; continue; } - native.nslog(data.keyword); +// native.nslog(data.keyword); switch (data.keyword) { case 'noframes': details.noFrames = true; @@ -147,15 +149,14 @@ window.parseUserScript = function(content, url, failWhenMissing=false) { details.grants.push(data.value); } else{ - details.pass = false; - details.errorMessage += 'Unsupport GM api '+data.value+'\n'; +// details.pass = false; +// details.errorMessage += 'Unsupport GM api '+data.value+'\n'; + details.unsupportedGrants.push(data.value); } break; case 'description': case 'name': let locale = data.locale; - native.nslog("locale"); - native.nslog(locale); if (locale) { if (!details.locales[locale]) details.locales[locale] = {}; details.locales[locale][data.keyword] = data.value; @@ -173,26 +174,18 @@ window.parseUserScript = function(content, url, failWhenMissing=false) { details.notes.push(data.value); break; case 'match': - try { - new window.MatchPattern(data.value); - details.matches.push(data.value); - } catch (e) { - details.errorMessage += 'Unsupport match pattern' + data.value; - } + details.matches.push(data.value); +// try { +// new window.MatchPattern(data.value); +// +// } catch (e) { +// details.errorMessage += 'Unsupport match pattern' + data.value; +// } break; case 'icon': - details.iconUrl = safeUrl(data.value, url).toString(); + details.iconUrl = data.value; break; case 'require': - //hard code cuz only support stay:// now. -// if (data.value.startsWith('stay://')){ -// details.requireUrls.push(safeUrl(data.value, url).toString()); -// } -// else{ -// details.pass = false; -// details.errorMessage += 'Unsupport require protocol: '+data.value; -// } - details.requireUrls.push(safeUrl(data.value, url).toString()); break; @@ -209,7 +202,7 @@ window.parseUserScript = function(content, url, failWhenMissing=false) { break; } } - native.nslog(details); +// native.nslog(details); return prepDefaults(details); } diff --git a/Stay/tampermonkey/lib/supported-apis.js b/Stay/tampermonkey/lib/supported-apis.js index c1af8412..10357fb5 100644 --- a/Stay/tampermonkey/lib/supported-apis.js +++ b/Stay/tampermonkey/lib/supported-apis.js @@ -32,7 +32,11 @@ const GM_APIS = new Set([ "GM.info", "window.onurlchange", "GM_notification", - "GM.notification" + "GM.notification", + "GM_setClipboard", + "GM.setClipboard", + "GM_download" ]); + const UserScriptUnsupport_TAGS = new Set(["source","connect"]); const RunAtUnsupport_ATTRS = new Set(["context-menu"]); diff --git a/Stay/zh-Hans.lproj/Localizable.strings b/Stay/zh-Hans.lproj/Localizable.strings index 0f000525..9bf5dc61 100644 --- a/Stay/zh-Hans.lproj/Localizable.strings +++ b/Stay/zh-Hans.lproj/Localizable.strings @@ -11,6 +11,8 @@ settings.rateApp="评价应用"; settings.openSource="开源代码"; settings.joinTelegram="加入Telegram"; settings.enableStay="在Safari浏览器扩展中激活Stay"; +settings.joinQQ="加入QQ群"; +settings.joinTwitter="追踪推特"; settings.section.otherApps="旗下应用"; @@ -19,7 +21,7 @@ settings.section.otherApps.fastclip="FastClip - 复制粘贴增强器"; cancel="取消"; settings.save="保存"; settings.library="资料库"; -settings.search="搜索"; +settings.search="浏览"; settings.more="更多"; settings.author="作者"; settings.version="版本"; @@ -37,9 +39,17 @@ settings.addScript="编写脚本"; settings.addScriptFromUrl="从链接导入"; settings.addScriptFromWeb="从GreasyFork导入"; settings.importFromFile = "从文件导入"; -settings.downloadScript = "正在下载脚本"; +settings.downloadScript = "下载脚本"; settings.close = "关闭"; settings.scriptError = "脚本格式错误"; unsupportedFileFormat = "不支持的文件格式"; ok = "确认"; +settings.update = "更新"; +Error="出错了"; +Loading="加载中"; + +Undo="撤销"; +Redo="重做"; +Clear="清空"; +Clipboard="从剪贴板粘贴"; diff --git a/Stay/zh-Hant.lproj/Localizable.strings b/Stay/zh-Hant.lproj/Localizable.strings new file mode 100644 index 00000000..d946f6a6 --- /dev/null +++ b/Stay/zh-Hant.lproj/Localizable.strings @@ -0,0 +1,55 @@ +/* + Localizable.strings + Stay + + Created by ris on 2021/10/21. + +*/ + +//settings +settings.rateApp="評價應用"; +settings.openSource="開源代碼"; +settings.joinTelegram="加入Telegram"; +settings.enableStay="在Safari瀏覽器擴展中激活Stay"; +settings.joinQQ="加入QQ群"; +settings.joinTwitter="追跡推特"; + +settings.section.otherApps="旗下應用"; + +settings.section.otherApps.fastclip="FastClip - 複製粘貼增強器"; + +cancel="取消"; +settings.save="保存"; +settings.library="資料庫"; +settings.search="瀏覽"; +settings.more="更多"; +settings.author="作者"; +settings.version="版本"; +settings.scriptContent="腳本詳情"; +settings.sourcePage="來源"; +settings.descDetail="描述"; +settings.autoUpdate="自動更新"; +settings.add="新增"; +settings.delete="刪除"; +settings.create="創建"; +settings.newScript="新建腳本"; +settings.notes="版本記錄"; +settings.uploadTips="處理腳本資源"; +settings.addScript="編寫腳本"; +settings.addScriptFromUrl="從鏈接導入"; +settings.addScriptFromWeb="從GreasyFork導入"; +settings.importFromFile = "從文件導入"; +settings.downloadScript = "下載腳本"; +settings.close = "關閉"; +settings.scriptError = "腳本格式錯誤"; + +unsupportedFileFormat = "不支持的文件格式"; +ok = "確認"; +settings.update = "更新"; +Error= "出錯了"; +Loading="加載中"; + +Undo="撤銷"; +Redo="重做"; +Clear="清空"; +Clipboard="從剪貼板粘貼"; diff --git a/Stay/zh-Hant.lproj/Main.html b/Stay/zh-Hant.lproj/Main.html new file mode 100644 index 00000000..28cdd287 --- /dev/null +++ b/Stay/zh-Hant.lproj/Main.html @@ -0,0 +1,15 @@ + + + + + + + + + + + + Stay Icon +

You can turn on Stay’s Safari extension in Settings.

+ +