├── .gitignore ├── CercubePlus.x ├── Makefile ├── README.md ├── Resources ├── Cercube.bundle │ ├── Assets.car │ ├── CADownloadsDatabase.momd │ │ ├── CADownloadObject.mom │ │ └── VersionInfo.plist │ ├── Help.md │ ├── Info.plist │ ├── MRAID.bundle │ │ └── mraid.js │ ├── MoPub.bundle │ │ ├── MPCloseBtn.png │ │ ├── MPCloseBtn@2x.png │ │ ├── MPCloseBtn@3x.png │ │ ├── MPCloseButtonX.png │ │ ├── MPCloseButtonX@2x.png │ │ ├── MPCloseButtonX@3x.png │ │ ├── MPDAAIcon.png │ │ ├── MPDAAIcon@2x.png │ │ ├── MPDAAIcon@3x.png │ │ ├── MPMutedBtn.png │ │ ├── MPMutedBtn@2x.png │ │ ├── MPMutedBtn@3x.png │ │ ├── MPPlayBtn.png │ │ ├── MPPlayBtn@2x.png │ │ ├── MPPlayBtn@3x.png │ │ ├── MPUnmutedBtn.png │ │ ├── MPUnmutedBtn@2x.png │ │ └── MPUnmutedBtn@3x.png │ ├── Policy.md │ ├── Terms.md │ ├── attributions.plist │ ├── base.js │ ├── metadata.plist │ └── video.mp4 ├── Frameworks │ └── Alderis.framework │ │ ├── Alderis │ │ ├── Assets.car │ │ └── Info.plist ├── YouPiP.bundle │ ├── Info.plist │ ├── PiPPlaceholderAsset.mp4 │ ├── yt-pip-overlay.png │ ├── yt-pip-overlay@2x.png │ └── yt-pip-overlay@3x.png └── com.galacticdev.isponsorblock.bundle │ ├── LogoSponsorBlocker128px.png │ ├── PlayerInfoIconSponsorBlocker256px-20@2x.png │ ├── sponsorblockend-20@2x.png │ ├── sponsorblocksettings-20@2x.png │ └── sponsorblockstart-20@2x.png └── Tweaks ├── Cercube.dylib ├── YTUHD.dylib ├── YouPiP.dylib ├── iSponsorBlock.dylib └── libcolorpicker.dylib /.gitignore: -------------------------------------------------------------------------------- 1 | .theos 2 | .DS_Store 3 | packages 4 | Makefile 5 | obj -------------------------------------------------------------------------------- /CercubePlus.x: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | 5 | // NOYTPremium: https://github.com/PoomSmart/NoYTPremium 6 | 7 | %hook YTCommerceEventGroupHandler 8 | - (void)addEventHandlers {} 9 | %end 10 | 11 | %hook YTInterstitialPromoEventGroupHandler 12 | - (void)addEventHandlers {} 13 | %end 14 | 15 | %hook YTIShowFullscreenInterstitialCommand 16 | - (BOOL)shouldThrottleInterstitial { return YES; } 17 | %end 18 | 19 | %hook YTPromoThrottleController 20 | - (BOOL)canShowThrottledPromo { return NO; } 21 | - (BOOL)canShowThrottledPromoWithFrequencyCap:(id)frequencyCap { return NO; } 22 | %end 23 | 24 | %hook YTSurveyController 25 | - (void)showSurveyWithRenderer:(id)arg1 surveyParentResponder:(id)arg2 {} 26 | %end 27 | 28 | 29 | // YTABGoodies: https://poomsmart.github.io/repo/depictions/ytabgoodies.html 30 | // YouAreThere 31 | 32 | %hook YTColdConfig 33 | - (BOOL)enableYouthereCommandsOnIos { 34 | return NO; 35 | } 36 | %end 37 | 38 | %hook YTYouThereController 39 | - (BOOL)shouldShowYouTherePrompt { 40 | return NO; 41 | } 42 | %end 43 | 44 | // YouRememberCaption 45 | 46 | %hook YTColdConfig 47 | - (BOOL)respectDeviceCaptionSetting { 48 | return NO; 49 | } 50 | %end 51 | 52 | // YTNOCheckLocalNetWork 53 | 54 | %hook YTHotConfig 55 | - (BOOL)isPromptForLocalNetworkPermissionsEnabled { 56 | return NO; 57 | } 58 | %end 59 | 60 | // YTSystemAppearance 61 | 62 | %hook YTColdConfig 63 | - (BOOL)shouldUseAppThemeSetting { 64 | return YES; 65 | } 66 | %end 67 | 68 | 69 | // YTClassicVideoQuality: https://poomsmart.github.io/repo/depictions/ytclassicvideoquality.html 70 | 71 | @interface YTVideoQualitySwitchOriginalController : NSObject 72 | - (instancetype)initWithParentResponder:(id)responder; 73 | @end 74 | 75 | %hook YTVideoQualitySwitchControllerFactory 76 | 77 | - (id)videoQualitySwitchControllerWithParentResponder:(id)responder { 78 | Class originalClass = %c(YTVideoQualitySwitchOriginalController); 79 | return originalClass ? [[originalClass alloc] initWithParentResponder:responder] : %orig; 80 | } 81 | %end 82 | 83 | 84 | // YTNoHoverCards 0.0.3: https://github.com/level3tjg/YTNoHoverCards 85 | 86 | @interface YTCollectionViewCell : UICollectionViewCell 87 | @end 88 | 89 | @interface YTSettingsCell : YTCollectionViewCell 90 | @end 91 | 92 | @interface YTSettingsSectionItem : NSObject 93 | @property BOOL hasSwitch; 94 | @property BOOL switchVisible; 95 | @property BOOL on; 96 | @property BOOL (^switchBlock)(YTSettingsCell *, BOOL); 97 | @property int settingItemId; 98 | - (instancetype)initWithTitle:(NSString *)title titleDescription:(NSString *)titleDescription; 99 | @end 100 | 101 | %hook YTSettingsViewController 102 | - (void)setSectionItems:(NSMutableArray *)sectionItems forCategory:(NSInteger)category title:(NSString *)title titleDescription:(NSString *)titleDescription headerHidden:(BOOL)headerHidden { 103 | if (category == 1) { 104 | NSInteger appropriateIdx = [sectionItems indexOfObjectPassingTest:^BOOL(YTSettingsSectionItem *item, NSUInteger idx, BOOL *stop) { 105 | return item.settingItemId == 294; 106 | }]; 107 | if (appropriateIdx != NSNotFound) { 108 | YTSettingsSectionItem *hoverCardItem = [[%c(YTSettingsSectionItem) alloc] initWithTitle:@"Show End screens hover cards" titleDescription:@"Allows creator End screens (thumbnails) to appear at the end of videos"]; 109 | hoverCardItem.hasSwitch = YES; 110 | hoverCardItem.switchVisible = YES; 111 | hoverCardItem.on = [[NSUserDefaults standardUserDefaults] boolForKey:@"hover_cards_enabled"]; 112 | hoverCardItem.switchBlock = ^BOOL (YTSettingsCell *cell, BOOL enabled) { 113 | [[NSUserDefaults standardUserDefaults] setBool:enabled forKey:@"hover_cards_enabled"]; 114 | return YES; 115 | }; 116 | [sectionItems insertObject:hoverCardItem atIndex:appropriateIdx + 1]; 117 | } 118 | } 119 | %orig; 120 | } 121 | %end 122 | 123 | %hook YTCreatorEndscreenView 124 | - (void)setHidden:(BOOL)hidden { 125 | if (![[NSUserDefaults standardUserDefaults] boolForKey:@"hover_cards_enabled"]) 126 | hidden = YES; 127 | %orig; 128 | } 129 | %end 130 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CercubePlus_INJECT_DYLIBS = Tweaks/Cercube.dylib Tweaks/libcolorpicker.dylib Tweaks/iSponsorBlock.dylib Tweaks/YTUHD.dylib Tweaks/YouPiP.dylib 2 | 3 | ARCHS = arm64 4 | MODULES = jailed 5 | FINALPACKAGE = 1 6 | CODESIGN_IPA = 0 7 | 8 | TWEAK_NAME = CercubePlus 9 | DISPLAY_NAME = YouTube 10 | BUNDLE_ID = com.google.ios.youtube 11 | 12 | CercubePlus_FILES = CercubePlus.x 13 | CercubePlus_IPA = /System/Volumes/Data/Volumes/Data_Macintosh/Sideloads/IPAs/YouTube_16.43.2.ipa 14 | ### edit the path to your decrypted YouTube IPA!!! 15 | 16 | include $(THEOS)/makefiles/common.mk 17 | include $(THEOS_MAKE_PATH)/tweak.mk 18 | 19 | 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Cercube with extra features! 2 | 3 | ![cydia-1900000833-iphone-0-1628581139 2](https://user-images.githubusercontent.com/52943116/135557251-f4be8ccf-8f0b-4d19-9fcf-6c9544aba281.PNG) 4 | 5 | 6 | # Table of Contents 7 | 8 | * [Credits](#credits) 9 | * [Features](#features) 10 | * [Known issues](#known-issues) 11 | * [Download (IPA)](#download-ipa) 12 | * [Building (optional)](#building-optional) 13 | 14 | 15 | # Credits 16 | 17 | - [Majd Alfhaily](https://twitter.com/freemanrepo?s=21) for [Cercube](https://apt.alfhaily.me/depiction/FDXO5R). 18 | 19 | - [Galactic-Dev](https://github.com/Galactic-Dev) and [Luewii](https://github.com/Luewii) for [iSponsorBlock](https://github.com/Galactic-Dev/iSponsorBlock). 20 | 21 | - [HASHBANG Productions](https://github.com/hbang) for [Alderis Color Picker](https://github.com/hbang/Alderis) - a dependency of iSponsorBlock. 22 | 23 | - [PoomSmart](https://twitter.com/poomsmart?s=21) - the developer of many tweaks used by CercubePlus, include: [YTABGoodies](https://poomsmart.github.io/repo/depictions/ytabgoodies.html), [NOYTPremium](https://poomsmart.github.io/repo/depictions/noytpremium.html), [YTClassicVideoQuality](https://poomsmart.github.io/repo/depictions/ytclassicvideoquality.html), [YTUHD](https://poomsmart.github.io/repo/depictions/ytuhd.html) and [YouPiP](https://poomsmart.github.io/repo/depictions/youpip.html). 24 | 25 | - [level3tjg](https://twitter.com/level3tjg?s=21) - for [YTNoHoverCards](https://github.com/level3tjg/YTNoHoverCards). 26 | 27 | - [Al4ise](https://github.com/Al4ise) for [Azule](https://github.com/Al4ise/Azule), a tool used to inject jailbreak tweaks into jailed iOS apps. 28 | 29 | - [theos team](https://github.com/theos/theos) for theos & theos-jailed. 30 | 31 | 32 | # Features 33 | 34 | 1. **Cercube:** 35 | 36 | - Block all advertisements & Enable background playback. 37 | 38 | - Set default player quality on WiFi & Celullar. 39 | 40 | - Save videos in high resolution, save videos as audio-only, save public playlists (beta). 41 | 42 | - And more... 43 | 44 | 2. **iSponsorBlock:** Skips annoying sponsor ads inside videos. iSponsorBlock is based on [SponsorBlock engine](https://sponsor.ajay.app/). Basically, this is the iOS version of the SponsorBlock extension. 45 | 46 | 3. **YTABGoodies:** allow you to disable some YouTube A/B testing features. It is a combination of several tweaks, such as: 47 | 48 | - YouAreThere: disable "Video paused. Continue watching?" popup in the YouTube app when you play a long video. 49 | 50 | - YouRememberCaption: make YouTube remember your video caption setting (if not already). 51 | 52 | - YTNoCheckLocalNetwork: block the Local Network permission popup. 53 | 54 | - YTSystemAppearance: sync the YouTube theme (dark/light) with the system theme. 55 | 56 | 4. **NOYTPremium:** remove YouTube Premium upsell alerts. 57 | 58 | 5. **YTClassicVideoQuality:** since YouTube v16.xx, you need one more step to change the video quality. YTClassicVideoQuality brings back the old video quality selector, which is a lot better than the new one. 59 | 60 | 6. **YTNoHoverCards:** offer an option to enable/disable the annoying suggested videos show up at the end of the videos. 61 | 62 | 7. **YTUHD:** unlock VP9 codec and in effect, enables video quality of 2K and 4K. You can enable/disable YTUHD in YouTube Settings => Video quality preferences. 63 | 64 | 8. **YouPiP:** enable YouTube's **native PiP**. More options are in YouTube Settings => General. 65 | 66 | 67 | # Known issues 68 | 69 | 1. **Cercube**: Hide Cast button is not working. 70 | 71 | 2. **iSponsorBlock**: the modified time doesn't show up in the seek bar. 72 | 73 | 3. **YTUHD**: Stuttering on 4K videos (sometimes). 74 | 75 | 4. **YouPiP** (iOS 14.0 - 14.4.2): due to Apple's fault, you may encounter the *speedup-bug* as described [here](https://drive.google.com/file/d/1NKdv1fr_KRWgD8nhkMDfG2eLBnbdeVtX/view?usp=sharing). The bug also happens when you try to play multi-sources of sound at the same time. Enable **LegacyPiP** is a workaround. Keep in mind that LegacyPiP also removes UHD quality and breaks YouTube Autoplay next. Use it at your own risk. 76 | 77 | 5. **Not a bug**: 78 | 79 | - The app won't be able to receive push notifications if you use a free developer account to sideload it. 80 | 81 | - It's impossible to fix deep-link (a.k.a Open in the YouTube app). However, you can use this [Shortcuts](https://shortcutsgallery.com/shortcuts/open-in-youtube/) as a workaround (tested on iOS 14). **Credit:** RandomAccessMemories#5025 82 | 83 | 84 | # Download (IPA) 85 | 86 | - **CercubePlus** (or you can call it Cercube++) requires iOS & iPadOS 13.0 and later. The latest version of **CercubePlus** can be found in the [Release tab](https://github.com/qnblackcat/CercubePlus/releases). 87 | 88 | - Version info: 89 | 90 | | **Tweaks/App** | **Version** | **Open source** | 91 | | :------------: | :----------:| :-------------: | 92 | | **YouTube** | 16.42.3 | ✖︎ | 93 | | **Cercube** | 5.3.3 | ✖︎ | 94 | | **iSponsorBlock** | 1.0-10 | [✔︎](https://github.com/Galactic-Dev/iSponsorBlock) | 95 | | **Alderis Color Picker** | 1.1.2| [✔︎](https://github.com/hbang/Alderis) | 96 | | **YTABGoodies** | 1.0 | [✔︎](https://poomsmart.github.io/repo/depictions/ytabgoodies.html) | 97 | | **NOYTPremium** | 1.0.2 | [✔︎](https://github.com/PoomSmart/NoYTPremium) | 98 | | **YTClassicVideoQuality** | 1.0.1 | [✔︎](https://github.com/PoomSmart/YTClassicVideoQuality) | 99 | | **YTNoHoverCards** | 0.0.3 | [✔︎](https://github.com/level3tjg/YTNoHoverCards) | 100 | | **YTUHD** | 1.2.5 | [✔︎](https://github.com/PoomSmart/YTUHD) | 101 | | **YouPiP** | 1.5.14 | [✔︎](https://github.com/PoomSmart/YouPiP) | 102 | 103 | 104 | # Building (optional) 105 | 106 | See [Building - Wiki](https://github.com/qnblackcat/CercubePlus/wiki/Building) 107 | -------------------------------------------------------------------------------- /Resources/Cercube.bundle/Assets.car: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/Cercube.bundle/Assets.car -------------------------------------------------------------------------------- /Resources/Cercube.bundle/CADownloadsDatabase.momd/CADownloadObject.mom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/Cercube.bundle/CADownloadsDatabase.momd/CADownloadObject.mom -------------------------------------------------------------------------------- /Resources/Cercube.bundle/CADownloadsDatabase.momd/VersionInfo.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/Cercube.bundle/CADownloadsDatabase.momd/VersionInfo.plist -------------------------------------------------------------------------------- /Resources/Cercube.bundle/Help.md: -------------------------------------------------------------------------------- 1 | ##### Slow Downloads 2 | 3 | When saving videos via Cercube, the download engine always fetches the videos from their original source. YouTube started throttling speeds in 2018 for specific formats. 4 | 5 | If you find out that a video is taking too long to download, please try a different quality or format. 6 | 7 | 8 | 9 | ##### Support 10 | 11 | You can always send an email to [support@cercube.com](mailto:support@cercube.com) in case you have further questions. 12 | 13 | -------------------------------------------------------------------------------- /Resources/Cercube.bundle/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/Cercube.bundle/Info.plist -------------------------------------------------------------------------------- /Resources/Cercube.bundle/MRAID.bundle/mraid.js: -------------------------------------------------------------------------------- 1 | /* 2 | Do not modify this version of the file. It will be copied over when any of the project's targets are built. 3 | If you wish to modify mraid.js, modify the version located at mopub-sdk-common/mraid/mraid.js. 4 | */ 5 | (function() { 6 | var isIOS = (/iphone|ipad|ipod/i).test(window.navigator.userAgent.toLowerCase()); 7 | if (isIOS) { 8 | console = {}; 9 | console.log = function(log) { 10 | var iframe = document.createElement('iframe'); 11 | iframe.setAttribute('src', 'ios-log: ' + log); 12 | document.documentElement.appendChild(iframe); 13 | iframe.parentNode.removeChild(iframe); 14 | iframe = null; 15 | }; 16 | console.debug = console.info = console.warn = console.error = console.log; 17 | } 18 | }()); 19 | 20 | 21 | (function() { 22 | var mraid = window.mraid = {}; 23 | 24 | ////////////////////////////////////////////////////////////////////////////////////////////////// 25 | 26 | // Bridge interface to SDK 27 | 28 | var bridge = window.mraidbridge = { 29 | nativeSDKFiredReady: false, 30 | nativeCallQueue: [], 31 | nativeCallInFlight: false, 32 | lastSizeChangeProperties: null 33 | }; 34 | 35 | bridge.fireChangeEvent = function(properties) { 36 | for (var p in properties) { 37 | if (properties.hasOwnProperty(p)) { 38 | // Change handlers defined by MRAID below 39 | var handler = changeHandlers[p]; 40 | handler(properties[p]); 41 | } 42 | } 43 | }; 44 | 45 | bridge.nativeCallComplete = function(command) { 46 | if (this.nativeCallQueue.length === 0) { 47 | this.nativeCallInFlight = false; 48 | return; 49 | } 50 | 51 | var nextCall = this.nativeCallQueue.pop(); 52 | window.location = nextCall; 53 | }; 54 | 55 | bridge.executeNativeCall = function(args) { 56 | var command = args.shift(); 57 | 58 | if (!this.nativeSDKFiredReady) { 59 | console.log('rejecting ' + command + ' because mraid is not ready'); 60 | bridge.notifyErrorEvent('mraid is not ready', command); 61 | return; 62 | } 63 | 64 | var call = 'mraid://' + command; 65 | 66 | var key, value; 67 | var isFirstArgument = true; 68 | 69 | for (var i = 0; i < args.length; i += 2) { 70 | key = args[i]; 71 | value = args[i + 1]; 72 | 73 | if (value === null) continue; 74 | 75 | if (isFirstArgument) { 76 | call += '?'; 77 | isFirstArgument = false; 78 | } else { 79 | call += '&'; 80 | } 81 | 82 | call += encodeURIComponent(key) + '=' + encodeURIComponent(value); 83 | } 84 | 85 | if (this.nativeCallInFlight) { 86 | this.nativeCallQueue.push(call); 87 | } else { 88 | this.nativeCallInFlight = true; 89 | window.location = call; 90 | } 91 | }; 92 | 93 | 94 | bridge.setCurrentPosition = function(x, y, width, height) { 95 | currentPosition = { 96 | x: x, 97 | y: y, 98 | width: width, 99 | height: height 100 | }; 101 | broadcastEvent(EVENTS.INFO, 'Set current position to ' + stringify(currentPosition)); 102 | }; 103 | 104 | bridge.setDefaultPosition = function(x, y, width, height) { 105 | defaultPosition = { 106 | x: x, 107 | y: y, 108 | width: width, 109 | height: height 110 | }; 111 | broadcastEvent(EVENTS.INFO, 'Set default position to ' + stringify(defaultPosition)); 112 | }; 113 | 114 | bridge.setMaxSize = function(width, height) { 115 | maxSize = { 116 | width: width, 117 | height: height 118 | }; 119 | 120 | expandProperties.width = width; 121 | expandProperties.height = height; 122 | 123 | broadcastEvent(EVENTS.INFO, 'Set max size to ' + stringify(maxSize)); 124 | }; 125 | 126 | bridge.setPlacementType = function(_placementType) { 127 | placementType = _placementType; 128 | broadcastEvent(EVENTS.INFO, 'Set placement type to ' + stringify(placementType)); 129 | }; 130 | 131 | bridge.setScreenSize = function(width, height) { 132 | screenSize = { 133 | width: width, 134 | height: height 135 | }; 136 | broadcastEvent(EVENTS.INFO, 'Set screen size to ' + stringify(screenSize)); 137 | }; 138 | 139 | bridge.setState = function(_state) { 140 | state = _state; 141 | broadcastEvent(EVENTS.INFO, 'Set state to ' + stringify(state)); 142 | broadcastEvent(EVENTS.STATECHANGE, state); 143 | }; 144 | 145 | bridge.setIsViewable = function(_isViewable) { 146 | isViewable = _isViewable; 147 | broadcastEvent(EVENTS.INFO, 'Set isViewable to ' + stringify(isViewable)); 148 | broadcastEvent(EVENTS.VIEWABLECHANGE, isViewable); 149 | }; 150 | 151 | bridge.setSupports = function(sms, tel, calendar, storePicture, inlineVideo) { 152 | supportProperties = { 153 | sms: sms, 154 | tel: tel, 155 | calendar: calendar, 156 | storePicture: storePicture, 157 | inlineVideo: inlineVideo 158 | }; 159 | }; 160 | 161 | bridge.notifyReadyEvent = function() { 162 | this.nativeSDKFiredReady = true; 163 | broadcastEvent(EVENTS.READY); 164 | }; 165 | 166 | bridge.notifyErrorEvent = function(message, action) { 167 | broadcastEvent(EVENTS.ERROR, message, action); 168 | }; 169 | 170 | // Temporary aliases while we migrate to the new API 171 | bridge.fireReadyEvent = bridge.notifyReadyEvent; 172 | bridge.fireErrorEvent = bridge.notifyErrorEvent; 173 | 174 | bridge.notifySizeChangeEvent = function(width, height) { 175 | if (this.lastSizeChangeProperties && 176 | width == this.lastSizeChangeProperties.width && height == this.lastSizeChangeProperties.height) { 177 | return; 178 | } 179 | 180 | this.lastSizeChangeProperties = { 181 | width: width, 182 | height: height 183 | }; 184 | broadcastEvent(EVENTS.SIZECHANGE, width, height); 185 | }; 186 | 187 | bridge.notifyStateChangeEvent = function() { 188 | if (state === STATES.LOADING) { 189 | broadcastEvent(EVENTS.INFO, 'Native SDK initialized.'); 190 | } 191 | 192 | broadcastEvent(EVENTS.INFO, 'Set state to ' + stringify(state)); 193 | broadcastEvent(EVENTS.STATECHANGE, state); 194 | }; 195 | 196 | bridge.notifyViewableChangeEvent = function() { 197 | broadcastEvent(EVENTS.INFO, 'Set isViewable to ' + stringify(isViewable)); 198 | broadcastEvent(EVENTS.VIEWABLECHANGE, isViewable); 199 | }; 200 | 201 | 202 | // Constants. //////////////////////////////////////////////////////////////////////////////////// 203 | 204 | var VERSION = mraid.VERSION = '2.0'; 205 | 206 | var STATES = mraid.STATES = { 207 | LOADING: 'loading', 208 | DEFAULT: 'default', 209 | EXPANDED: 'expanded', 210 | HIDDEN: 'hidden', 211 | RESIZED: 'resized' 212 | }; 213 | 214 | var EVENTS = mraid.EVENTS = { 215 | ERROR: 'error', 216 | INFO: 'info', 217 | READY: 'ready', 218 | STATECHANGE: 'stateChange', 219 | VIEWABLECHANGE: 'viewableChange', 220 | SIZECHANGE: 'sizeChange' 221 | }; 222 | 223 | var PLACEMENT_TYPES = mraid.PLACEMENT_TYPES = { 224 | UNKNOWN: 'unknown', 225 | INLINE: 'inline', 226 | INTERSTITIAL: 'interstitial' 227 | }; 228 | 229 | // External MRAID state: may be directly or indirectly modified by the ad JS. //////////////////// 230 | 231 | // Properties which define the behavior of an expandable ad. 232 | var expandProperties = { 233 | width: false, 234 | height: false, 235 | useCustomClose: false, 236 | isModal: true 237 | }; 238 | 239 | var resizeProperties = { 240 | width: false, 241 | height: false, 242 | offsetX: false, 243 | offsetY: false, 244 | customClosePosition: 'top-right', 245 | allowOffscreen: true 246 | }; 247 | 248 | var orientationProperties = { 249 | allowOrientationChange: true, 250 | forceOrientation: "none" 251 | }; 252 | 253 | var supportProperties = { 254 | sms: false, 255 | tel: false, 256 | calendar: false, 257 | storePicture: false, 258 | inlineVideo: false 259 | }; 260 | 261 | // default is undefined so that notifySizeChangeEvent can track changes 262 | var lastSizeChangeProperties; 263 | 264 | var maxSize = {}; 265 | 266 | var currentPosition = {}; 267 | 268 | var defaultPosition = {}; 269 | 270 | var screenSize = {}; 271 | 272 | var hasSetCustomClose = false; 273 | 274 | var listeners = {}; 275 | 276 | // Internal MRAID state. Modified by the native SDK. ///////////////////////////////////////////// 277 | 278 | var state = STATES.LOADING; 279 | 280 | var isViewable = false; 281 | 282 | var placementType = PLACEMENT_TYPES.UNKNOWN; 283 | 284 | var hostSDKVersion = { 285 | 'major': 0, 286 | 'minor': 0, 287 | 'patch': 0 288 | }; 289 | 290 | ////////////////////////////////////////////////////////////////////////////////////////////////// 291 | 292 | var EventListeners = function(event) { 293 | this.event = event; 294 | this.count = 0; 295 | var listeners = {}; 296 | 297 | this.add = function(func) { 298 | var id = String(func); 299 | if (!listeners[id]) { 300 | listeners[id] = func; 301 | this.count++; 302 | } 303 | }; 304 | 305 | this.remove = function(func) { 306 | var id = String(func); 307 | if (listeners[id]) { 308 | listeners[id] = null; 309 | delete listeners[id]; 310 | this.count--; 311 | return true; 312 | } else { 313 | return false; 314 | } 315 | }; 316 | 317 | this.removeAll = function() { 318 | for (var id in listeners) { 319 | if (listeners.hasOwnProperty(id)) this.remove(listeners[id]); 320 | } 321 | }; 322 | 323 | this.broadcast = function(args) { 324 | for (var id in listeners) { 325 | if (listeners.hasOwnProperty(id)) listeners[id].apply(mraid, args); 326 | } 327 | }; 328 | 329 | this.toString = function() { 330 | var out = [event, ':']; 331 | for (var id in listeners) { 332 | if (listeners.hasOwnProperty(id)) out.push('|', id, '|'); 333 | } 334 | return out.join(''); 335 | }; 336 | }; 337 | 338 | var broadcastEvent = function() { 339 | var args = new Array(arguments.length); 340 | var l = arguments.length; 341 | for (var i = 0; i < l; i++) args[i] = arguments[i]; 342 | var event = args.shift(); 343 | if (listeners[event]) listeners[event].broadcast(args); 344 | }; 345 | 346 | var contains = function(value, array) { 347 | for (var i in array) { 348 | if (array[i] === value) return true; 349 | } 350 | return false; 351 | }; 352 | 353 | var clone = function(obj) { 354 | if (obj === null) return null; 355 | var f = function() {}; 356 | f.prototype = obj; 357 | return new f(); 358 | }; 359 | 360 | var stringify = function(obj) { 361 | if (typeof obj === 'object') { 362 | var out = []; 363 | if (obj.push) { 364 | // Array. 365 | for (var p in obj) out.push(obj[p]); 366 | return '[' + out.join(',') + ']'; 367 | } else { 368 | // Other object. 369 | for (var p in obj) out.push("'" + p + "': " + obj[p]); 370 | return '{' + out.join(',') + '}'; 371 | } 372 | } else return String(obj); 373 | }; 374 | 375 | var trim = function(str) { 376 | return str.replace(/^\s+|\s+$/g, ''); 377 | }; 378 | 379 | // Functions that will be invoked by the native SDK whenever a "change" event occurs. 380 | var changeHandlers = { 381 | state: function(val) { 382 | if (state === STATES.LOADING) { 383 | broadcastEvent(EVENTS.INFO, 'Native SDK initialized.'); 384 | } 385 | state = val; 386 | broadcastEvent(EVENTS.INFO, 'Set state to ' + stringify(val)); 387 | broadcastEvent(EVENTS.STATECHANGE, state); 388 | }, 389 | 390 | viewable: function(val) { 391 | isViewable = val; 392 | broadcastEvent(EVENTS.INFO, 'Set isViewable to ' + stringify(val)); 393 | broadcastEvent(EVENTS.VIEWABLECHANGE, isViewable); 394 | }, 395 | 396 | placementType: function(val) { 397 | broadcastEvent(EVENTS.INFO, 'Set placementType to ' + stringify(val)); 398 | placementType = val; 399 | }, 400 | 401 | sizeChange: function(val) { 402 | broadcastEvent(EVENTS.INFO, 'Set screenSize to ' + stringify(val)); 403 | for (var key in val) { 404 | if (val.hasOwnProperty(key)) screenSize[key] = val[key]; 405 | } 406 | }, 407 | 408 | supports: function(val) { 409 | broadcastEvent(EVENTS.INFO, 'Set supports to ' + stringify(val)); 410 | supportProperties = val; 411 | }, 412 | 413 | hostSDKVersion: function(val) { 414 | // val is expected to be formatted like 'X.Y.Z[-+]identifier'. 415 | var versions = val.split('.').map(function(version) { 416 | return parseInt(version, 10); 417 | }).filter(function(version) { 418 | return version >= 0; 419 | }); 420 | 421 | if (versions.length >= 3) { 422 | hostSDKVersion['major'] = parseInt(versions[0], 10); 423 | hostSDKVersion['minor'] = parseInt(versions[1], 10); 424 | hostSDKVersion['patch'] = parseInt(versions[2], 10); 425 | broadcastEvent(EVENTS.INFO, 'Set hostSDKVersion to ' + stringify(hostSDKVersion)); 426 | } 427 | } 428 | }; 429 | 430 | var validate = function(obj, validators, action, merge) { 431 | if (!merge) { 432 | // Check to see if any required properties are missing. 433 | if (obj === null) { 434 | broadcastEvent(EVENTS.ERROR, 'Required object not provided.', action); 435 | return false; 436 | } else { 437 | for (var i in validators) { 438 | if (validators.hasOwnProperty(i) && obj[i] === undefined) { 439 | broadcastEvent(EVENTS.ERROR, 'Object is missing required property: ' + i, action); 440 | return false; 441 | } 442 | } 443 | } 444 | } 445 | 446 | for (var prop in obj) { 447 | var validator = validators[prop]; 448 | var value = obj[prop]; 449 | if (validator && !validator(value)) { 450 | // Failed validation. 451 | broadcastEvent(EVENTS.ERROR, 'Value of property ' + prop + ' is invalid: ' + value, action); 452 | return false; 453 | } 454 | } 455 | return true; 456 | }; 457 | 458 | var expandPropertyValidators = { 459 | useCustomClose: function(v) { return (typeof v === 'boolean'); }, 460 | }; 461 | 462 | ////////////////////////////////////////////////////////////////////////////////////////////////// 463 | 464 | mraid.addEventListener = function(event, listener) { 465 | if (!event || !listener) { 466 | broadcastEvent(EVENTS.ERROR, 'Both event and listener are required.', 'addEventListener'); 467 | } else if (!contains(event, EVENTS)) { 468 | broadcastEvent(EVENTS.ERROR, 'Unknown MRAID event: ' + event, 'addEventListener'); 469 | } else { 470 | if (!listeners[event]) { 471 | listeners[event] = new EventListeners(event); 472 | } 473 | listeners[event].add(listener); 474 | } 475 | }; 476 | 477 | mraid.close = function() { 478 | if (state === STATES.HIDDEN) { 479 | broadcastEvent(EVENTS.ERROR, 'Ad cannot be closed when it is already hidden.', 480 | 'close'); 481 | } else bridge.executeNativeCall(['close']); 482 | }; 483 | 484 | mraid.expand = function(URL) { 485 | if (!(this.getState() === STATES.DEFAULT || this.getState() === STATES.RESIZED)) { 486 | broadcastEvent(EVENTS.ERROR, 'Ad can only be expanded from the default or resized state.', 'expand'); 487 | } else { 488 | var args = ['expand', 489 | 'shouldUseCustomClose', expandProperties.useCustomClose 490 | ]; 491 | 492 | if (URL) { 493 | args = args.concat(['url', URL]); 494 | } 495 | 496 | bridge.executeNativeCall(args); 497 | } 498 | }; 499 | 500 | mraid.getExpandProperties = function() { 501 | var properties = { 502 | width: expandProperties.width, 503 | height: expandProperties.height, 504 | useCustomClose: expandProperties.useCustomClose, 505 | isModal: expandProperties.isModal 506 | }; 507 | return properties; 508 | }; 509 | 510 | 511 | mraid.getCurrentPosition = function() { 512 | return { 513 | x: currentPosition.x, 514 | y: currentPosition.y, 515 | width: currentPosition.width, 516 | height: currentPosition.height 517 | }; 518 | }; 519 | 520 | mraid.getDefaultPosition = function() { 521 | return { 522 | x: defaultPosition.x, 523 | y: defaultPosition.y, 524 | width: defaultPosition.width, 525 | height: defaultPosition.height 526 | }; 527 | }; 528 | 529 | mraid.getMaxSize = function() { 530 | return { 531 | width: maxSize.width, 532 | height: maxSize.height 533 | }; 534 | }; 535 | 536 | mraid.getPlacementType = function() { 537 | return placementType; 538 | }; 539 | 540 | mraid.getScreenSize = function() { 541 | return { 542 | width: screenSize.width, 543 | height: screenSize.height 544 | }; 545 | }; 546 | 547 | mraid.getState = function() { 548 | return state; 549 | }; 550 | 551 | mraid.isViewable = function() { 552 | return isViewable; 553 | }; 554 | 555 | mraid.getVersion = function() { 556 | return mraid.VERSION; 557 | }; 558 | 559 | mraid.open = function(URL) { 560 | if (!URL) broadcastEvent(EVENTS.ERROR, 'URL is required.', 'open'); 561 | else bridge.executeNativeCall(['open', 'url', URL]); 562 | }; 563 | 564 | mraid.removeEventListener = function(event, listener) { 565 | if (!event) { 566 | broadcastEvent(EVENTS.ERROR, 'Event is required.', 'removeEventListener'); 567 | return; 568 | } 569 | 570 | if (listener) { 571 | // If we have a valid event, we'll try to remove the listener from it. 572 | var success = false; 573 | if (listeners[event]) { 574 | success = listeners[event].remove(listener); 575 | } 576 | 577 | // If we didn't have a valid event or couldn't remove the listener from the event, broadcast an error and return early. 578 | if (!success) { 579 | broadcastEvent(EVENTS.ERROR, 'Listener not currently registered for event.', 'removeEventListener'); 580 | return; 581 | } 582 | 583 | } else if (!listener && listeners[event]) { 584 | listeners[event].removeAll(); 585 | } 586 | 587 | if (listeners[event] && listeners[event].count === 0) { 588 | listeners[event] = null; 589 | delete listeners[event]; 590 | } 591 | }; 592 | 593 | mraid.setExpandProperties = function(properties) { 594 | if (validate(properties, expandPropertyValidators, 'setExpandProperties', true)) { 595 | if (properties.hasOwnProperty('useCustomClose')) { 596 | expandProperties.useCustomClose = properties.useCustomClose; 597 | } 598 | } 599 | }; 600 | 601 | mraid.useCustomClose = function(shouldUseCustomClose) { 602 | expandProperties.useCustomClose = shouldUseCustomClose; 603 | hasSetCustomClose = true; 604 | bridge.executeNativeCall(['usecustomclose', 'shouldUseCustomClose', shouldUseCustomClose]); 605 | }; 606 | 607 | // MRAID 2.0 APIs //////////////////////////////////////////////////////////////////////////////// 608 | 609 | mraid.createCalendarEvent = function(parameters) { 610 | CalendarEventParser.initialize(parameters); 611 | if (CalendarEventParser.parse()) { 612 | bridge.executeNativeCall(CalendarEventParser.arguments); 613 | } else { 614 | broadcastEvent(EVENTS.ERROR, CalendarEventParser.errors[0], 'createCalendarEvent'); 615 | } 616 | }; 617 | 618 | mraid.supports = function(feature) { 619 | return supportProperties[feature]; 620 | }; 621 | 622 | mraid.playVideo = function(uri) { 623 | if (!mraid.isViewable()) { 624 | broadcastEvent(EVENTS.ERROR, 'playVideo cannot be called until the ad is viewable', 'playVideo'); 625 | return; 626 | } 627 | 628 | if (!uri) { 629 | broadcastEvent(EVENTS.ERROR, 'playVideo must be called with a valid URI', 'playVideo'); 630 | } else { 631 | bridge.executeNativeCall(['playVideo', 'uri', uri]); 632 | } 633 | }; 634 | 635 | mraid.storePicture = function(uri) { 636 | if (!mraid.isViewable()) { 637 | broadcastEvent(EVENTS.ERROR, 'storePicture cannot be called until the ad is viewable', 'storePicture'); 638 | return; 639 | } 640 | 641 | if (!uri) { 642 | broadcastEvent(EVENTS.ERROR, 'storePicture must be called with a valid URI', 'storePicture'); 643 | } else { 644 | bridge.executeNativeCall(['storePicture', 'uri', uri]); 645 | } 646 | }; 647 | 648 | 649 | var resizePropertyValidators = { 650 | width: function(v) { 651 | return !isNaN(v) && v > 0; 652 | }, 653 | height: function(v) { 654 | return !isNaN(v) && v > 0; 655 | }, 656 | offsetX: function(v) { 657 | return !isNaN(v); 658 | }, 659 | offsetY: function(v) { 660 | return !isNaN(v); 661 | }, 662 | customClosePosition: function(v) { 663 | return (typeof v === 'string' && 664 | ['top-right', 'bottom-right', 'top-left', 'bottom-left', 'center', 'top-center', 'bottom-center'].indexOf(v) > -1); 665 | }, 666 | allowOffscreen: function(v) { 667 | return (typeof v === 'boolean'); 668 | } 669 | }; 670 | 671 | mraid.setOrientationProperties = function(properties) { 672 | 673 | if (properties.hasOwnProperty('allowOrientationChange')) { 674 | orientationProperties.allowOrientationChange = properties.allowOrientationChange; 675 | } 676 | 677 | if (properties.hasOwnProperty('forceOrientation')) { 678 | orientationProperties.forceOrientation = properties.forceOrientation; 679 | } 680 | 681 | var args = ['setOrientationProperties', 682 | 'allowOrientationChange', orientationProperties.allowOrientationChange, 683 | 'forceOrientation', orientationProperties.forceOrientation 684 | ]; 685 | bridge.executeNativeCall(args); 686 | }; 687 | 688 | mraid.getOrientationProperties = function() { 689 | return { 690 | allowOrientationChange: orientationProperties.allowOrientationChange, 691 | forceOrientation: orientationProperties.forceOrientation 692 | }; 693 | }; 694 | 695 | mraid.resize = function() { 696 | if (!(this.getState() === STATES.DEFAULT || this.getState() === STATES.RESIZED)) { 697 | broadcastEvent(EVENTS.ERROR, 'Ad can only be resized from the default or resized state.', 'resize'); 698 | } else if (!resizeProperties.width || !resizeProperties.height) { 699 | broadcastEvent(EVENTS.ERROR, 'Must set resize properties before calling resize()', 'resize'); 700 | } else { 701 | var args = ['resize', 702 | 'width', resizeProperties.width, 703 | 'height', resizeProperties.height, 704 | 'offsetX', resizeProperties.offsetX || 0, 705 | 'offsetY', resizeProperties.offsetY || 0, 706 | 'customClosePosition', resizeProperties.customClosePosition, 707 | 'allowOffscreen', !!resizeProperties.allowOffscreen 708 | ]; 709 | 710 | bridge.executeNativeCall(args); 711 | } 712 | }; 713 | 714 | mraid.getResizeProperties = function() { 715 | var properties = { 716 | width: resizeProperties.width, 717 | height: resizeProperties.height, 718 | offsetX: resizeProperties.offsetX, 719 | offsetY: resizeProperties.offsetY, 720 | customClosePosition: resizeProperties.customClosePosition, 721 | allowOffscreen: resizeProperties.allowOffscreen 722 | }; 723 | return properties; 724 | }; 725 | 726 | mraid.setResizeProperties = function(properties) { 727 | if (validate(properties, resizePropertyValidators, 'setResizeProperties', true)) { 728 | 729 | var desiredProperties = ['width', 'height', 'offsetX', 'offsetY', 'customClosePosition', 'allowOffscreen']; 730 | 731 | var length = desiredProperties.length; 732 | 733 | for (var i = 0; i < length; i++) { 734 | var propname = desiredProperties[i]; 735 | if (properties.hasOwnProperty(propname)) { 736 | resizeProperties[propname] = properties[propname]; 737 | } 738 | } 739 | } 740 | }; 741 | 742 | // Determining SDK version /////////////////////////////////////////////////////////////////////// 743 | 744 | mraid.getHostSDKVersion = function() { 745 | return hostSDKVersion; 746 | } 747 | 748 | // Calendar helpers ////////////////////////////////////////////////////////////////////////////// 749 | 750 | var CalendarEventParser = { 751 | initialize: function(parameters) { 752 | this.parameters = parameters; 753 | this.errors = []; 754 | this.arguments = ['createCalendarEvent']; 755 | }, 756 | 757 | parse: function() { 758 | if (!this.parameters) { 759 | this.errors.push('The object passed to createCalendarEvent cannot be null.'); 760 | } else { 761 | this.parseDescription(); 762 | this.parseLocation(); 763 | this.parseSummary(); 764 | this.parseStartAndEndDates(); 765 | this.parseReminder(); 766 | this.parseRecurrence(); 767 | this.parseTransparency(); 768 | } 769 | 770 | var errorCount = this.errors.length; 771 | if (errorCount) { 772 | this.arguments.length = 0; 773 | } 774 | 775 | return (errorCount === 0); 776 | }, 777 | 778 | parseDescription: function() { 779 | this._processStringValue('description'); 780 | }, 781 | 782 | parseLocation: function() { 783 | this._processStringValue('location'); 784 | }, 785 | 786 | parseSummary: function() { 787 | this._processStringValue('summary'); 788 | }, 789 | 790 | parseStartAndEndDates: function() { 791 | this._processDateValue('start'); 792 | this._processDateValue('end'); 793 | }, 794 | 795 | parseReminder: function() { 796 | var reminder = this._getParameter('reminder'); 797 | if (!reminder) { 798 | return; 799 | } 800 | 801 | if (reminder < 0) { 802 | this.arguments.push('relativeReminder'); 803 | this.arguments.push(parseInt(reminder) / 1000); 804 | } else { 805 | this.arguments.push('absoluteReminder'); 806 | this.arguments.push(reminder); 807 | } 808 | }, 809 | 810 | parseRecurrence: function() { 811 | var recurrenceDict = this._getParameter('recurrence'); 812 | if (!recurrenceDict) { 813 | return; 814 | } 815 | 816 | this.parseRecurrenceInterval(recurrenceDict); 817 | this.parseRecurrenceFrequency(recurrenceDict); 818 | this.parseRecurrenceEndDate(recurrenceDict); 819 | this.parseRecurrenceArrayValue(recurrenceDict, 'daysInWeek'); 820 | this.parseRecurrenceArrayValue(recurrenceDict, 'daysInMonth'); 821 | this.parseRecurrenceArrayValue(recurrenceDict, 'daysInYear'); 822 | this.parseRecurrenceArrayValue(recurrenceDict, 'monthsInYear'); 823 | }, 824 | 825 | parseTransparency: function() { 826 | var validValues = ['opaque', 'transparent']; 827 | 828 | if (this.parameters.hasOwnProperty('transparency')) { 829 | var transparency = this.parameters.transparency; 830 | if (contains(transparency, validValues)) { 831 | this.arguments.push('transparency'); 832 | this.arguments.push(transparency); 833 | } else { 834 | this.errors.push('transparency must be opaque or transparent'); 835 | } 836 | } 837 | }, 838 | 839 | parseRecurrenceArrayValue: function(recurrenceDict, kind) { 840 | if (recurrenceDict.hasOwnProperty(kind)) { 841 | var array = recurrenceDict[kind]; 842 | if (!array || !(array instanceof Array)) { 843 | this.errors.push(kind + ' must be an array.'); 844 | } else { 845 | var arrayStr = array.join(','); 846 | this.arguments.push(kind); 847 | this.arguments.push(arrayStr); 848 | } 849 | } 850 | }, 851 | 852 | parseRecurrenceInterval: function(recurrenceDict) { 853 | if (recurrenceDict.hasOwnProperty('interval')) { 854 | var interval = recurrenceDict.interval; 855 | if (!interval) { 856 | this.errors.push('Recurrence interval cannot be null.'); 857 | } else { 858 | this.arguments.push('interval'); 859 | this.arguments.push(interval); 860 | } 861 | } else { 862 | // If a recurrence rule was specified without an interval, use a default value of 1. 863 | this.arguments.push('interval'); 864 | this.arguments.push(1); 865 | } 866 | }, 867 | 868 | parseRecurrenceFrequency: function(recurrenceDict) { 869 | if (recurrenceDict.hasOwnProperty('frequency')) { 870 | var frequency = recurrenceDict.frequency; 871 | var validFrequencies = ['daily', 'weekly', 'monthly', 'yearly']; 872 | if (contains(frequency, validFrequencies)) { 873 | this.arguments.push('frequency'); 874 | this.arguments.push(frequency); 875 | } else { 876 | this.errors.push('Recurrence frequency must be one of: "daily", "weekly", "monthly", "yearly".'); 877 | } 878 | } 879 | }, 880 | 881 | parseRecurrenceEndDate: function(recurrenceDict) { 882 | var expires = recurrenceDict.expires; 883 | 884 | if (!expires) { 885 | return; 886 | } 887 | 888 | this.arguments.push('expires'); 889 | this.arguments.push(expires); 890 | }, 891 | 892 | _getParameter: function(key) { 893 | if (this.parameters.hasOwnProperty(key)) { 894 | return this.parameters[key]; 895 | } 896 | 897 | return null; 898 | }, 899 | 900 | _processStringValue: function(kind) { 901 | if (this.parameters.hasOwnProperty(kind)) { 902 | var value = this.parameters[kind]; 903 | this.arguments.push(kind); 904 | this.arguments.push(value); 905 | } 906 | }, 907 | 908 | _processDateValue: function(kind) { 909 | if (this.parameters.hasOwnProperty(kind)) { 910 | var dateString = this._getParameter(kind); 911 | this.arguments.push(kind); 912 | this.arguments.push(dateString); 913 | } 914 | } 915 | }; 916 | }()); -------------------------------------------------------------------------------- /Resources/Cercube.bundle/MoPub.bundle/MPCloseBtn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/Cercube.bundle/MoPub.bundle/MPCloseBtn.png -------------------------------------------------------------------------------- /Resources/Cercube.bundle/MoPub.bundle/MPCloseBtn@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/Cercube.bundle/MoPub.bundle/MPCloseBtn@2x.png -------------------------------------------------------------------------------- /Resources/Cercube.bundle/MoPub.bundle/MPCloseBtn@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/Cercube.bundle/MoPub.bundle/MPCloseBtn@3x.png -------------------------------------------------------------------------------- /Resources/Cercube.bundle/MoPub.bundle/MPCloseButtonX.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/Cercube.bundle/MoPub.bundle/MPCloseButtonX.png -------------------------------------------------------------------------------- /Resources/Cercube.bundle/MoPub.bundle/MPCloseButtonX@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/Cercube.bundle/MoPub.bundle/MPCloseButtonX@2x.png -------------------------------------------------------------------------------- /Resources/Cercube.bundle/MoPub.bundle/MPCloseButtonX@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/Cercube.bundle/MoPub.bundle/MPCloseButtonX@3x.png -------------------------------------------------------------------------------- /Resources/Cercube.bundle/MoPub.bundle/MPDAAIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/Cercube.bundle/MoPub.bundle/MPDAAIcon.png -------------------------------------------------------------------------------- /Resources/Cercube.bundle/MoPub.bundle/MPDAAIcon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/Cercube.bundle/MoPub.bundle/MPDAAIcon@2x.png -------------------------------------------------------------------------------- /Resources/Cercube.bundle/MoPub.bundle/MPDAAIcon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/Cercube.bundle/MoPub.bundle/MPDAAIcon@3x.png -------------------------------------------------------------------------------- /Resources/Cercube.bundle/MoPub.bundle/MPMutedBtn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/Cercube.bundle/MoPub.bundle/MPMutedBtn.png -------------------------------------------------------------------------------- /Resources/Cercube.bundle/MoPub.bundle/MPMutedBtn@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/Cercube.bundle/MoPub.bundle/MPMutedBtn@2x.png -------------------------------------------------------------------------------- /Resources/Cercube.bundle/MoPub.bundle/MPMutedBtn@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/Cercube.bundle/MoPub.bundle/MPMutedBtn@3x.png -------------------------------------------------------------------------------- /Resources/Cercube.bundle/MoPub.bundle/MPPlayBtn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/Cercube.bundle/MoPub.bundle/MPPlayBtn.png -------------------------------------------------------------------------------- /Resources/Cercube.bundle/MoPub.bundle/MPPlayBtn@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/Cercube.bundle/MoPub.bundle/MPPlayBtn@2x.png -------------------------------------------------------------------------------- /Resources/Cercube.bundle/MoPub.bundle/MPPlayBtn@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/Cercube.bundle/MoPub.bundle/MPPlayBtn@3x.png -------------------------------------------------------------------------------- /Resources/Cercube.bundle/MoPub.bundle/MPUnmutedBtn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/Cercube.bundle/MoPub.bundle/MPUnmutedBtn.png -------------------------------------------------------------------------------- /Resources/Cercube.bundle/MoPub.bundle/MPUnmutedBtn@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/Cercube.bundle/MoPub.bundle/MPUnmutedBtn@2x.png -------------------------------------------------------------------------------- /Resources/Cercube.bundle/MoPub.bundle/MPUnmutedBtn@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/Cercube.bundle/MoPub.bundle/MPUnmutedBtn@3x.png -------------------------------------------------------------------------------- /Resources/Cercube.bundle/Policy.md: -------------------------------------------------------------------------------- 1 | Your privacy is critically important to us. This document describes our privacy practices as well as your choices regarding use, access and correction of personal data. 2 | 3 | ### What do we collect 4 | 5 | We only collect information about you if we have a reason to do so, for example to provide our service or to communicate with you. It can either be information you provide or collected automatically. Let’s have a look: 6 | 7 | ##### Provided 8 | 9 | 1. Basic account information (ex: username, email, password) are required to operate your service and allow you to log in. 10 | 11 | 2. Billing information (ex: country) are only collected at your initiative to provide invoices addressed to you. 12 | 13 | ##### Automatic 14 | 15 | 1. Log Information: like most online service providers, we collect information such as operating system and other meta information that is not identifiable or private. 16 | 17 | 2. Usage Information: we collect information about your usage of our service. For example, we collect information about the screens you visit in our app. We use this information to get insights on how people use our service, so we can make it better. 18 | 19 | 3. Location Information: we may determine the country based on your IP address. We collect and use this information when you make a purchase to calculate the correct tax rates based on your home country. 20 | 21 | 4. Information from Cookies: A cookie is a string of information that a website stores on a visitor’s device, and that the visitor’s device provides to the website each time the visitor returns. We use cookies to authenticate you to your Cercube account. 22 | 23 | *Note: Financial information (ex: credit card number, name, postal code) are not collected by us, they go directly to the payment services provider and only when you make a purchase.* 24 | 25 | ### You're in control 26 | 27 | 1. If you have an account with us, you can choose not to provide the optional account information. Of course if you do this certain features may not be accessible or degraded. 28 | 29 | 2. You can also choose at any time to close your Cercube account, in which case all data (personal or not) associated with your account will be removed from the service database instantly. Some information may still be present in our logs and backups though, and will definitely be gone after up to one month. 30 | 31 | 3. You can access and update most of your personal information from your account directly, but if you have a more specific request (right to portability, to object, etc.) feel free to reach out to support@cercube.com. 32 | 33 | ### Security 34 | 35 | While no online service is 100% secure, we work very hard to protect your information against unauthorized access or alteration, and take reasonable measures to do so, such as keeping software up-to-date, restricting access to internal services and using state-of-the-art authentication and encryption methods. 36 | 37 | ### Other Things You Should Know 38 | 39 | ##### Transferring Information 40 | 41 | Because our service is offered worldwide, the information about you that we process when you use the service in the EU may be used, stored, and/or accessed by individuals operating outside the European Economic Area (EEA) who work for us, or third party data processors. This is required for the purposes listed in the "What do we collect" section above. When providing information about you to entities outside the EEA, we will take appropriate measures to ensure that the recipient protects your personal information adequately in accordance with this Privacy Policy as required by applicable law. 42 | 43 | ##### Analytics service Provided by Others 44 | 45 | Analytics providers may set tracking technologies (like cookies) to collect information about your use of our service and across other websites and online service. These technologies allow these third parties to recognize your device to compile information about you or others who use your device. This information allows us and other companies to, among other things, analyze and track usage, determine the popularity of certain content. Please note this Privacy Policy only covers the collection of information by Cercube and does not cover the collection of information by any third party advertisers or analytics providers. 46 | 47 | ##### List of sub-processors 48 | 49 | Cercube uses the following products/services: 50 | 51 | * Digital Ocean for hosting the servers and storing data. 52 | * Stripe for payments processing. 53 | * Amazon's Simple Email Service (SES) for delivering Email notifications. 54 | 55 | We have partnerships with the following ad networks: 56 | 57 | * MoPub. 58 | 59 | We use the following analytics collection services: 60 | 61 | * Microsoft AppCenter. 62 | 63 | All data that is collected by Cercube is anonymized and cannot identify individual users. 64 | 65 | ### Updates 66 | 67 | Although most changes are likely to be minor, we may change our Privacy Policy from time to time. We encourage you to frequently check this page for any changes. 68 | 69 | ### Changelog 70 | 71 | May 18, 2019: Initial version 72 | 73 | May 29, 2019: Updated list of ad networks. 74 | 75 | April 4, 2020: Updated list of sub-processors. 76 | 77 | November 18, 2020: Updated list of sub-processors. 78 | 79 | August 9, 2021: Updated list of sub-processors. 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /Resources/Cercube.bundle/Terms.md: -------------------------------------------------------------------------------- 1 | ##### TL;DR 2 | 3 | 1. Use Cercube for good, not evil. 4 | 5 | 2. You agree to not violate any laws in your jurisdiction. 6 | 7 | 3. You may not download copyrighted material from YouTube. 8 | 9 | 4. If you have any questions, ask support@cercube.com. 10 | 11 | ##### Full Terms 12 | 13 | By using the Cercube ("Service"), you are agreeing to be bound by the following terms and conditions ("Terms of Service"). If you are entering into this agreement on behalf of a company or other legal entity, you represent that you have the authority to bind such entity to these terms and conditions, in which case the terms "you" or "your" shall refer to such entity. 14 | 15 | ##### Use of Your Cercube Account 16 | 17 | 1. You must use a valid email address for your Cercube account. 18 | 19 | 2. You must not use the Service for any illegal or abusive purposes. 20 | 21 | 3. You are solely responsible for the security of your account. 22 | 23 | 4. Our service shall only be used in accordance with any and all applicable laws and regulations. 24 | 25 | ##### Payment and Billing 26 | 27 | 1. The Cercube Service is a freemium service. A payment can be made in form of a "one-time purchase" or a "subscription" to provide extra functionality and remove advertisements. 28 | 29 | 2. Subscriptions will auto renew based on the provided terms when creating the subscription unless cancelled by the user before the end of the current billing period. 30 | 31 | ##### Refund Policy 32 | 33 | All services rendered by the Cercube are provided on a non-refundable basis. This includes, but it not limited to, one-time fees and subscription fees, regardless of usage. In addition, if your account is canceled by the company for violation of this Agreement, all payments made to Cercube become completely non refundable. Customer agrees not to charge back any credit card payments for services rendered. In the event that a customer files a charge back or other payment dispute, they will be considered to be in violation of this agreement and may be subject to collection action as described above. 34 | 35 | ##### Cancellation and Termination of Service 36 | 37 | 1. You may at any time cancel your running subscription. Your subscription will be active until the next billing period. 38 | 39 | 2. Any abuse of the Service will lead to termination of your account. Cercube reserves the right to decide what is considered abuse of the Service. 40 | 41 | ##### Disclaimer 42 | 43 | 1. The Cercube Service is provided “as is” and “as available”. 44 | 45 | 2. Cercube reserves the right to modify the Service from time to time at any time, including adding and removing features. 46 | 47 | 3. Cercube reserves the right to discontinue the Service at any time after a notice via email. 48 | 49 | 4. Cercube reserves the right to change the prices for the Service and any connected extra services at any time. 50 | 51 | 5. Cercube gives no warranties regarding the correctness of the data collected with the Service or any potential corruption or loss of such data. 52 | 53 | ##### Changes to Terms of Service 54 | 55 | These Terms of Service may be updated from time to time without prior notice. We encourage you to frequently check this page for any changes. 56 | 57 | ##### Limitation of Liability 58 | 59 | 1. You expressly understand and agree that Cercube shall not be liable for any damages resulting from the use of the Cercube service. 60 | 61 | 2. Any decisions or claims you make based on data from the Cercube Service are your sole responsibility. Cercube shall not be held liable for any such decisions or claims. 62 | 63 | 3. In no event shall Cercube's total liability to you for all damages, losses, and causes of action exceed the amount paid by you, if any, for using this Service. 64 | 65 | ##### Indemnification 66 | 67 | 1. You agree to indemnify, defend and hold harmless Cercube and its officers, directors, employees, consultants and agents from any and all third party claims, liability, damages and/or costs arising from your use of the Cercube service, your violation of the terms of use or your infringement, or infringement by any other user of your account, of any intellectual property or other right of any person or entity. 68 | 69 | 2. You agree to immediately notify Cercube of any unauthorized use of your account or any other breach of security known to you. 70 | 71 | ##### Other 72 | 73 | If any part of this Terms of Service would be determined by any competent authority to be invalid, unlawful or unenforceable, the remainder of the Terms of Service shall continue to be valid and enforceable to the fullest extent permitted by law. 74 | 75 | ##### Applicable law 76 | 77 | This contract shall be governed by the law of Germany. Any dispute, controversy or claim arising out of or in connection with this contract, or the breach, termination or invalidity thereof, shall be solved according to jurisdiction laws in Germany. 78 | -------------------------------------------------------------------------------- /Resources/Cercube.bundle/attributions.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/Cercube.bundle/attributions.plist -------------------------------------------------------------------------------- /Resources/Cercube.bundle/base.js: -------------------------------------------------------------------------------- 1 | var global = this 2 | 3 | ;(function() { 4 | 5 | var _ocCls = {}; 6 | var _jsCls = {}; 7 | 8 | var _formatOCToJS = function(obj) { 9 | if (obj === undefined || obj === null) return false 10 | if (typeof obj == "object") { 11 | if (obj.__obj) return obj 12 | if (obj.__isNil) return false 13 | } 14 | if (obj instanceof Array) { 15 | var ret = [] 16 | obj.forEach(function(o) { 17 | ret.push(_formatOCToJS(o)) 18 | }) 19 | return ret 20 | } 21 | if (obj instanceof Function) { 22 | return function() { 23 | var args = Array.prototype.slice.call(arguments) 24 | var formatedArgs = _OC_formatJSToOC(args) 25 | for (var i = 0; i < args.length; i++) { 26 | if (args[i] === null || args[i] === undefined || args[i] === false) { 27 | formatedArgs.splice(i, 1, undefined) 28 | } else if (args[i] == nsnull) { 29 | formatedArgs.splice(i, 1, null) 30 | } 31 | } 32 | return _OC_formatOCToJS(obj.apply(obj, formatedArgs)) 33 | } 34 | } 35 | if (obj instanceof Object) { 36 | var ret = {} 37 | for (var key in obj) { 38 | ret[key] = _formatOCToJS(obj[key]) 39 | } 40 | return ret 41 | } 42 | return obj 43 | } 44 | 45 | var _methodFunc = function(instance, clsName, methodName, args, isSuper, isPerformSelector) { 46 | var selectorName = methodName 47 | if (!isPerformSelector) { 48 | methodName = methodName.replace(/__/g, "-") 49 | selectorName = methodName.replace(/_/g, ":").replace(/-/g, "_") 50 | var marchArr = selectorName.match(/:/g) 51 | var numOfArgs = marchArr ? marchArr.length : 0 52 | if (args.length > numOfArgs) { 53 | selectorName += ":" 54 | } 55 | } 56 | var ret = instance ? _OC_callI(instance, selectorName, args, isSuper): 57 | _OC_callC(clsName, selectorName, args) 58 | return _formatOCToJS(ret) 59 | } 60 | 61 | var _customMethods = { 62 | __c: function(methodName) { 63 | var slf = this 64 | 65 | if (slf instanceof Boolean) { 66 | return function() { 67 | return false 68 | } 69 | } 70 | if (slf[methodName]) { 71 | return slf[methodName].bind(slf); 72 | } 73 | 74 | if (!slf.__obj && !slf.__clsName) { 75 | throw new Error(slf + '.' + methodName + ' is undefined') 76 | } 77 | if (slf.__isSuper && slf.__clsName) { 78 | slf.__clsName = _OC_superClsName(slf.__obj.__realClsName ? slf.__obj.__realClsName: slf.__clsName); 79 | } 80 | var clsName = slf.__clsName 81 | if (clsName && _ocCls[clsName]) { 82 | var methodType = slf.__obj ? 'instMethods': 'clsMethods' 83 | if (_ocCls[clsName][methodType][methodName]) { 84 | slf.__isSuper = 0; 85 | return _ocCls[clsName][methodType][methodName].bind(slf) 86 | } 87 | } 88 | 89 | return function(){ 90 | var args = Array.prototype.slice.call(arguments) 91 | return _methodFunc(slf.__obj, slf.__clsName, methodName, args, slf.__isSuper) 92 | } 93 | }, 94 | 95 | super: function() { 96 | var slf = this 97 | if (slf.__obj) { 98 | slf.__obj.__realClsName = slf.__realClsName; 99 | } 100 | return {__obj: slf.__obj, __clsName: slf.__clsName, __isSuper: 1} 101 | }, 102 | 103 | performSelectorInOC: function() { 104 | var slf = this 105 | var args = Array.prototype.slice.call(arguments) 106 | return {__isPerformInOC:1, obj:slf.__obj, clsName:slf.__clsName, sel: args[0], args: args[1], cb: args[2]} 107 | }, 108 | 109 | performSelector: function() { 110 | var slf = this 111 | var args = Array.prototype.slice.call(arguments) 112 | return _methodFunc(slf.__obj, slf.__clsName, args[0], args.splice(1), slf.__isSuper, true) 113 | } 114 | } 115 | 116 | for (var method in _customMethods) { 117 | if (_customMethods.hasOwnProperty(method)) { 118 | Object.defineProperty(Object.prototype, method, {value: _customMethods[method], configurable:false, enumerable: false}) 119 | } 120 | } 121 | 122 | var _require = function(clsName) { 123 | if (!global[clsName]) { 124 | global[clsName] = { 125 | __clsName: clsName 126 | } 127 | } 128 | return global[clsName] 129 | } 130 | 131 | global.require = function() { 132 | var lastRequire 133 | for (var i = 0; i < arguments.length; i ++) { 134 | arguments[i].split(',').forEach(function(clsName) { 135 | lastRequire = _require(clsName.trim()) 136 | }) 137 | } 138 | return lastRequire 139 | } 140 | 141 | var _formatDefineMethods = function(methods, newMethods, realClsName) { 142 | for (var methodName in methods) { 143 | if (!(methods[methodName] instanceof Function)) return; 144 | (function(){ 145 | var originMethod = methods[methodName] 146 | newMethods[methodName] = [originMethod.length, function() { 147 | try { 148 | var args = _formatOCToJS(Array.prototype.slice.call(arguments)) 149 | var lastSelf = global.self 150 | global.self = args[0] 151 | if (global.self) global.self.__realClsName = realClsName 152 | args.splice(0,1) 153 | var ret = originMethod.apply(originMethod, args) 154 | global.self = lastSelf 155 | return ret 156 | } catch(e) { 157 | _OC_catch(e.message, e.stack) 158 | } 159 | }] 160 | })() 161 | } 162 | } 163 | 164 | var _wrapLocalMethod = function(methodName, func, realClsName) { 165 | return function() { 166 | var lastSelf = global.self 167 | global.self = this 168 | this.__realClsName = realClsName 169 | var ret = func.apply(this, arguments) 170 | global.self = lastSelf 171 | return ret 172 | } 173 | } 174 | 175 | var _setupJSMethod = function(className, methods, isInst, realClsName) { 176 | for (var name in methods) { 177 | var key = isInst ? 'instMethods': 'clsMethods', 178 | func = methods[name] 179 | _ocCls[className][key][name] = _wrapLocalMethod(name, func, realClsName) 180 | } 181 | } 182 | 183 | var _propertiesGetFun = function(name){ 184 | return function(){ 185 | var slf = this; 186 | if (!slf.__ocProps) { 187 | var props = _OC_getCustomProps(slf.__obj) 188 | if (!props) { 189 | props = {} 190 | _OC_setCustomProps(slf.__obj, props) 191 | } 192 | slf.__ocProps = props; 193 | } 194 | return slf.__ocProps[name]; 195 | }; 196 | } 197 | 198 | var _propertiesSetFun = function(name){ 199 | return function(jval){ 200 | var slf = this; 201 | if (!slf.__ocProps) { 202 | var props = _OC_getCustomProps(slf.__obj) 203 | if (!props) { 204 | props = {} 205 | _OC_setCustomProps(slf.__obj, props) 206 | } 207 | slf.__ocProps = props; 208 | } 209 | slf.__ocProps[name] = jval; 210 | }; 211 | } 212 | 213 | global.defineClass = function(declaration, properties, instMethods, clsMethods) { 214 | var newInstMethods = {}, newClsMethods = {} 215 | if (!(properties instanceof Array)) { 216 | clsMethods = instMethods 217 | instMethods = properties 218 | properties = null 219 | } 220 | 221 | if (properties) { 222 | properties.forEach(function(name){ 223 | if (!instMethods[name]) { 224 | instMethods[name] = _propertiesGetFun(name); 225 | } 226 | var nameOfSet = "set"+ name.substr(0,1).toUpperCase() + name.substr(1); 227 | if (!instMethods[nameOfSet]) { 228 | instMethods[nameOfSet] = _propertiesSetFun(name); 229 | } 230 | }); 231 | } 232 | 233 | var realClsName = declaration.split(':')[0].trim() 234 | 235 | _formatDefineMethods(instMethods, newInstMethods, realClsName) 236 | _formatDefineMethods(clsMethods, newClsMethods, realClsName) 237 | 238 | var ret = _OC_defineClass(declaration, newInstMethods, newClsMethods) 239 | var className = ret['cls'] 240 | var superCls = ret['superCls'] 241 | 242 | _ocCls[className] = { 243 | instMethods: {}, 244 | clsMethods: {}, 245 | } 246 | 247 | if (superCls.length && _ocCls[superCls]) { 248 | for (var funcName in _ocCls[superCls]['instMethods']) { 249 | _ocCls[className]['instMethods'][funcName] = _ocCls[superCls]['instMethods'][funcName] 250 | } 251 | for (var funcName in _ocCls[superCls]['clsMethods']) { 252 | _ocCls[className]['clsMethods'][funcName] = _ocCls[superCls]['clsMethods'][funcName] 253 | } 254 | } 255 | 256 | _setupJSMethod(className, instMethods, 1, realClsName) 257 | _setupJSMethod(className, clsMethods, 0, realClsName) 258 | 259 | return require(className) 260 | } 261 | 262 | global.defineProtocol = function(declaration, instProtos , clsProtos) { 263 | var ret = _OC_defineProtocol(declaration, instProtos,clsProtos); 264 | return ret 265 | } 266 | 267 | global.block = function(args, cb) { 268 | var that = this 269 | var slf = global.self 270 | if (args instanceof Function) { 271 | cb = args 272 | args = '' 273 | } 274 | var callback = function() { 275 | var args = Array.prototype.slice.call(arguments) 276 | global.self = slf 277 | return cb.apply(that, _formatOCToJS(args)) 278 | } 279 | var ret = {args: args, cb: callback, argCount: cb.length, __isBlock: 1} 280 | if (global.__genBlock) { 281 | ret['blockObj'] = global.__genBlock(args, cb) 282 | } 283 | return ret 284 | } 285 | 286 | if (global.console) { 287 | var jsLogger = console.log; 288 | global.console.log = function() { 289 | global._OC_log.apply(global, arguments); 290 | if (jsLogger) { 291 | jsLogger.apply(global.console, arguments); 292 | } 293 | } 294 | } else { 295 | global.console = { 296 | log: global._OC_log 297 | } 298 | } 299 | 300 | global.defineJSClass = function(declaration, instMethods, clsMethods) { 301 | var o = function() {}, 302 | a = declaration.split(':'), 303 | clsName = a[0].trim(), 304 | superClsName = a[1] ? a[1].trim() : null 305 | o.prototype = { 306 | init: function() { 307 | if (this.super()) this.super().init() 308 | return this; 309 | }, 310 | super: function() { 311 | return superClsName ? _jsCls[superClsName].prototype : null 312 | } 313 | } 314 | var cls = { 315 | alloc: function() { 316 | return new o; 317 | } 318 | } 319 | for (var methodName in instMethods) { 320 | o.prototype[methodName] = instMethods[methodName]; 321 | } 322 | for (var methodName in clsMethods) { 323 | cls[methodName] = clsMethods[methodName]; 324 | } 325 | global[clsName] = cls 326 | _jsCls[clsName] = o 327 | } 328 | 329 | global.YES = 1 330 | global.NO = 0 331 | global.nsnull = _OC_null 332 | global._formatOCToJS = _formatOCToJS 333 | 334 | })() 335 | -------------------------------------------------------------------------------- /Resources/Cercube.bundle/metadata.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/Cercube.bundle/metadata.plist -------------------------------------------------------------------------------- /Resources/Cercube.bundle/video.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/Cercube.bundle/video.mp4 -------------------------------------------------------------------------------- /Resources/Frameworks/Alderis.framework/Alderis: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/Frameworks/Alderis.framework/Alderis -------------------------------------------------------------------------------- /Resources/Frameworks/Alderis.framework/Assets.car: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/Frameworks/Alderis.framework/Assets.car -------------------------------------------------------------------------------- /Resources/Frameworks/Alderis.framework/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/Frameworks/Alderis.framework/Info.plist -------------------------------------------------------------------------------- /Resources/YouPiP.bundle/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/YouPiP.bundle/Info.plist -------------------------------------------------------------------------------- /Resources/YouPiP.bundle/PiPPlaceholderAsset.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/YouPiP.bundle/PiPPlaceholderAsset.mp4 -------------------------------------------------------------------------------- /Resources/YouPiP.bundle/yt-pip-overlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/YouPiP.bundle/yt-pip-overlay.png -------------------------------------------------------------------------------- /Resources/YouPiP.bundle/yt-pip-overlay@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/YouPiP.bundle/yt-pip-overlay@2x.png -------------------------------------------------------------------------------- /Resources/YouPiP.bundle/yt-pip-overlay@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/YouPiP.bundle/yt-pip-overlay@3x.png -------------------------------------------------------------------------------- /Resources/com.galacticdev.isponsorblock.bundle/LogoSponsorBlocker128px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/com.galacticdev.isponsorblock.bundle/LogoSponsorBlocker128px.png -------------------------------------------------------------------------------- /Resources/com.galacticdev.isponsorblock.bundle/PlayerInfoIconSponsorBlocker256px-20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/com.galacticdev.isponsorblock.bundle/PlayerInfoIconSponsorBlocker256px-20@2x.png -------------------------------------------------------------------------------- /Resources/com.galacticdev.isponsorblock.bundle/sponsorblockend-20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/com.galacticdev.isponsorblock.bundle/sponsorblockend-20@2x.png -------------------------------------------------------------------------------- /Resources/com.galacticdev.isponsorblock.bundle/sponsorblocksettings-20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/com.galacticdev.isponsorblock.bundle/sponsorblocksettings-20@2x.png -------------------------------------------------------------------------------- /Resources/com.galacticdev.isponsorblock.bundle/sponsorblockstart-20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Resources/com.galacticdev.isponsorblock.bundle/sponsorblockstart-20@2x.png -------------------------------------------------------------------------------- /Tweaks/Cercube.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Tweaks/Cercube.dylib -------------------------------------------------------------------------------- /Tweaks/YTUHD.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Tweaks/YTUHD.dylib -------------------------------------------------------------------------------- /Tweaks/YouPiP.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Tweaks/YouPiP.dylib -------------------------------------------------------------------------------- /Tweaks/iSponsorBlock.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Tweaks/iSponsorBlock.dylib -------------------------------------------------------------------------------- /Tweaks/libcolorpicker.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UprisingPulse/CercubePlus/d4818440ced726070aa21fd70bcd840c8cd5325f/Tweaks/libcolorpicker.dylib --------------------------------------------------------------------------------