text:
@implementation Mixpanel

static NSMutableDictionary *instances;
static NSString *defaultProjectToken;

#if !MIXPANEL_NO_REACHABILITY_SUPPORT
static CTTelephonyNetworkInfo *telephonyInfo;
#endif

+ (Mixpanel *)sharedInstanceWithToken:(NSString *)apiToken trackAutomaticEvents:(BOOL)trackAutomaticEvents trackCrashes:(BOOL)trackCrashes
{
    return [Mixpanel sharedInstanceWithToken:apiToken trackAutomaticEvents:trackAutomaticEvents trackCrashes:trackCrashes optOutTrackingByDefault:NO useUniqueDistinctId:NO];
}

+ (Mixpanel *)sharedInstanceWithToken:(NSString *)apiToken trackAutomaticEvents:(BOOL)trackAutomaticEvents trackCrashes:(BOOL)trackCrashes optOutTrackingByDefault:(BOOL)optOutTrackingByDefault useUniqueDistinctId:(BOOL)useUniqueDistinctId
{
    if (instances[apiToken]) {
        return instances[apiToken];
    }

#if defined(DEBUG)
    const NSUInteger flushInterval = 2;
#else
    const NSUInteger flushInterval = 60;
#endif

    return [[self alloc] initWithToken:apiToken trackAutomaticEvents:trackAutomaticEvents flushInterval:flushInterval trackCrashes:trackCrashes optOutTrackingByDefault:optOutTrackingByDefault useUniqueDistinctId:useUniqueDistinctId];
}

+ (Mixpanel *)sharedInstanceWithToken:(NSString *)apiToken trackAutomaticEvents:(BOOL)trackAutomaticEvents
{
    return [Mixpanel sharedInstanceWithToken:apiToken trackAutomaticEvents:trackAutomaticEvents trackCrashes:YES];
}

+ (Mixpanel *)sharedInstanceWithToken:(NSString *)apiToken trackAutomaticEvents:(BOOL)trackAutomaticEvents optOutTrackingByDefault:(BOOL)optOutTrackingByDefault
{
    return [Mixpanel sharedInstanceWithToken:apiToken trackAutomaticEvents:trackAutomaticEvents trackCrashes:YES optOutTrackingByDefault:optOutTrackingByDefault useUniqueDistinctId:NO];
}

+ (Mixpanel *)sharedInstanceWithToken:(NSString *)apiToken trackAutomaticEvents:(BOOL)trackAutomaticEvents useUniqueDistinctId:(BOOL)useUniqueDistinctId
{
    return [Mixpanel sharedInstanceWithToken:apiToken trackAutomaticEvents:trackAutomaticEvents trackCrashes:YES optOutTrackingByDefault:NO useUniqueDistinctId:useUniqueDistinctId];
}

+ (nullable Mixpanel *)sharedInstance
{
    if (instances.count == 0) {
        MPLogWarning(@"sharedInstance called before creating a Mixpanel instance");
        return nil;
    }

    if (instances.count > 1) {
        MPLogWarning([NSString stringWithFormat:@"sharedInstance called with multiple mixpanel instances. Using (the first) token %@", defaultProjectToken]);
    }

    return instances[defaultProjectToken];
}

- (instancetype)init:(NSString *)apiToken
{
    if (self = [super init]) {
        self.cachedGroups = [NSMutableDictionary dictionary];
        self.timedEvents = [NSMutableDictionary dictionary];
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            instances = [NSMutableDictionary dictionary];
            defaultProjectToken = apiToken;
            #if !MIXPANEL_NO_REACHABILITY_SUPPORT
            telephonyInfo = [[CTTelephonyNetworkInfo alloc] init];
            #endif
        });
    }

    return self;
}

- (instancetype)initWithToken:(NSString *)apiToken
         trackAutomaticEvents:(BOOL)trackAutomaticEvents
                flushInterval:(NSUInteger)flushInterval
                 trackCrashes:(BOOL)trackCrashes
               optOutTrackingByDefault:(BOOL)optOutTrackingByDefault
          useUniqueDistinctId:(BOOL)useUniqueDistinctId
{
    if (apiToken.length == 0) {
        if (apiToken == nil) {
            apiToken = @"";
        }
        MPLogWarning(@"%@ empty api token", self);
    }
    if (self = [self init:apiToken]) {
        if (![Mixpanel isAppExtension]) {
#if !MIXPANEL_NO_AUTOMATIC_EVENTS_SUPPORT
            if (trackCrashes) {
                // Install signal and exception handlers first
                [[MixpanelExceptionHandler sharedHandler] addMixpanelInstance:self];
            }
#endif
        }
        self.apiToken = apiToken;
        _trackAutomaticEventsEnabled = trackAutomaticEvents;
        _flushInterval = flushInterval;
        self.persistence = [[MixpanelPersistence alloc] initWithToken: apiToken];
        [self.persistence migrate];
        self.useIPAddressForGeoLocation = YES;
        self.shouldManageNetworkActivityIndicator = YES;
        self.flushOnBackground = YES;

        self.serverURL = @"https://api.mixpanel.com";
        self.useUniqueDistinctId = useUniqueDistinctId;
        self.distinctId = [self defaultDistinctId];
        self.superProperties = [NSDictionary dictionary];
        self.automaticProperties = [self collectAutomaticProperties];

#if !TARGET_OS_WATCH && !TARGET_OS_OSX
        if (![Mixpanel isAppExtension]) {
            self.taskId = UIBackgroundTaskInvalid;
        }
#endif
        NSString *label = [NSString stringWithFormat:@"com.mixpanel.%@.%p", apiToken, (void *)self];
        self.serialQueue = dispatch_queue_create([label UTF8String], DISPATCH_QUEUE_SERIAL);
        NSString *networkLabel = [label stringByAppendingString:@".network"];
        self.networkQueue = dispatch_queue_create([networkLabel UTF8String], DISPATCH_QUEUE_SERIAL);
        self.sessionMetadata = [[SessionMetadata alloc] init];
        self.network = [[MPNetwork alloc] initWithServerURL:[NSURL URLWithString:self.serverURL] mixpanel:self];
        self.people = [[MixpanelPeople alloc] initWithMixpanel:self];
        [self setUpListeners];
        [self unarchive];

        // check whether we should opt out by default
        // note: we don't override opt out persistence here since opt-out default state is often
        // used as an initial state while GDPR information is being collected
        if (optOutTrackingByDefault && ([self hasOptedOutTracking] || self.optOutStatusNotSet)) {
            [self optOutTracking];
        }

        if (![Mixpanel isAppExtension]) {
#if !MIXPANEL_NO_AUTOMATIC_EVENTS_SUPPORT
            self.automaticEvents = [[AutomaticEvents alloc] init];
            self.automaticEvents.delegate = self;
            [self.automaticEvents initializeEvents:self.people apiToken:apiToken];
#endif
        }
#if defined(DEBUG)
        [self didDebugInit:apiToken];
#endif
        instances[apiToken] = self;
    }
    return self;
}

- (instancetype)initWithToken:(NSString *)apiToken
         trackAutomaticEvents:(BOOL)trackAutomaticEvents
                flushInterval:(NSUInteger)flushInterval
                 trackCrashes:(BOOL)trackCrashes
          useUniqueDistinctId:(BOOL)useUniqueDistinctId
{
    return [self initWithToken:apiToken
          trackAutomaticEvents:trackAutomaticEvents
                 flushInterval:flushInterval
                  trackCrashes:trackCrashes
                optOutTrackingByDefault:NO
           useUniqueDistinctId:useUniqueDistinctId];
}

- (instancetype)initWithToken:(NSString *)apiToken
         trackAutomaticEvents:(BOOL)trackAutomaticEvents
             andFlushInterval:(NSUInteger)flushInterval
{
    return [self initWithToken:apiToken
          trackAutomaticEvents:trackAutomaticEvents
                 flushInterval:flushInterval
                  trackCrashes:NO
           useUniqueDistinctId:NO];
}

- (void)didDebugInit:(NSString *)distinctId
{
    if (distinctId.length == 32) {
        NSInteger debugInitCount = [[NSUserDefaults standardUserDefaults] integerForKey:MPDebugInitCountKey] + 1;
        [self sendHttpEvent:@"SDK Debug Launch" apiToken:@"metrics-1" distinctId:distinctId properties:@{@"Debug Launch Count": @(debugInitCount)}];
        [self checkIfImplemented:distinctId debugInitCount: debugInitCount];
        [[NSUserDefaults standardUserDefaults] setInteger:debugInitCount forKey:MPDebugInitCountKey];
        [[NSUserDefaults standardUserDefaults] synchronize];
    }
}

- (void)checkIfImplemented:(NSString *)distinctId
            debugInitCount:(NSInteger)debugInitCount
{
    BOOL hasImplemented = [[NSUserDefaults standardUserDefaults] boolForKey:MPDebugImplementedKey];
    if (!hasImplemented) {
        NSInteger completed = 0;
        BOOL hasTracked = [[NSUserDefaults standardUserDefaults] boolForKey:MPDebugTrackedKey];
        completed += hasTracked;
        BOOL hasIdentified = [[NSUserDefaults standardUserDefaults] boolForKey:MPDebugIdentifiedKey];
        completed += hasIdentified;
        BOOL hasAliased = [[NSUserDefaults standardUserDefaults] boolForKey:MPDebugAliasedKey];
        completed += hasAliased;
        BOOL hasUsedPeople = [[NSUserDefaults standardUserDefaults] boolForKey:MPDebugUsedPeopleKey];
        completed += hasUsedPeople;
        if (completed >= 3) {
            [self sendHttpEvent:@"SDK Implemented" apiToken:@"metrics-1" distinctId:distinctId properties:@{@"Tracked": @(hasTracked), @"Identified": @(hasIdentified), @"Aliased": @(hasAliased), @"Used People": @(hasUsedPeople), @"Debug Launch Count": @(debugInitCount)}];
            [[NSUserDefaults standardUserDefaults] setBool:YES forKey:MPDebugImplementedKey];
        }
    }
}

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];

    if (![Mixpanel isAppExtension]) {
#if !MIXPANEL_NO_REACHABILITY_SUPPORT
        if (_reachability != NULL) {
            if (!SCNetworkReachabilitySetCallback(_reachability, NULL, NULL)) {
                MPLogError(@"%@ error unsetting reachability callback", self);
            }
            if (!SCNetworkReachabilitySetDispatchQueue(_reachability, NULL)) {
                MPLogError(@"%@ error unsetting reachability dispatch queue", self);
            }
            CFRelease(_reachability);
            _reachability = NULL;
        }
#endif
    }
}

+ (BOOL)isAppExtension
{
#if TARGET_OS_IOS
    return [[NSBundle mainBundle].bundlePath hasSuffix:@".appex"];
#else
    return NO;
#endif
}

#if !MIXPANEL_NO_UIAPPLICATION_ACCESS
+ (UIApplication *)sharedUIApplication
{
    if ([[UIApplication class] respondsToSelector:@selector(sharedApplication)]) {
        return [[UIApplication class] performSelector:@selector(sharedApplication)];
    }
    return nil;
}
#endif

- (BOOL)shouldManageNetworkActivityIndicator
{
    return self.network.shouldManageNetworkActivityIndicator;
}

- (void)setShouldManageNetworkActivityIndicator:(BOOL)shouldManageNetworkActivityIndicator
{
    self.network.shouldManageNetworkActivityIndicator = shouldManageNetworkActivityIndicator;
}

- (BOOL)useIPAddressForGeoLocation
{
    return self.network.useIPAddressForGeoLocation;
}

- (void)setUseIPAddressForGeoLocation:(BOOL)useIPAddressForGeoLocation
{
    self.network.useIPAddressForGeoLocation = useIPAddressForGeoLocation;
}

- (void)setTrackAutomaticEventsEnabled:(BOOL)trackAutomaticEventsEnabled
{
    _trackAutomaticEventsEnabled = trackAutomaticEventsEnabled;
    if (!trackAutomaticEventsEnabled) {
        dispatch_async(self.serialQueue, ^{
            [self.persistence removeAutomaticEvents];
        });
    }
}

#if !MIXPANEL_NO_AUTOMATIC_EVENTS_SUPPORT
- (UInt64)minimumSessionDuration
{
    return self.automaticEvents.minimumSessionDuration;
}

- (void)setMinimumSessionDuration:(UInt64)minimumSessionDuration
{
    self.automaticEvents.minimumSessionDuration = minimumSessionDuration;
}

- (UInt64)maximumSessionDuration
{
    return self.automaticEvents.maximumSessionDuration;
}

- (void)setMaximumSessionDuration:(UInt64)maximumSessionDuration
{
    self.automaticEvents.maximumSessionDuration = maximumSessionDuration;
}
#endif

#pragma mark - Tracking
+ (void)assertPropertyTypes:(NSDictionary *)properties
{
    [Mixpanel assertPropertyTypesInDictionary:properties depth:0];
}

+ (void)assertPropertyType:(id)propertyValue depth:(NSUInteger)depth
{
    // Note that @YES and @NO pass as instances of NSNumber class.
    NSAssert([propertyValue isKindOfClass:[NSString class]] ||
             [propertyValue isKindOfClass:[NSNumber class]] ||
             [propertyValue isKindOfClass:[NSNull class]] ||
             [propertyValue isKindOfClass:[NSArray class]] ||
             [propertyValue isKindOfClass:[NSDictionary class]] ||
             [propertyValue isKindOfClass:[NSDate class]] ||
             [propertyValue isKindOfClass:[NSURL class]],
             @"%@ property values must be NSString, NSNumber, NSNull, NSArray, NSDictionary, NSDate or NSURL. got: %@ %@", self, [propertyValue class], propertyValue);

#ifdef DEBUG
    if (depth == 3) {
        MPLogWarning(@"Your properties are overly nested, specifically 3 or more levels deep. \
                     Generally this is not recommended due to its complexity.");
    }
    if ([propertyValue isKindOfClass:[NSDictionary class]]) {
        [Mixpanel assertPropertyTypesInDictionary:propertyValue depth:depth+1];
    } else if ([propertyValue isKindOfClass:[NSArray class]]) {
        [Mixpanel assertPropertyTypesInArray:propertyValue depth:depth+1];
    }
#endif
}

+ (void)assertPropertyTypesInDictionary:(NSDictionary *)properties depth:(NSUInteger)depth
{
    if([properties count] > 1000) {
        MPLogWarning(@"You have an NSDictionary in your properties that is bigger than 1000 in size. \
                     Generally this is not recommended due to its size.");
    }
    for (id key in properties) {
        id value = properties[key];
        NSAssert([key isKindOfClass:[NSString class]], @"%@ property keys must be NSString. got: %@ %@", self, [key class], key);
        [Mixpanel assertPropertyType:value depth:depth];
    }
}

+ (void)assertPropertyTypesInArray:(NSArray *)arrayOfProperties depth:(NSUInteger)depth
{
    if([arrayOfProperties count] > 1000) {
        MPLogWarning(@"You have an NSArray in your properties that is bigger than 1000 in size. \
                     Generally this is not recommended due to its size.");
    }
    for (id value in arrayOfProperties) {
        [Mixpanel assertPropertyType:value depth:depth];
    }
}

- (NSString *)defaultDistinctId
{
    NSString *distinctId;
#if defined(MIXPANEL_UNIQUE_DISTINCT_ID)
    distinctId = [self uniqueIdentifierForDevice];
#else
    if (self.useUniqueDistinctId) {
        distinctId = [self uniqueIdentifierForDevice];
    } else {
        distinctId = [[NSUUID UUID] UUIDString];
    }
#endif
    if (!distinctId) {
        MPLogInfo(@"%@ error getting device identifier: falling back to uuid", self);
        distinctId = [[NSUUID UUID] UUIDString];
    }
    return distinctId;
}

- (MixpanelIdentity *)currentMixpanelIdentity
{
    return [[MixpanelIdentity alloc] initWithDistinctId:self.distinctId peopleDistinctId:self.people.distinctId
                                            anonymousId:self.anonymousId userId:self.userId alias:self.alias hadPersistedDistinctId:self.hadPersistedDistinctId];;
}

- (void)identify:(NSString *)distinctId
{
    [self identify:distinctId usePeople:YES];
}

- (void)identify:(NSString *)distinctId usePeople:(BOOL)usePeople
{
    if ([self hasOptedOutTracking]) {
        return;
    }

    if (distinctId.length == 0) {
        MPLogWarning(@"%@ cannot identify blank distinct id: %@", self, distinctId);
        return;
    }

#if defined(DEBUG)
    [[NSUserDefaults standardUserDefaults] setBool:YES forKey:MPDebugIdentifiedKey];
#endif

    dispatch_async(self.serialQueue, ^{
        if(!self.anonymousId) {
            self.anonymousId = self.distinctId;
            self.hadPersistedDistinctId = YES;
        }
        // identify only changes the distinct id if it doesn't match the ID
        // if it's new, blow away the alias as well.
        if (![distinctId isEqualToString:self.distinctId]) {
            NSString *oldDistinctId = [self.distinctId copy];
            self.alias = nil;
            self.distinctId = distinctId;
            self.userId = distinctId;
            [self track:@"$identify" properties:@{@"$anon_distinct_id": oldDistinctId}];
        }
        if (usePeople) {
            self.people.distinctId = distinctId;
            [self.persistence identifyPeople];
        } else {
            self.people.distinctId = nil;
        }

        [MixpanelPersistence saveIdentity:[self currentMixpanelIdentity] apiToken:self.apiToken];
    });
#if MIXPANEL_FLUSH_IMMEDIATELY
    [self flush];
#else
    if ([Mixpanel isAppExtension]) {
        [self flush];
    }
#endif
}

- (void)createAlias:(NSString *)alias forDistinctID:(NSString *)distinctID
{
    [self createAlias:alias forDistinctID:distinctID usePeople:YES];
}

- (void)createAlias:(NSString *)alias forDistinctID:(NSString *)distinctID usePeople:(BOOL)usePeople
{
    if ([self hasOptedOutTracking]) {
        return;
    }

    if (alias.length == 0) {
        MPLogError(@"%@ create alias called with empty alias: %@", self, alias);
        return;
    }
    if (distinctID.length == 0) {
        MPLogError(@"%@ create alias called with empty distinct id: %@", self, distinctID);
        return;
    }

#if defined(DEBUG)
    [[NSUserDefaults standardUserDefaults] setBool:YES forKey:MPDebugAliasedKey];
#endif

    if (![alias isEqualToString:distinctID]) {
        dispatch_async(self.serialQueue, ^{
            self.alias = alias;
            [MixpanelPersistence saveIdentity:[self currentMixpanelIdentity]  apiToken:self.apiToken];
        });
        [self track:@"$create_alias" properties:@{ @"distinct_id": distinctID, @"alias": alias }];
        [self identify:distinctID usePeople:usePeople];
        [self flush];
    } else {
        MPLogWarning(@"alias: %@ matches distinctID: %@ - skipping api call.", alias, distinctID);
    }
}

- (void)track:(NSString *)event
{
    [self track:event properties:nil];
}

- (void)track:(NSString *)event properties:(NSDictionary *)properties
{
    if ([self hasOptedOutTracking]) {
        return;
    }

    if (event.length == 0) {
        MPLogWarning(@"%@ mixpanel track called with empty event parameter. using 'mp_event'", self);
        event = @"mp_event";
    }

    if (!self.trackAutomaticEventsEnabled && [event hasPrefix:@"$ae_"]) {
        return;
    }

#if defined(DEBUG)
    if (![event hasPrefix:@"$"]) [[NSUserDefaults standardUserDefaults] setBool:YES forKey:MPDebugTrackedKey];
#endif

    properties = [properties copy];
    [Mixpanel assertPropertyTypes:properties];

    NSTimeInterval epochInterval = [[NSDate date] timeIntervalSince1970];
    NSNumber *epochMilliseconds = @(round(epochInterval * 1000));
    dispatch_async(self.serialQueue, ^{
        NSNumber *eventStartTime = self.timedEvents[event];
        NSMutableDictionary *p = [NSMutableDictionary dictionaryWithDictionary:self.automaticProperties];
        p[@"token"] = self.apiToken;
        p[@"time"] = epochMilliseconds;
        if (eventStartTime != nil) {
            [self.timedEvents removeObjectForKey:event];
            p[@"$duration"] = @([[NSString stringWithFormat:@"%.3f", epochInterval - [eventStartTime doubleValue]] floatValue]);
        }
        if (self.distinctId) {
            p[@"distinct_id"] = self.distinctId;
        }
        if (self.anonymousId) {
            p[@"$device_id"] = self.anonymousId;
        }
        if (self.userId) {
            p[@"$user_id"] = self.userId;
        }
        if (self.hadPersistedDistinctId) {
            p[@"$had_persisted_distinct_id"] = [NSNumber numberWithBool:self.hadPersistedDistinctId];
        }
        [p addEntriesFromDictionary:self.superProperties];
        if (properties) {
            [p addEntriesFromDictionary:properties];
        }

        NSMutableDictionary *e = [[NSMutableDictionary alloc] initWithDictionary:@{ @"event": event,
                                                                                @"properties": [NSDictionary dictionaryWithDictionary:p]}];
        [e addEntriesFromDictionary:[self.sessionMetadata toDictionaryForEvent:YES]];
        MPLogInfo(@"%@ queueing event: %@", self, e);
        [self.persistence saveEntity:e type:PersistenceTypeEvents flag:NO];
        [MixpanelPersistence saveTimedEvents:self.timedEvents apiToken:self.apiToken];
    });
#if MIXPANEL_FLUSH_IMMEDIATELY
    [self flush];
#else
    if ([Mixpanel isAppExtension]) {
        [self flush];
    }
#endif
}

- (void)trackWithGroups:(NSString *)event
             properties:(NSDictionary *)properties
                 groups:(NSDictionary *)groups {
    if ([self hasOptedOutTracking]) {
        return;
    }
    if (properties == nil) {
        [self track:event properties:groups];
        return;
    }
    if (groups == nil) {
        [self track:event properties:properties];
        return;
    }
    NSMutableDictionary *mergedProps = [properties mutableCopy];
    [groups enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
        if (value != nil)
            [mergedProps setObject:value forKey:key];
    }];
    [self track:event properties:mergedProps];
}

- (void)addGroup:(NSString *)groupKey groupID:(id<MixpanelType>)groupID {
    if ([self hasOptedOutTracking]) {
        return;
    }

    [self addValuesToGroupSuperProperty:groupKey groupID:groupID];
    [self.people union:@{groupKey : @[groupID]}];
}

- (void)addValuesToGroupSuperProperty:(NSString *)groupKey
                              groupID:(id<MixpanelType>)groupID {
    [self updateSuperPropertiesAsync:^NSDictionary *(NSDictionary *superProps) {
        NSMutableDictionary *mutableSuperProps = [superProps mutableCopy];
        NSMutableArray<id<MixpanelType>> *values =
        [NSMutableArray arrayWithArray:mutableSuperProps[groupKey]];
        BOOL exist = NO;
        for (NSUInteger i = 0; i < [values count]; ++i) {
            if ([values[i] equalToMixpanelType:groupID]) {
                exist = YES;
                break;
            }
        }
        if (!exist) {
            [values addObject:groupID];
        }
        mutableSuperProps[groupKey] = [values copy];
        return mutableSuperProps;
    }];
}

- (void)removeGroup:(NSString *)groupKey groupID:(id<MixpanelType>)groupID {
    if ([self hasOptedOutTracking]) {
        return;
    }
    [self updateSuperPropertiesAsync:^NSDictionary *(NSDictionary *superProps) {
        NSMutableDictionary *mutableSuperProps = [superProps mutableCopy];
        NSObject *oldValue = superProps[groupKey];
        if (oldValue == nil) {
            return superProps;
        }
        if (![oldValue isKindOfClass:[NSArray<MixpanelType> class]]) {
            [mutableSuperProps removeObjectForKey:groupKey];
            [self.people unset:@[groupKey]];
            return mutableSuperProps;
        }
        NSMutableArray *vals =
        [NSMutableArray arrayWithArray:mutableSuperProps[groupKey]];

        for (NSUInteger i = 0; i < [vals count]; ++i) {
            if ([vals[i] equalToMixpanelType:groupID]) {
                [vals removeObjectAtIndex:i];
                break;
            }
        }
        if (![vals count]) {
            [mutableSuperProps removeObjectForKey:groupKey];
        } else {
            mutableSuperProps[groupKey] = vals;
        }
        [self.people remove:@{groupKey : groupID}];
        return mutableSuperProps;
    }];
}

- (void)setGroup:(NSString *)groupKey
        groupIDs:(NSArray<id<MixpanelType>> *)groupIDs {
    if ([self hasOptedOutTracking]) {
        return;
    }
    NSDictionary *properties = @{groupKey : groupIDs};
    [self registerSuperProperties:properties];
    [self.people set:properties];
}

- (void)setGroup:(NSString *)groupKey groupID:(id<MixpanelType>)groupID {
    NSArray *groupIDs = @[ groupID ];
    [self setGroup:groupKey groupIDs:groupIDs];
}

- (NSString *)keyForGroup:(NSString *)groupKey
                  groupID:(id<MixpanelType>)groupID {
    return [NSString stringWithFormat:@"%@_%@", groupKey, groupID];
}

- (MixpanelGroup *)getGroup:(NSString *)groupKey
                    groupID:(id<MixpanelType>)groupID {
    NSString *mapKey = [self keyForGroup:groupKey groupID:groupID];
    @synchronized(self.cachedGroups) {
        MixpanelGroup *group = self.cachedGroups[mapKey];
        if (!group || group.groupKey != groupKey ||
            ![groupID equalToMixpanelType:group.groupID]) {
            group = [[MixpanelGroup alloc] init:self groupKey:groupKey groupID:groupID];
            // if key collision happens, the old entry will be evicted
            self.cachedGroups[mapKey] = group;
        }
        return group;
    }
}

typedef NSDictionary*(^PropertyUpdate)(NSDictionary*);

- (void)updateSuperPropertiesAsync:(PropertyUpdate) update{
    dispatch_async(self.serialQueue, ^{
        NSDictionary* newSuperProp = update(self.currentSuperProperties);
        [self setSuperProperties:newSuperProp];
        [MixpanelPersistence saveSuperProperties:self.superProperties apiToken:self.apiToken];
    });
}

- (void)registerSuperProperties:(NSDictionary *)properties
{
    if ([self hasOptedOutTracking]) {
        return;
    }

    [Mixpanel assertPropertyTypes:properties];
    dispatch_async(self.serialQueue, ^{
        NSMutableDictionary *tmp = [NSMutableDictionary dictionaryWithDictionary:self.superProperties];
        [tmp addEntriesFromDictionary:properties];
        self.superProperties = [NSDictionary dictionaryWithDictionary:tmp];
        [MixpanelPersistence saveSuperProperties:self.superProperties apiToken:self.apiToken];
    });
}

- (void)registerSuperPropertiesOnce:(NSDictionary *)properties
{
    [self registerSuperPropertiesOnce:properties defaultValue:nil];
}

- (void)registerSuperPropertiesOnce:(NSDictionary *)properties defaultValue:(id)defaultValue
{
    if ([self hasOptedOutTracking]) {
        return;
    }

    [Mixpanel assertPropertyTypes:properties];
    dispatch_async(self.serialQueue, ^{
        NSMutableDictionary *tmp = [NSMutableDictionary dictionaryWithDictionary:self.superProperties];
        for (NSString *key in properties) {
            id value = tmp[key];
            if (value == nil || [value isEqual:defaultValue]) {
                tmp[key] = properties[key];
            }
        }
        self.superProperties = [NSDictionary dictionaryWithDictionary:tmp];
        [MixpanelPersistence saveSuperProperties:self.superProperties apiToken:self.apiToken];
    });
}

- (void)unregisterSuperProperty:(NSString *)propertyName
{
    dispatch_async(self.serialQueue, ^{
        NSMutableDictionary *tmp = [NSMutableDictionary dictionaryWithDictionary:self.superProperties];
        tmp[propertyName] = nil;
        self.superProperties = [NSDictionary dictionaryWithDictionary:tmp];
        [MixpanelPersistence saveSuperProperties:self.superProperties apiToken:self.apiToken];
    });
}

- (void)clearSuperProperties
{
    dispatch_async(self.serialQueue, ^{
        self.superProperties = @{};
        [MixpanelPersistence saveSuperProperties:self.superProperties apiToken:self.apiToken];
    });
}

- (NSDictionary *)currentSuperProperties
{
    return [self.superProperties copy];
}

- (void)timeEvent:(NSString *)event
{
    if ([self hasOptedOutTracking]) {
        return;
    }

    NSNumber *startTime = @([[NSDate date] timeIntervalSince1970]);

    if (event.length == 0) {
        MPLogError(@"Mixpanel cannot time an empty event");
        return;
    }

    dispatch_async(self.serialQueue, ^{
        self.timedEvents[event] = startTime;
        [MixpanelPersistence saveTimedEvents:self.timedEvents apiToken:self.apiToken];
    });
}

- (double)eventElapsedTime:(NSString *)event
{
    __block NSNumber *startTime;

    dispatch_sync(self.serialQueue, ^{
        startTime = self.timedEvents[event];
    });

    if (startTime == nil) {
        return 0;
    } else {
        return [[NSDate date] timeIntervalSince1970] - [startTime doubleValue];
    }
}

- (void)clearTimedEvent:(NSString *)event
{
    if (event.length == 0) {
        MPLogError(@"Mixpanel cannot clear the timer for an empty event");
        return;
    }

    dispatch_async(self.serialQueue, ^{
        [self.timedEvents removeObjectForKey:event];
        [MixpanelPersistence saveTimedEvents:self.timedEvents apiToken:self.apiToken];
    });
}

- (void)clearTimedEvents
{
    dispatch_async(self.serialQueue, ^{
        self.timedEvents = [NSMutableDictionary dictionary];
        [MixpanelPersistence saveTimedEvents:self.timedEvents apiToken:self.apiToken];
    });
}

- (void)reset
{
    [self flush];
    dispatch_async(self.serialQueue, ^{
        // wait for all current network requests to finish before resetting
        [MixpanelPersistence deleteMPUserDefaultsData:self.apiToken];
        self.anonymousId = [self defaultDistinctId];
        self.distinctId = self.anonymousId;
        self.superProperties = [NSDictionary dictionary];
        self.userId = nil;
        self.people.distinctId = nil;
        self.alias = nil;
        self.hadPersistedDistinctId = NO;
        self.cachedGroups = [NSMutableDictionary dictionary];
        self.timedEvents = [NSMutableDictionary dictionary];
        [self.persistence resetEntities];
        [self archive];
    });
}

- (void)optOutTracking{
    if (self.people.distinctId) {
        [self.people deleteUser];
        [self.people clearCharges];
        [self flush];
    }
    dispatch_async(self.serialQueue, ^{
        self.alias = nil;
        self.people.distinctId = nil;
        self.userId = nil;
        self.anonymousId = [self defaultDistinctId];
        self.distinctId = self.anonymousId;
        self.hadPersistedDistinctId = NO;
        self.superProperties = [NSDictionary new];
        [self.timedEvents removeAllObjects];
        [self archive];
    });

    self.optOutStatus = YES;
    [MixpanelPersistence saveOptOutStatusFlag:YES apiToken:self.apiToken];
}

- (void)optInTracking
{
    [self optInTrackingForDistinctID:nil withEventProperties:nil];
}

- (void)optInTrackingForDistinctID:(NSString *)distinctID
{
    [self optInTrackingForDistinctID:distinctID withEventProperties:nil];
}

- (void)optInTrackingForDistinctID:(NSString *)distinctID withEventProperties:(NSDictionary *)properties
{
    self.optOutStatus = NO;
    [MixpanelPersistence saveOptOutStatusFlag:NO apiToken:self.apiToken];
    if (distinctID) {
        [self identify:distinctID];
    }
    [self track:@"$opt_in" properties:properties];
}

- (BOOL)hasOptedOutTracking
{
    return self.optOutStatus;
}

#pragma mark - Network control
- (void)setServerURL:(NSString *)serverURL
{
    _serverURL = serverURL.copy;
    self.network = [[MPNetwork alloc] initWithServerURL:[NSURL URLWithString:serverURL] mixpanel:self];
}

- (NSUInteger)flushInterval {
    return _flushInterval;
}

- (void)setFlushInterval:(NSUInteger)interval
{
    @synchronized (self) {
        _flushInterval = interval;
    }
    [self flush];
    [self startFlushTimer];
}

- (void)startFlushTimer
{
    [self stopFlushTimer];
    dispatch_async(dispatch_get_main_queue(), ^{
        if (self.flushInterval > 0) {
            self.timer = [NSTimer scheduledTimerWithTimeInterval:self.flushInterval
                                                          target:self
                                                        selector:@selector(flush)
                                                        userInfo:nil
                                                         repeats:YES];
            MPLogInfo(@"%@ started flush timer: %@", self, self.timer);
        }
    });
}

- (void)stopFlushTimer
{
    dispatch_async(dispatch_get_main_queue(), ^{
        if (self.timer) {
            [self.timer invalidate];
            MPLogInfo(@"%@ stopped flush timer: %@", self, self.timer);
            self.timer = nil;
        }
    });
}

- (void)flush
{
    [self flushWithCompletion:nil];
}

- (void)flushWithCompletion:(void (^)(void))handler
{
    if ([self hasOptedOutTracking]) {
        if (handler) {
            dispatch_async(dispatch_get_main_queue(), handler);
        }
        return;
    }

    dispatch_async(self.serialQueue, ^{
        MPLogInfo(@"%@ flush starting", self);

        __strong id<MixpanelDelegate> strongDelegate = self.delegate;
        if (strongDelegate && [strongDelegate respondsToSelector:@selector(mixpanelWillFlush:)]) {
            if (![strongDelegate mixpanelWillFlush:self]) {
                MPLogInfo(@"%@ flush deferred by delegate", self);
                return;
            }
        }

        NSArray *eventQueue = [self.persistence loadEntitiesInBatch:PersistenceTypeEvents];
        NSArray *peopleQueue = [self.persistence loadEntitiesInBatch:PersistenceTypePeople];
        NSArray *groupsQueue = [self.persistence loadEntitiesInBatch:PersistenceTypeGroups];

        dispatch_async(self.networkQueue, ^{
            [self.network flushEventQueue:eventQueue];
            [self.network flushPeopleQueue:peopleQueue];
            [self.network flushGroupsQueue:groupsQueue];

            if (handler) {
                dispatch_async(dispatch_get_main_queue(), handler);
            }

            MPLogInfo(@"%@ flush complete", self);
        });
    });
}

#pragma mark - Persistence
- (NSString *)filePathFor:(NSString *)data
{
    NSString *filename = [NSString stringWithFormat:@"mixpanel-%@-%@.plist", self.apiToken, data];
#if !TARGET_OS_TV
    return [[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject]
            stringByAppendingPathComponent:filename];
#else
    return [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]
            stringByAppendingPathComponent:filename];
#endif
}

- (void)archive
{
    [MixpanelPersistence saveTimedEvents:self.timedEvents apiToken:self.apiToken];
    [MixpanelPersistence saveSuperProperties:self.superProperties apiToken:self.apiToken];
    [MixpanelPersistence saveIdentity:[self currentMixpanelIdentity] apiToken:self.apiToken];
}

- (void)unarchive
{
    self.optOutStatus = [MixpanelPersistence loadOptOutStatusFlagWithApiToken:self.apiToken];
    self.optOutStatusNotSet = [MixpanelPersistence optOutStatusNotSet:self.apiToken];
    self.superProperties = [MixpanelPersistence loadSuperProperties:self.apiToken];
    self.timedEvents = [[MixpanelPersistence loadTimedEvents:self.apiToken] mutableCopy];
    MixpanelIdentity *mixpanelIdentity = [MixpanelPersistence loadIdentity:self.apiToken];
    self.distinctId = mixpanelIdentity.distinctId;
    self.people.distinctId = mixpanelIdentity.peopleDistinctId;
    self.anonymousId = mixpanelIdentity.anonymousId;
    self.userId = mixpanelIdentity.userId;
    self.alias = mixpanelIdentity.alias;
    self.hadPersistedDistinctId = mixpanelIdentity.hadPersistedDistinctId;
    if (!self.distinctId) {
        self.distinctId = [self defaultDistinctId];
        self.anonymousId = self.distinctId;
        self.hadPersistedDistinctId = YES;
        self.userId = nil;
        [MixpanelPersistence saveIdentity:[self currentMixpanelIdentity] apiToken:self.apiToken];
    }
}

#pragma mark - Application Helpers

- (NSString *)description
{
    return [NSString stringWithFormat:@"<Mixpanel: %p - Token: %@>", (void *)self, self.apiToken];
}

- (NSString *)deviceModel
{
    NSString *results = nil;
    size_t size;
    sysctlbyname("hw.machine", NULL, &size, NULL, 0);
    char answer[size];
    sysctlbyname("hw.machine", answer, &size, NULL, 0);
    if (size) {
        results = @(answer);
    } else {
        MPLogError(@"Failed fetch hw.machine from sysctl.");
    }
    return results;
}

#if TARGET_OS_OSX
- (NSString *)macOSIdentifier
{
    io_service_t platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault,
                                                              IOServiceMatching("IOPlatformExpertDevice"));
    CFStringRef serialNumberAsCFString = NULL;
    if (platformExpert) {
        serialNumberAsCFString = IORegistryEntryCreateCFProperty(platformExpert,
                                                                 CFSTR(kIOPlatformSerialNumberKey),
                                                                 kCFAllocatorDefault, 0);
        IOObjectRelease(platformExpert);
    }
    NSString *serialNumberAsNSString = nil;
    if (serialNumberAsCFString) {
        serialNumberAsNSString = [NSString stringWithString:(__bridge NSString *)serialNumberAsCFString];
        CFRelease(serialNumberAsCFString);
    }
    return serialNumberAsNSString;
}
#endif

- (NSString *)uniqueIdentifierForDevice
{
    NSString *distinctId = nil;
#if !TARGET_OS_WATCH && !TARGET_OS_OSX
    if (!distinctId && NSClassFromString(@"UIDevice")) {
        distinctId = [[UIDevice currentDevice].identifierForVendor UUIDString];
    }
#elif TARGET_OS_OSX
    distinctId = [self macOSIdentifier];
#endif
    return distinctId;
}

- (void)setCurrentRadio
{
    dispatch_async(self.serialQueue, ^{
        NSMutableDictionary *properties = [self.automaticProperties mutableCopy];
        if (properties) {
            properties[@"$radio"] = [self currentRadio];
            self.automaticProperties = [properties copy];
        }
    });
}

- (NSString *)currentRadio
{
#if !MIXPANEL_NO_REACHABILITY_SUPPORT
    if (![Mixpanel isAppExtension]) {
        if (@available(iOS 12, *)) {
            NSDictionary *radioDict = telephonyInfo.serviceCurrentRadioAccessTechnology;
            NSMutableString *radio = [NSMutableString new];
            [radioDict enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value, BOOL * _Nonnull stop) {
                if (value && [value hasPrefix:@"CTRadioAccessTechnology"]) {
                    if (radio.length > 0) {
                        [radio appendString:@","];
                    }
                    [radio appendString:[value substringFromIndex:23]];
                }
            }];
            return radio.length == 0 ? @"None" : [radio copy];
        } else {
            NSString *radio = nil;
            if (@available(iOS 12, *)) {
                radio = [[telephonyInfo.serviceCurrentRadioAccessTechnology allValues] firstObject];
            } else {
                radio = telephonyInfo.currentRadioAccessTechnology;
            }
            if (!radio) {
                radio = @"None";
            } else if ([radio hasPrefix:@"CTRadioAccessTechnology"]) {
                radio = [radio substringFromIndex:23];
            }
            return radio;
        }
    }
#endif
    return @"";
}

- (NSString *)libVersion
{
    return [Mixpanel libVersion];
}

+ (NSString *)libVersion
{
    return VERSION;
}

- (NSDictionary *)collectDeviceProperties
{
#if TARGET_OS_WATCH
    return [MixpanelWatchProperties collectDeviceProperties];
#elif TARGET_OS_OSX
    CGSize size = [NSScreen mainScreen].frame.size;
    return @{
             @"$os": @"macOS",
             @"$os_version": [NSProcessInfo processInfo].operatingSystemVersionString,
             @"$screen_height": @((NSInteger)size.height),
             @"$screen_width": @((NSInteger)size.width),
             };
#else
    UIDevice *device = [UIDevice currentDevice];
    CGSize size = [UIScreen mainScreen].bounds.size;
    return @{
             @"$os": [device systemName],
             @"$os_version": [device systemVersion],
             @"$screen_height": @((NSInteger)size.height),
             @"$screen_width": @((NSInteger)size.width),
             };
#endif
}

- (NSDictionary *)collectAutomaticProperties
{
    NSMutableDictionary *p = [NSMutableDictionary dictionary];
    id deviceModel = [self deviceModel] ? : [NSNull null];

    // Use setValue semantics to avoid adding keys where value can be nil.
    [p setValue:[[NSBundle mainBundle] infoDictionary][@"CFBundleVersion"] forKey:@"$app_version"];
    [p setValue:[[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] forKey:@"$app_release"];
    [p setValue:[[NSBundle mainBundle] infoDictionary][@"CFBundleVersion"] forKey:@"$app_build_number"];
    [p setValue:[[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] forKey:@"$app_version_string"];

#if !MIXPANEL_NO_REACHABILITY_SUPPORT
    if (![Mixpanel isAppExtension]) {
        CTCarrier *carrier = nil;
        if (@available(iOS 12, *)) {
            NSArray *carriers = [[telephonyInfo serviceSubscriberCellularProviders] allValues];
            // Find the first carrier object that has a non-empty name
            for (CTCarrier *carrierCandidate in carriers) {
                if (carrierCandidate.carrierName.length != 0) {
                    carrier = carrierCandidate;
                    break;
                }
            }
            // Use the first object as fallback in case there are no carriers with a name
            if (carrier == nil) {
                carrier = carriers.firstObject;
            }
        } else {
            carrier = [telephonyInfo subscriberCellularProvider];
        }
        [p setValue:carrier.carrierName forKey:@"$carrier"];
    }
#endif

    [p addEntriesFromDictionary:@{
                                  @"mp_lib": @"iphone",
                                  @"$lib_version": [self libVersion],
                                  @"$manufacturer": @"Apple",
                                  @"$model": deviceModel,
                                  @"mp_device_model": deviceModel, //legacy
                                  }];
    [p addEntriesFromDictionary:[self collectDeviceProperties]];
    return [p copy];
}

#pragma mark - UIApplication Events

#if !TARGET_OS_OSX
- (void)setUpListeners
{
    if (![Mixpanel isAppExtension]) {
#if !MIXPANEL_NO_REACHABILITY_SUPPORT
        // wifi reachability
        if ((_reachability = SCNetworkReachabilityCreateWithName(NULL, "api.mixpanel.com")) != NULL) {
            SCNetworkReachabilityContext context = {0, (__bridge void*)self, NULL, NULL, NULL};
            if (SCNetworkReachabilitySetCallback(_reachability, MixpanelReachabilityCallback, &context)) {
                if (!SCNetworkReachabilitySetDispatchQueue(_reachability, self.serialQueue)) {
                    // cleanup callback if setting dispatch queue failed
                    SCNetworkReachabilitySetCallback(_reachability, NULL, NULL);
                }
            }
        }

        // cellular info
        [self setCurrentRadio];
        // Temporarily remove the ability to monitor the radio change due to a crash issue might relate to the api from Apple
        // https://openradar.appspot.com/46873673
//        [[NSNotificationCenter defaultCenter] addObserver:self
//                                                 selector:@selector(setCurrentRadio)
//                                                     name:CTRadioAccessTechnologyDidChangeNotification
//                                                   object:nil];
#endif // MIXPANEL_NO_REACHABILITY_SUPPORT

#if !MIXPANEL_NO_APP_LIFECYCLE_SUPPORT
        NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];

        // Application lifecycle events
        [notificationCenter addObserver:self
                               selector:@selector(applicationWillResignActive:)
                                   name:UIApplicationWillResignActiveNotification
                                 object:nil];
        [notificationCenter addObserver:self
                               selector:@selector(applicationDidBecomeActive:)
                                   name:UIApplicationDidBecomeActiveNotification
                                 object:nil];
        [notificationCenter addObserver:self
                               selector:@selector(applicationDidEnterBackground:)
                                   name:UIApplicationDidEnterBackgroundNotification
                                 object:nil];
        [notificationCenter addObserver:self
                               selector:@selector(applicationWillEnterForeground:)
                                   name:UIApplicationWillEnterForegroundNotification
                                 object:nil];
#endif // MIXPANEL_NO_APP_LIFECYCLE_SUPPORT
    }
}
#else
- (void)setUpListeners
{
    NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];

    // Application lifecycle events
    [notificationCenter addObserver:self
                           selector:@selector(applicationWillResignActive:)
                               name:NSApplicationWillResignActiveNotification
                             object:nil];
    [notificationCenter addObserver:self
                           selector:@selector(applicationDidBecomeActive:)
                               name:NSApplicationDidBecomeActiveNotification
                             object:nil];
}
#endif

#if !MIXPANEL_NO_REACHABILITY_SUPPORT

static void MixpanelReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info)
{
    Mixpanel *mixpanel = (__bridge Mixpanel *)info;
    if (mixpanel && [mixpanel isKindOfClass:[Mixpanel class]]) {
        [mixpanel reachabilityChanged:flags];
    }
}

- (void)reachabilityChanged:(SCNetworkReachabilityFlags)flags
{
    // this should be run in the serial queue. the reason we don't dispatch_async here
    // is because it's only ever called by the reachability callback, which is already
    // set to run on the serial queue. see SCNetworkReachabilitySetDispatchQueue in init
    NSMutableDictionary *properties = [self.automaticProperties mutableCopy];
    if (properties) {
        BOOL wifi = (flags & kSCNetworkReachabilityFlagsReachable) && !(flags & kSCNetworkReachabilityFlagsIsWWAN);
        properties[@"$wifi"] = @(wifi);
        self.automaticProperties = [properties copy];
        MPLogInfo(@"%@ reachability changed, wifi=%d", self, wifi);
    }
}

#endif // MIXPANEL_NO_REACHABILITY_SUPPORT

#if !MIXPANEL_NO_APP_LIFECYCLE_SUPPORT

- (void)applicationDidBecomeActive:(NSNotification *)notification
{
    MPLogInfo(@"%@ application did become active", self);
    [self startFlushTimer];
}

- (void)applicationWillResignActive:(NSNotification *)notification
{
    MPLogInfo(@"%@ application will resign active", self);
    [self stopFlushTimer];

#if TARGET_OS_OSX
    if (self.flushOnBackground) {
        [self flush];
    } else {
        dispatch_async(self.serialQueue, ^{
            [self archive];
        });
    }
#endif
}

#if !TARGET_OS_OSX
- (void)applicationDidEnterBackground:(NSNotification *)notification
{
    MPLogInfo(@"%@ did enter background", self);
    if ([self hasOptedOutTracking]) {
        return;
    }

    __block UIBackgroundTaskIdentifier backgroundTask = [[Mixpanel sharedUIApplication] beginBackgroundTaskWithExpirationHandler:^{
        MPLogInfo(@"%@ flush %lu cut short", self, (unsigned long) backgroundTask);
        [[Mixpanel sharedUIApplication] endBackgroundTask:backgroundTask];
        self.taskId = UIBackgroundTaskInvalid;
    }];
    self.taskId = backgroundTask;
    MPLogInfo(@"%@ starting background cleanup task %lu", self, (unsigned long)self.taskId);

    dispatch_group_t bgGroup = dispatch_group_create();
    NSString *trackedKey = [NSString stringWithFormat:@"MPTracked:%@", self.apiToken];
    if (![[NSUserDefaults standardUserDefaults] boolForKey:trackedKey]) {
        dispatch_group_enter(bgGroup);
        [self sendHttpEvent:@"Integration"
                   apiToken:@"85053bf24bba75239b16a601d9387e17"
                 distinctId:self.apiToken
                 properties:@{}
               updatePeople:NO
          completionHandler:^(NSData *responseData,
                              NSURLResponse *urlResponse,
                              NSError *error) {
            if (!error) {
                [[NSUserDefaults standardUserDefaults] setBool:YES forKey:trackedKey];
            }
            dispatch_group_leave(bgGroup);
        }];
    }

    if (self.flushOnBackground) {
        dispatch_group_enter(bgGroup);
        [self flushWithCompletion:^{
            dispatch_group_leave(bgGroup);
        }];
    }
    dispatch_group_notify(bgGroup, dispatch_get_main_queue(), ^{
        MPLogInfo(@"%@ ending background cleanup task %lu", self, (unsigned long)self.taskId);
        if (self.taskId != UIBackgroundTaskInvalid) {
            [[Mixpanel sharedUIApplication] endBackgroundTask:self.taskId];
            self.taskId = UIBackgroundTaskInvalid;
        }
    });
}

- (void)applicationWillEnterForeground:(NSNotificationCenter *)notification
{
    MPLogInfo(@"%@ will enter foreground", self);
    dispatch_async(self.serialQueue, ^{
        [self.sessionMetadata reset];
        if (self.taskId != UIBackgroundTaskInvalid) {
            [[Mixpanel sharedUIApplication] endBackgroundTask:self.taskId];
            self.taskId = UIBackgroundTaskInvalid;
            [self.network updateNetworkActivityIndicator:NO];
        }
    });
}

#endif // MIXPANEL_MACOS

#endif // MIXPANEL_NO_APP_LIFECYCLE_SUPPORT

- (void)sendHttpEvent:(NSString *)eventName
             apiToken:(NSString *)apiToken
           distinctId:(NSString *)distinctId
{
    [self sendHttpEvent:eventName apiToken:apiToken distinctId:distinctId properties:@{} updatePeople: YES completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {}];
}

- (void)sendHttpEvent:(NSString *)eventName
             apiToken:(NSString *)apiToken
           distinctId:(NSString *)distinctId
           properties:(NSDictionary *)properties
{
    [self sendHttpEvent:eventName apiToken:apiToken distinctId:distinctId properties:properties updatePeople: YES completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {}];
}

- (void)sendHttpEvent:(NSString *)eventName
             apiToken:(NSString *)apiToken
           distinctId:(NSString *)distinctId
           properties:(NSDictionary *)properties
         updatePeople:(BOOL)updatePeople
    completionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler
{
    NSMutableDictionary *trackingProperties = [[NSMutableDictionary alloc] initWithDictionary:@{@"token": apiToken, @"mp_lib": @"iphone", @"distinct_id": distinctId, @"$lib_version": self.libVersion, @"Logging Enabled": @(self.enableLogging), @"Project Token": distinctId, @"DevX": @YES}];
    [trackingProperties addEntriesFromDictionary:properties];
    NSString *requestData = [MPJSONHandler encodedJSONString:@[@{@"event": eventName, @"properties": trackingProperties}]];
    NSURLQueryItem *useIPAddressForGeoLocation = [NSURLQueryItem queryItemWithName:@"ip" value:self.useIPAddressForGeoLocation ? @"1": @"0"];
    NSURLRequest *request = [self.network buildPostRequestForEndpoint:MPNetworkEndpointTrack withQueryItems:@[useIPAddressForGeoLocation] andBody:requestData];
    [[[MPNetwork sharedURLSession] dataTaskWithRequest:request completionHandler:completionHandler] resume];
    if (updatePeople) {
        NSString *engageData = [MPJSONHandler encodedJSONString:@[@{@"$token": apiToken, @"$distinct_id": distinctId, @"$add": @{eventName: @1}}]];
        NSURLRequest *engageRequest = [self.network buildPostRequestForEndpoint:MPNetworkEndpointEngage withQueryItems:@[useIPAddressForGeoLocation] andBody:engageData];
        [[[MPNetwork sharedURLSession] dataTaskWithRequest:engageRequest completionHandler:completionHandler] resume];
    }
}

#pragma mark - Logging
- (void)setEnableLogging:(BOOL)enableLogging
{
    [MPLogger sharedInstance].loggingEnabled = enableLogging;
#if defined(DEBUG)
    [self sendHttpEvent:@"Toggle SDK Logging" apiToken:@"metrics-1" distinctId:self.apiToken properties:@{@"Logging Enabled": @(enableLogging)}];
#endif
}

- (BOOL)enableLogging
{
    return [MPLogger sharedInstance].loggingEnabled;
}

@end


text:
#ifndef _KERNEL
#include <signal.h>
#endif

struct uwx_ttrace_info;

extern struct uwx_ttrace_info *uwx_ttrace_init_info(
    struct uwx_env *env,
    pid_t pid,
    lwpid_t lwpid,
    uint64_t load_map);

extern int uwx_ttrace_free_info(struct uwx_ttrace_info *info);

extern int uwx_ttrace_init_context(
    struct uwx_env *env,
    struct uwx_ttrace_info *info);

extern int uwx_ttrace_init_from_sigcontext(
    struct uwx_env *env,
    struct uwx_ttrace_info *info,
    uint64_t ucontext);

extern int uwx_ttrace_do_context_frame(
    struct uwx_env *env,
    struct uwx_ttrace_info *info);

extern int uwx_ttrace_copyin(
    int request,
    char *loc,
    uint64_t rem,
    int len,
    intptr_t tok);

extern int uwx_ttrace_lookupip(
    int request,
    uint64_t ip,
    intptr_t tok,
    uint64_t **resultp);

#define UWX_TT_ERR_BADABICONTEXT	(-101)
#define UWX_TT_ERR_TTRACE		(-102)

redpajama_set_name: RedPajamaGithub


text:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using VGA.Mutations.Mutators;

namespace VGA.Mutations
{
    public class MutationRunner
    {
        private readonly string _methodName;
        private readonly List<Mutator> _mutators;
        private readonly Type _typeToMutate;

        private string _assemblyPath;
        private ITestRunner _testRunner;

        public MutationRunner(Type typeToMutate, string methodName)
        {
            _typeToMutate = typeToMutate;
            _methodName = methodName;
            _assemblyPath = new Uri(_typeToMutate.Assembly.CodeBase).LocalPath;

            _mutators = new List<Mutator>
            {
                new ArithmeticMutator(),
                new BooleanMutator()
            };

            _testRunner = new NUnitTestRunner();
        }

        public MutationRunner WithTestRunner(ITestRunner testRunner)
        {
            _testRunner = testRunner;
            return this;
        }

        public List<MutationResult> Run()
        {
            string tempPath = CopyFilesToTemporaryFolder();

            _assemblyPath = Path.Combine(tempPath, Path.GetFileName(_assemblyPath));

            var mutationResults = new List<MutationResult>();
            var methodToMutate = new MethodToMutate(_assemblyPath, _typeToMutate, _methodName);

            _mutators.ForEach(m => mutationResults.AddRange(m.Mutate(methodToMutate, _testRunner)));

            var result = new StringBuilder();
            mutationResults.ForEach(mr => result.AppendLine(mr.ToString()));

            Debug.Write(result.ToString());

            if (mutationResults.Any(mr => mr.TestResults.Any(tr => tr.Killed == false)))
            {
                throw new MutationTestFailedException(result.ToString(), mutationResults);
            }

            return mutationResults;
        }

        private static string CopyFilesToTemporaryFolder()
        {
            string tempFolder = Guid.NewGuid().ToString();
            string tempPath = Path.Combine(Path.GetTempPath(), tempFolder);
            Directory.CreateDirectory(tempPath);

            foreach (var file in Directory.GetFiles("."))
            {
                File.Copy(file, Path.Combine(tempPath, file));
            }
            return tempPath;
        }
    }

    public class MutationTestFailedException : Exception
    {
        public MutationTestFailedException(string message, List<MutationResult> results) : base(message)
        {
            Results = results;
        }

        public List<MutationResult> Results { get; private set; }
    }
}


text:
import React, { Component } from 'react';
import {
    Button,
    ScrollView,
    View,
    KeyboardAvoidingView,
    Picker,
    TextInput,
    StyleSheet,
    StatusBar
} from 'react-native';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { NavigationActions } from 'react-navigation';
import * as bottleActions from '../BottleActions';
import GrowingTextInput from '../../components/GrowingTextInput';

class AddBottle extends Component {
    static navigationOptions = {
        title: 'Add Bottle'
    };

    constructor(props) {
        super(props);

        this.state = {
            bottle: {
                distillery: '',
                name: '',
                age: '',
                description: ''
            }
        };

        this._handleOnAddPress = this._handleOnAddPress.bind(this);
        this._handleOnChange = this._handleOnChange.bind(this);
    }

    _handleOnChange = (property, value) => {
        let { bottle } = this.state;
        bottle[property] = value;

        this.setState({ bottle });
    };

    _handleOnAddPress = () => {
        let { addBottle } = this.props.actions;
        let { bottle } = this.state;
        let { event } = this.props.navigation.state.params;

        addBottle(bottle, event);
        const backAction = NavigationActions.back({
        });

        this.props.navigation.dispatch(backAction);
    };

    render() {
        let { bottle } = this.state;
        let { members } = this.props;

        return (
            <View>
                <KeyboardAvoidingView behavior="padding" style={{ paddingVertical: 20 }}>
                    <View style={[styles.row, styles.firstRow]}>
                        <TextInput
                            ref={view => {
                                this.distillery = view;
                            }}
                            value={bottle.distillery}
                            placeholder="Distillery"
                            onChangeText={this._handleOnChange.bind(this, 'distillery')}
                            style={styles.textInput}
                            autoCapitalize="words"
                            autoCorrect={false}
                            blurOnSubmit={false}
                            returnKeyType="next"
                            onSubmitEditing={async () => {
                                this._locationInput.focus();
                            }} />
                    </View>
                    <View style={styles.row}>
                        <TextInput
                            ref={view => {
                                this.name = view;
                            }}
                            value={bottle.name}
                            placeholder="Name"
                            onChangeText={this._handleOnChange.bind(this, 'name')}
                            style={styles.textInput}
                            autoCapitalize="words"
                            autoCorrect={false}
                            blurOnSubmit={false}
                            returnKeyType="next" />
                    </View>
                    <View style={styles.row}>
                        <TextInput
                            ref={view => {
                                this.name = view;
                            }}
                            value={bottle.age}
                            placeholder="Age"
                            onChangeText={this._handleOnChange.bind(this, 'age')}
                            style={styles.textInput}
                            autoCapitalize="words"
                            autoCorrect={false}
                            blurOnSubmit={false}
                            returnKeyType="next" />
                    </View>
                </KeyboardAvoidingView>
                <KeyboardAvoidingView behavior="padding" style={{ paddingVertical: 20 }}>
                    <View style={styles.row}>
                        <GrowingTextInput
                            placeholder='Description'
                            ref={view => { this._descriptionInput = view; }}
                            minHeight={60}
                            value={bottle.description}
                            onChangeText={this._handleOnChange.bind(this, 'description')}
                            autoCapitalize="sentences"
                            autoCorrect={false}
                            blurOnSubmit={false}
                            returnKeyType="next"
                            style={styles.textInput} />
                    </View>
                </KeyboardAvoidingView>
                <KeyboardAvoidingView behavior="padding" style={{ paddingVertical: 20 }}>
                    <Button title="Add Bottle" onPress={this._handleOnAddPress} color="#009688" />
                </KeyboardAvoidingView>
                <StatusBar barStyle="light-content" />
            </View>
        );
    }
}

function mapStateToProps(state) {
    return state;
}

function mapDispatchToProps(dispatch) {
    return { actions: bindActionCreators(bottleActions, dispatch) };
}

export default connect(mapStateToProps, mapDispatchToProps)(AddBottle);

const styles = StyleSheet.create({
    headerText: {
        fontSize: 18,
        color: '#fff',
        fontWeight: '600'
    },
    row: {
        backgroundColor: '#fff',
        borderBottomWidth: StyleSheet.hairlineWidth,
        borderColor: '#ccc',
        display: 'flex',
        flexDirection: 'row',
        justifyContent: 'space-between',
        padding: 5,
        alignItems: 'center'
    },
    label: {
        marginLeft: 15,
        fontSize: 16
    },
    firstRow: {
        borderTopWidth: StyleSheet.hairlineWidth,
        borderColor: '#ccc'
    },
    textInput: {
        flex: 1,
        height: 45,
        paddingHorizontal: 15,
        fontSize: 16
    },
    growingTextInput: {
        paddingHorizontal: 15,
        paddingVertical: 15,
        fontSize: 16
    }
});


text:
package org.datasays.wes.actions;

import okhttp3.HttpUrl;
import org.datasays.wes.core.RequestInfo;
import org.datasays.wes.types.*;
// documentation: https://www.elastic.co/guide/en/elasticsearch/reference/5.x/docs-multi-get.html
public class Mget extends RequestInfo{

	public Mget(String baseUrl){
		super(baseUrl);
	}
	public Mget(HttpUrl baseUrl){
		super(baseUrl);
	}

	// param: list storedFields: A comma-separated list of stored fields to return in the response
	public Mget storedFields(String storedFields){
		addParams("storedFields", storedFields);
		return this;
	}
	// param: string preference: Specify the node or shard the operation should be performed on (default: random)
	public Mget preference(String preference){
		addParams("preference", preference);
		return this;
	}
	// param: boolean realtime: Specify whether to perform the operation in realtime or search mode
	public Mget realtime(boolean realtime){
		addParams("realtime", realtime);
		return this;
	}
	// param: boolean refresh: Refresh the shard containing the document before performing the operation
	public Mget refresh(boolean refresh){
		addParams("refresh", refresh);
		return this;
	}
	// param: string routing: Specific routing value
	public Mget routing(String routing){
		addParams("routing", routing);
		return this;
	}
	// param: list source: True or false to return the _source field or not, or a list of fields to return
	public Mget source(String source){
		addParams("source", source);
		return this;
	}
	// param: list sourceExclude: A list of fields to exclude from the returned _source field
	public Mget sourceExclude(String sourceExclude){
		addParams("sourceExclude", sourceExclude);
		return this;
	}
	// param: list sourceInclude: A list of fields to extract and return from the _source field
	public Mget sourceInclude(String sourceInclude){
		addParams("sourceInclude", sourceInclude);
		return this;
	}
	// body*:Document identifiers; can be either `docs` (containing full document information) or `ids` (when index and type is provided in the URL.
	@Override
	public void setBody(Object body) {
		super.setBody(body);
	}

	//The name of the index
	private String index;
	//The type of the document
	private String type;
	public Mget setParts(String index,String type){
		this.index=index;
		this.type=type;

		return this;
	}

	@Override
	public String parseUrl(String method) {
		if(!"GET".equalsIgnoreCase(method) && !"POST".equalsIgnoreCase(method)){
			throw new IllegalArgumentException("Unsupported method:"+method);
		}
		//=>/{index}/{type}/_mget
		if(index != null && type != null ){
			setUrl(index, type, "_mget");
			return super.parseUrl(method);
		}
		//=>/{index}/_mget
		if(index != null ){
			setUrl(index, "_mget");
			return super.parseUrl(method);
		}
		//=>/_mget
		setUrl("_mget");
		return super.parseUrl(method);

	}
}


text:
package com.mygdx.pmd.utils;

/**
 * Created by Cameron on 11/19/2016.
 */

public class MathLogic {

    public static double calculateDistance(int x1, int y1, int x2, int y2){
        int xOffset = x2-x1;
        int yOffset = y2-y1;

        return Math.sqrt((xOffset*xOffset) + (yOffset*yOffset));
    }

    public static int random(int floor, int ceiling){
        return (int)(Math.random()*(ceiling+1)) + floor;
    }
}


text:
//
//  HWAccount.m
//  黑马微博
//
//  Created by 弓苗苗 on 2016/11/28.
//  Copyright © 2016年 弓苗苗. All rights reserved.
//

#import "HWAccount.h"

@implementation HWAccount

+(instancetype)accountWithDict:(NSDictionary *)dict{
    HWAccount *account = [[HWAccount alloc] init];
    account.access_token = dict[@"access_token"];
    account.expires_in = dict[@"expires_in"];
    account.uid = dict[@"uid"];
    account.create_time = [NSDate date];
    return account;
}

/**
 当一个对象要归档进沙盒时,就会调用这个方法
 目的:在这个方法中说明这个对象的那些属性要存进沙盒
 */
-(void)encodeWithCoder:(NSCoder *)encoder{
    [encoder encodeObject:self.access_token forKey:@"access_token"];
    [encoder encodeObject:self.expires_in forKey:@"expires_in"];
    [encoder encodeObject:self.uid forKey:@"uid"];
    [encoder encodeObject:self.create_time forKey:@"create_time"];
    [encoder encodeObject:self.name forKey:@"name"];
}

/**
 当从沙盒中解档一个对象时(从沙盒中加载一个对象),就会调用这个方法
 目的:在这个方法中说明沙盒中的属性改怎么解析(需要取出那些属性)
 */
-(instancetype)initWithCoder:(NSCoder *)decoder{
    if (self = [super init]) {
        self.access_token = [decoder decodeObjectForKey:@"access_token"];
        self.expires_in = [decoder decodeObjectForKey:@"expires_in"];
        self.uid = [decoder decodeObjectForKey:@"uid"];
        self.create_time = [decoder decodeObjectForKey:@"create_time"];
        self.name = [decoder decodeObjectForKey:@"name"];
    }
    return self;
}
@end


text:
{% load i18n %}
{% load difftags %}
{% load djblets_deco %}
{% load djblets_utils %}
{% if standalone and error %}
{{error}}
{% endif %}

{% if file.changed_chunk_indexes or file.binary or file.deleted or file.moved %}
{%  if not standalone %}

 <colgroup>
  <col class="line" />
  <col class="left" />
  <col class="line" />
  <col class="right" />
 </colgroup>

{%   if file.dest_filename == file.depot_filename %}
   {{ file.depot_filename }}
{%   else %}
   {{ file.depot_filename }}
   {{ file.dest_filename }}{% if file.moved %}{% trans " (moved)" %}{% endif %}
{%   endif %}{# file.dest_filename == file.depot_filename #}

{%   if file.moved and file.num_changes == 0 %}
   {% trans "No changes were made to this file." %}
{%   else %}
   {{file.revision}}
   {{file.dest_revision}}
{%   endif %}{# num_changes and moved #}

{%  endif %}{# not standalone #}
{%  if file.binary %}

   {% trans "This is a binary file. The content cannot be displayed." %}

{%  elif file.deleted %}

   {% trans "This file was deleted. The content cannot be displayed." %}

{%  elif file.whitespace_only %}

      {% trans "This file contains only whitespace changes." %}

{%  else %}
{%   for chunk in file.chunks %}
{%    if not chunk.collapsable or not collapseall %}

{%     for line in chunk.lines %}

{%      if forloop.first %}
   {% if chunk.change != 'equal' %}{% endif %}{{line.1}}
{%      else %}
   {{line.1}}
{%      endif %}
{%      if chunk.change == "replace" %}

{{ line.2|highlightregion:line.3|showextrawhitespace }}

   {{line.4}}

{{ line.5|highlightregion:line.6|showextrawhitespace }}

{%      else %}
   {% if chunk.change == 'insert' and line.8 %}
    {% trans "Moved from" %} {{line.8}}
    {% endif %}

{{line.2|showextrawhitespace}}

   {{line.4}}
   {% if chunk.change == 'delete' and line.8 %}
    {% trans "Moved to" %} {{line.8}}
    {% endif %}

{{line.5|showextrawhitespace}}

{%      endif %}

{%     endfor %}

{%    else %}

   ...
   {{chunk.numlines}} line{{chunk.numlines|pluralize}} hidden [{% trans "Expand" %}]

{% if chunk.meta.headers %}

{%  if chunk.meta.headers.0 == chunk.meta.headers.1 %}

{{chunk.meta.headers.0}}

{%  else %}

{{chunk.meta.headers.0}}

{{chunk.meta.headers.1}}

{%  endif %}

{% endif %}

{%       endif %}
{%      endfor %}{# chunks #}
{%     endif %}{# file deleted, binary and whitespace_only #}
{%  if not standalone %}

{%  endif %}{# not standalone #}
{% else %}{# No changed chunks and not a binary file #}
{%  if not standalone %}

{%  endif %}{# not standalone #}
{% endif %}{# No changed chunks and not a binary file #}


text:
r"""
This code was generated by
\ / _    _  _|   _  _
 | (_)\/(_)(_|\/| |(/_  v1.0.0
      /       /
"""

from tests import IntegrationTestCase
from tests.holodeck import Request
from twilio.base.exceptions import TwilioException
from twilio.http.response import Response

class TaskTestCase(IntegrationTestCase):

    def test_fetch_request(self):
        self.holodeck.mock(Response(500, ''))

        with self.assertRaises(TwilioException):
            self.client.taskrouter.v1.workspaces("WSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX") \
                                     .tasks("WTXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX").fetch()

        self.holodeck.assert_has_request(Request(
            'get',
            'https://taskrouter.twilio.com/v1/Workspaces/WSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/Tasks/WTXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
        ))

    def test_fetch_response(self):
        self.holodeck.mock(Response(
            200,
            '''
            {
                "account_sid": "ACaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                "age": 25200,
                "assignment_status": "pending",
                "attributes": "{\\"body\\": \\"hello\\"}",
                "date_created": "2014-05-14T18:50:02Z",
                "date_updated": "2014-05-15T07:26:06Z",
                "task_queue_entered_date": "2014-05-14T18:50:02Z",
                "priority": 0,
                "reason": "Test Reason",
                "sid": "WTaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                "task_queue_sid": "WQaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                "task_channel_sid": "TCaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                "task_channel_unique_name": "task-channel",
                "timeout": 60,
                "url": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Tasks/WTaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                "workflow_sid": "WWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                "workspace_sid": "WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                "workflow_friendly_name": "Test Workflow",
                "task_queue_friendly_name": "Test Queue",
                "addons": "{}",
                "links": {
                    "task_queue": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/TaskQueues/WQaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                    "workflow": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Workflows/WWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                    "workspace": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                    "reservations": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Tasks/WTaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Reservations"
                }
            }
            '''
        ))

        actual = self.client.taskrouter.v1.workspaces("WSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX") \
                                          .tasks("WTXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX").fetch()

        self.assertIsNotNone(actual)

    def test_update_request(self):
        self.holodeck.mock(Response(500, ''))

        with self.assertRaises(TwilioException):
            self.client.taskrouter.v1.workspaces("WSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX") \
                                     .tasks("WTXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX").update(if_match="if_match")

        headers = {'If-Match': "if_match", }
        self.holodeck.assert_has_request(Request(
            'post',
            'https://taskrouter.twilio.com/v1/Workspaces/WSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/Tasks/WTXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
            headers=headers,
        ))

    def test_update_response(self):
        self.holodeck.mock(Response(
            200,
            '''
            {
                "account_sid": "ACaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                "age": 25200,
                "assignment_status": "pending",
                "attributes": "{\\"body\\": \\"hello\\"}",
                "date_created": "2014-05-14T18:50:02Z",
                "date_updated": "2014-05-15T07:26:06Z",
                "task_queue_entered_date": "2014-05-14T18:50:02Z",
                "priority": 0,
                "reason": "Test Reason",
                "sid": "WTaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                "task_queue_sid": "WQaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                "task_channel_sid": "TCaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                "task_channel_unique_name": "task-channel",
                "timeout": 60,
                "url": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Tasks/WTaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                "workflow_sid": "WWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                "workspace_sid": "WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                "workflow_friendly_name": "Test Workflow",
                "task_queue_friendly_name": "Test Queue",
                "addons": "{}",
                "links": {
                    "task_queue": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/TaskQueues/WQaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                    "workflow": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Workflows/WWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                    "workspace": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                    "reservations": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Tasks/WTaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Reservations"
                }
            }
            '''
        ))

        actual = self.client.taskrouter.v1.workspaces("WSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX") \
                                          .tasks("WTXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX").update()

        self.assertIsNotNone(actual)

    def test_delete_request(self):
        self.holodeck.mock(Response(500, ''))

        with self.assertRaises(TwilioException):
            self.client.taskrouter.v1.workspaces("WSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX") \
                                     .tasks("WTXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX").delete(if_match="if_match")

        headers = {'If-Match': "if_match", }
        self.holodeck.assert_has_request(Request(
            'delete',
            'https://taskrouter.twilio.com/v1/Workspaces/WSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/Tasks/WTXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
            headers=headers,
        ))

    def test_delete_response(self):
        self.holodeck.mock(Response(
            204,
            None,
        ))

        actual = self.client.taskrouter.v1.workspaces("WSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX") \
                                          .tasks("WTXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX").delete()

        self.assertTrue(actual)

    def test_list_request(self):
        self.holodeck.mock(Response(500, ''))

        with self.assertRaises(TwilioException):
            self.client.taskrouter.v1.workspaces("WSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX") \
                                     .tasks.list()

        self.holodeck.assert_has_request(Request(
            'get',
            'https://taskrouter.twilio.com/v1/Workspaces/WSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/Tasks',
        ))

    def test_read_full_response(self):
        self.holodeck.mock(Response(
            200,
            '''
            {
                "meta": {
                    "first_page_url": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Tasks?TaskQueueSid=WQaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&Priority=1&TaskQueueName=task_queue_name&WorkflowName=workflow_name&WorkflowSid=WWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&AssignmentStatus=pending%2Creserved&PageSize=50&Page=0",
                    "key": "tasks",
                    "next_page_url": null,
                    "page": 0,
                    "page_size": 50,
                    "previous_page_url": null,
                    "url": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Tasks?TaskQueueSid=WQaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&Priority=1&TaskQueueName=task_queue_name&WorkflowName=workflow_name&WorkflowSid=WWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&AssignmentStatus=pending%2Creserved&PageSize=50&Page=0"
                },
                "tasks": [
                    {
                        "account_sid": "ACaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                        "age": 25200,
                        "assignment_status": "pending",
                        "attributes": "{\\"body\\": \\"hello\\"}",
                        "date_created": "2014-05-14T14:26:54Z",
                        "date_updated": "2014-05-15T16:03:42Z",
                        "task_queue_entered_date": "2014-05-14T14:26:54Z",
                        "priority": 0,
                        "reason": "Test Reason",
                        "sid": "WTaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                        "task_queue_sid": "WQaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                        "task_channel_sid": "TCaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                        "task_channel_unique_name": "task-channel",
                        "timeout": 60,
                        "url": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Tasks/WTaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                        "workflow_sid": "WWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                        "workspace_sid": "WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                        "workflow_friendly_name": "Test Workflow",
                        "task_queue_friendly_name": "Test Queue",
                        "addons": "{}",
                        "links": {
                            "task_queue": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/TaskQueues/WQaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                            "workflow": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Workflows/WWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                            "workspace": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                            "reservations": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Tasks/WTaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Reservations"
                        }
                    }
                ]
            }
            '''
        ))

        actual = self.client.taskrouter.v1.workspaces("WSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX") \
                                          .tasks.list()

        self.assertIsNotNone(actual)

    def test_read_empty_response(self):
        self.holodeck.mock(Response(
            200,
            '''
            {
                "meta": {
                    "first_page_url": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Tasks?TaskQueueSid=WQaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&Priority=1&TaskQueueName=task_queue_name&WorkflowName=workflow_name&WorkflowSid=WWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&AssignmentStatus=pending&PageSize=50&Page=0",
                    "key": "tasks",
                    "next_page_url": null,
                    "page": 0,
                    "page_size": 50,
                    "previous_page_url": null,
                    "url": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Tasks?TaskQueueSid=WQaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&Priority=1&TaskQueueName=task_queue_name&WorkflowName=workflow_name&WorkflowSid=WWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&AssignmentStatus=pending&PageSize=50&Page=0"
                },
                "tasks": []
            }
            '''
        ))

        actual = self.client.taskrouter.v1.workspaces("WSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX") \
                                          .tasks.list()

        self.assertIsNotNone(actual)

    def test_read_assignment_status_multiple_response(self):
        self.holodeck.mock(Response(
            200,
            '''
            {
                "meta": {
                    "first_page_url": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Tasks?TaskQueueSid=WQaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&Priority=1&TaskQueueName=task_queue_name&WorkflowName=workflow_name&WorkflowSid=WWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&AssignmentStatus=pending&PageSize=50&Page=0",
                    "key": "tasks",
                    "next_page_url": null,
                    "page": 0,
                    "page_size": 50,
                    "previous_page_url": null,
                    "url": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Tasks?TaskQueueSid=WQaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&Priority=1&TaskQueueName=task_queue_name&WorkflowName=workflow_name&WorkflowSid=WWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&AssignmentStatus=pending&PageSize=50&Page=0"
                },
                "tasks": []
            }
            '''
        ))

        actual = self.client.taskrouter.v1.workspaces("WSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX") \
                                          .tasks.list()

        self.assertIsNotNone(actual)

    def test_create_request(self):
        self.holodeck.mock(Response(500, ''))

        with self.assertRaises(TwilioException):
            self.client.taskrouter.v1.workspaces("WSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX") \
                                     .tasks.create()

        self.holodeck.assert_has_request(Request(
            'post',
            'https://taskrouter.twilio.com/v1/Workspaces/WSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/Tasks',
        ))

    def test_create_response(self):
        self.holodeck.mock(Response(
            201,
            '''
            {
                "account_sid": "ACaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                "age": 25200,
                "assignment_status": "pending",
                "attributes": "{\\"body\\": \\"attributes\\"}",
                "date_created": "2014-05-14T18:50:02Z",
                "date_updated": "2014-05-15T07:26:06Z",
                "task_queue_entered_date": null,
                "priority": 1,
                "reason": "Test Reason",
                "sid": "WTaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                "task_queue_sid": "WQaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                "task_channel_sid": "TCaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                "task_channel_unique_name": "unique",
                "timeout": 60,
                "url": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Tasks/WTaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                "workspace_sid": "WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                "workflow_sid": "WWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                "workflow_friendly_name": "Example Workflow",
                "task_queue_friendly_name": "Example Task Queue",
                "addons": "{}",
                "links": {
                    "task_queue": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/TaskQueues/WQaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                    "workflow": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Workflows/WWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                    "workspace": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                    "reservations": "https://taskrouter.twilio.com/v1/Workspaces/WSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Tasks/WTaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/Reservations"
                }
            }
            '''
        ))

        actual = self.client.taskrouter.v1.workspaces("WSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX") \
                                          .tasks.create()

        self.assertIsNotNone(actual)


text:
/**
 * Requires sticking binary operators to the right.
 *
 * Type: `Array` or `Boolean`
 *
 * Values: Array of quoted operators or `true` to disallow space after all possible binary operators
 *
 * #### Example
 *
 * ```js
 * "disallowSpaceAfterBinaryOperators": [
 *     "=",
 *     ",",
 *     "+",
 *     "-",
 *     "/",
 *     "*",
 *     "==",
 *     "===",
 *     "!=",
 *     "!=="
 *     // etc
 * ]
 * ```
 *
 * ##### Valid
 *
 * ```js
 * x +y;
 * ```
 *
 * ##### Invalid
 *
 * ```js
 * x+ y;
 * ```
 */

var assert = require('assert');
var allOperators = require('../utils').binaryOperators;

module.exports = function() {};

module.exports.prototype = {
    configure: function(operators) {
        var isTrue = operators === true;

        assert(
            Array.isArray(operators) || isTrue,
            'disallowSpaceAfterBinaryOperators option requires array or true value'
        );

        if (isTrue) {
            operators = allOperators;
        }

        this._operatorIndex = {};
        for (var i = 0, l = operators.length; i < l; i++) {
            this._operatorIndex[operators[i]] = true;
        }
    },

    getOptionName: function() {
        return 'disallowSpaceAfterBinaryOperators';
    },

    check: function(file, errors) {
        var operators = this._operatorIndex;

        // Comma
        if (operators[',']) {
            file.iterateTokensByType('Punctuator', function(token) {
                if (token.value === ',') {
                    errors.assert.noWhitespaceBetween({
                        token: token,
                        nextToken: file.getNextToken(token),
                        message: 'Operator , should stick to following expression'
                    });
                }
            });
        }

        // For everything else
        file.iterateNodesByType(
            ['BinaryExpression', 'AssignmentExpression', 'VariableDeclarator', 'LogicalExpression'],
            function(node) {
                var operator;
                var expression;

                if (node.type === 'VariableDeclarator') {
                    expression = node.init;
                    operator = '=';
                } else {
                    operator = node.operator;
                    expression = node.right;
                }

                if (expression === null) {
                    return;
                }

                var operatorToken = file.findPrevOperatorToken(
                    file.getFirstNodeToken(expression),
                    operator
                );

                var nextToken = file.getNextToken(operatorToken);

                if (operators[operator]) {
                    errors.assert.noWhitespaceBetween({
                        token: operatorToken,
                        nextToken: nextToken,
                        message: 'Operator ' + operator + ' should stick to following expression'
                    });
                }
            }
        );
    }

};


text:
require 'spec_helper'
require 'sqs_worker/sqs'

module SqsWorker
  describe Sqs do

    subject(:sqs) { described_class.clone.instance }

    let(:aws_sqs) { double(Aws::SQS::Resource, get_queue_by_name: queue) }
    let(:queue) { instance_double(Aws::SQS::Queue) }
    let(:wrapped_queue) { instance_double(Queue)}
    let(:queue_name) { 'test_queue' }

    describe '#find_queue' do

      before do
        allow(Aws::SQS::Resource).to receive(:new).and_return(aws_sqs)
        allow(Queue).to receive(:new).and_return(wrapped_queue)
      end

      it 'finds the queue using the correct queue name' do
        sqs.find_queue(queue_name)
        expect(aws_sqs).to have_received(:get_queue_by_name).with({queue_name: queue_name})
      end

      it 'creates the queue with the sqs queue and correct queue name' do
        sqs.find_queue(queue_name)
        expect(Queue).to have_received(:new).with(queue, queue_name)
      end

      it 'returns a wrapped queue instance' do
        expect(sqs.find_queue(queue_name)).to be(wrapped_queue)
      end

      context "when the queue doesn't exist" do

        before do
          allow(Queue).to receive(:new).and_raise(Aws::SQS::Errors::NonExistentQueue.new("context", "error message"))
        end

        it 'raises an error' do
          expect { sqs.find_queue('invalid') }.to raise_error(SqsWorker::Errors::NonExistentQueue)
        end
      end
    end
  end
end


text:
require 'spec_helper'
require 'puppet_spec/files'
require 'puppet/pops'
require 'puppet/loaders'

describe 'loader paths' do
  include PuppetSpec::Files
  before(:each) { Puppet[:biff] = true }

  let(:static_loader) { Puppet::Pops::Loader::StaticLoader.new() }
  let(:unused_loaders) { nil }

  describe 'the relative_path_for_types method' do
    it 'produces paths to load in precendence order' do
      module_dir = dir_containing('testmodule', {
        'functions' => {},
        'lib' => {
          'puppet' => {
            'functions' => {},
            'parser' => {
              'functions' => {},
            }
          }}})
      module_loader = Puppet::Pops::Loader::ModuleLoaders::FileBased.new(static_loader, unused_loaders, 'testmodule', module_dir, 'test1')

      effective_paths = Puppet::Pops::Loader::LoaderPaths.relative_paths_for_type(:function, module_loader)

      expect(effective_paths.collect(&:generic_path)).to eq([
        File.join(module_dir, 'lib', 'puppet', 'functions'), # 4x functions
        File.join(module_dir, 'lib', 'puppet','parser', 'functions') # 3x functions
      ])
    end

    it 'module loader has smart-paths that prunes unavailable paths' do
      module_dir = dir_containing('testmodule', {'lib' => {'puppet' => {'functions' => {'foo.rb' => 'Puppet::Functions.create_function("testmodule::foo") { def foo; end; }' }}}})
      module_loader = Puppet::Pops::Loader::ModuleLoaders::FileBased.new(static_loader, unused_loaders, 'testmodule', module_dir, 'test1')

      effective_paths = module_loader.smart_paths.effective_paths(:function)

      expect(effective_paths.size).to be_eql(1)
      expect(effective_paths[0].generic_path).to be_eql(File.join(module_dir, 'lib', 'puppet', 'functions'))
      expect(module_loader.path_index.size).to be_eql(1)
      expect(module_loader.path_index.include?(File.join(module_dir, 'lib', 'puppet', 'functions', 'foo.rb'))).to be(true)
    end

    it 'all function smart-paths produces entries if they exist' do
      module_dir = dir_containing('testmodule', {
        'lib' => {
          'puppet' => {
            'functions' => {'foo4x.rb' => 'ignored in this test'},
            'parser' => {
              'functions' => {'foo3x.rb' => 'ignored in this test'},
            }
          }}})
      module_loader = Puppet::Pops::Loader::ModuleLoaders::FileBased.new(static_loader, unused_loaders, 'testmodule', module_dir, 'test1')

      effective_paths = module_loader.smart_paths.effective_paths(:function)

      expect(effective_paths.size).to eq(2)
      expect(module_loader.path_index.size).to eq(2)
      path_index = module_loader.path_index
      expect(path_index.include?(File.join(module_dir, 'lib', 'puppet', 'functions', 'foo4x.rb'))).to eq(true)
      expect(path_index.include?(File.join(module_dir, 'lib', 'puppet', 'parser', 'functions', 'foo3x.rb'))).to eq(true)
    end
  end
end


text:
Role('Joose.Attribute.Accessor.Combined', {

    have : {
        isCombined        : false
    },

    after : {
        initialize : function() {
            this.isCombined = this.isCombined || /..c/i.test(this.is)

            if (this.isCombined) {
                this.slot = '$$' + this.name

                this.hasGetter = true
                this.hasSetter = false

                this.setterName = this.getterName = this.publicName
            }
        }
    },

    override : {

        getGetter : function() {
            var getter    = this.SUPER()

            if (!this.isCombined) return getter

            var setter    = this.getSetter()

            var me = this

            return function () {

                if (!arguments.length) {
                    if (me.readable) return getter.call(this)
                    throw new Error("Call to getter of unreadable attribute: [" + me.name + "]")
                }

                if (me.writeable) return setter.apply(this, arguments)

                throw new Error("Call to setter of read-only attribute: [" + me.name + "]")
            }
        }
    }

})


text:
var Promise = require('es6-promise').Promise;
var axios = require('axios');
var config = require('./config');
var token = require('./token');
var request = require('./request');
var response = require('./response');
var FormData = require('form-data');

var path = '/v2/images';

module.exports.add = function(options) {
  return new Promise(function(resolve, reject) {
    token.get().then(
      function(tokenString) {
        return addRequest(options, tokenString);
      }
    ).then(
      function (_response) {
        response.handleV2(_response, resolve, reject);
      }
    ).catch(
      function(err){
        response.errorV2(err, reject);
      }
    );
  });
};

function addRequest(images, tokenString) {
  var url = config.get('apiEndpoint') + path;
  if ( images.length === undefined ) {
    images = [images];
  };
  var data = {
    'images': images
  };
  var headers = request.getHeaders(tokenString);
  return axios({
    'method': 'post',
    'url': url,
    'data': data,
    'headers': headers
  });
};

module.exports.get = function(options) {
  return new Promise(function(resolve, reject) {
    token.get().then(
      function(tokenString) {
        return getRequest(options, tokenString);
      }
    ).then(
      function (_response) {
        response.handleV2(_response, resolve, reject);
      }
    ).catch(
      function(err){
        response.errorV2(err, reject);
      }
    );
  });
};

function getRequest(params, tokenString) {
  var url = config.get('apiEndpoint') + path;
  var options = {
    'headers': request.getHeaders(tokenString)
  };
  if ( params !== null && params !== undefined ) {
    options.params = request.fillOptionalParams({}, params);
    if ( params.id !== undefined ) {
      url = config.get('apiEndpoint') + path + '/' + params.id;
    }
  }
  return axios.get(url, options);
};

module.exports.search = function(query, pagination) {
  return new Promise(function(resolve, reject) {
    token.get().then(
      function(tokenString) {
        return searchRequest(query, pagination, tokenString);
      }
    ).then(
      function (_response) {
        response.handleV2(_response, resolve, reject);
      }
    ).catch(
      function(err){
        response.errorV2(err, reject);
      }
    );
  });
};

function searchRequest(query, pagination, tokenString) {
  var url = config.get('apiEndpoint') + path + '/searches';
  if ( query !== null && query !== undefined ) {
    query = request.fillOptionalData({}, query);
  };
  if ( pagination !== null && pagination !== undefined ) {
    pagination = request.fillOptionalData({}, pagination);
  };
  var data = {
    'query': query,
    'pagination': pagination
  };
  return axios({
    'method': 'post',
    'url': url,
    'data': data,
    'headers': request.getHeaders(tokenString)
  });
};

module.exports.getById = function(id) {
  return new Promise(function(resolve, reject) {
    token.get().then(
      function(tokenString) {
        var params = {
          'id': id
        };
        return getRequest(params, tokenString);
      }
    ).then(
      function (_response) {
        response.handleV2(_response, resolve, reject);
      }
    ).catch(
      function(err){
        response.errorV2(err, reject);
      }
    );
  });
};

module.exports.deleteById = function(id) {
  return new Promise(function(resolve, reject) {
    token.get().then(
      function(tokenString) {
        return deleteRequest(id, tokenString);
      }
    ).then(
      function (_response) {
        response.handleV2(_response, resolve, reject);
      }
    ).catch(
      function(err){
        response.errorV2(err, reject);
      }
    );
  });
};

function deleteRequest(id, tokenString) {
  var url = config.get('apiEndpoint') + path + '/' + id;
  var options = {
    'headers': request.getHeaders(tokenString)
  };
  return axios.delete(url, options);
};

module.exports.getStatus = function() {
  return new Promise(function(resolve, reject) {
    token.get().then(
      function(tokenString) {
        return getStatustRequest(tokenString);
      }
    ).then(
      function (_response) {
        response.handleV2(_response, resolve, reject);
      }
    ).catch(
      function(err){
        response.errorV2(err, reject);
      }
    );
  });
};

function getStatustRequest(tokenString) {
  var url = config.get('apiEndpoint') + path + '/status';
  var options = {
    'headers': request.getHeaders(tokenString)
  };
  return axios.get(url, options);
};

module.exports.addFile = function(file) {
  return new Promise(function(resolve, reject) {
    token.get().then(
      function(tokenString) {
        return addFileRequest(file, tokenString);
      }
    ).then(
      function (_response) {
        response.handleV2(_response, resolve, reject);
      }
    ).catch(
      function(err){
        response.errorV2(err, reject);
      }
    );
  });
};

function addFileRequest(file, tokenString) {
  var url = config.get('apiEndpoint') + path + '/file';
  var formData = new FormData();
  var fileType = 'none';
  if (file.type !== undefined) {
    fileType = file.type.indexOf('csv') !== -1 ? 'csv' : 'tsv';
  };
  if (file.path !== undefined) {
    fileType = file.path.indexOf('csv') !== -1 ? 'csv' : 'tsv';
  };
  formData.append(fileType, file);
  var headers = request.getHeaders(tokenString);
  headers['content-type'] = 'multipart/form-data';
  return axios({
    'method': 'post',
    'url': url,
    'data': formData,
    'headers': headers
  });
};

// todo (dk) support localId


text:
<!DOCTYPE html>

	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
	<meta name="viewport" content="width=device-width,target-densitydpi=device-dpi,user-scalable=no,initial-scale=1.0">
	<title>Swipe Menu</title>

swipemenu.js

			A swipe-down menu framework for BlackBerry WebWorks SDK for Tablet OS.

			Designed to be easy to use (JavaScript only; no CSS or HTML), simply follow these steps to add a swipe-down menu bar to your PlayBook application:

				Add feature to config.xml:

				 <feature id="blackberry.app.event" required="true" version="1.0.0.0"/>

				Include swipemenu.js:

				 <script src="swipemenu.js"></script>

				Add buttons for your menu during page onload event:

				 swipemenu.addButton("Label", doButton, false, "img.png"); // title, callback, alignRight, imgPath

			<button onclick="swipemenu.simulateSwipeEvent()"> Simulate bezel swipe</button>

		By @n_adam_stanley and @roryboy