Просмотр исходного кода

NPD MOBILE

完成对网络播放的优化
增加从后台参数中提取播放id的功能。
Ray Zhang 9 лет назад
Родитель
Сommit
69d21f08dd

+ 126 - 141
RedAnt ERP Mobile/RedAnt ERP Mobile.xcworkspace/xcuserdata/Ray.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist

@@ -10,11 +10,11 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "common/Functions/modelDetail/DetailViewController.m"
-            timestampString = "519553581.091549"
+            timestampString = "519640112.69855"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "1010"
-            endingLineNumber = "1010"
+            startingLineNumber = "1019"
+            endingLineNumber = "1019"
             landmarkName = "-tableView:cellForRowAtIndexPath:"
             landmarkType = "7">
          </BreakpointContent>
@@ -26,12 +26,28 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "common/Functions/modelDetail/DetailHeaderCell.m"
-            timestampString = "519547902.606104"
+            timestampString = "519721547.756947"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "1071"
-            endingLineNumber = "1071"
-            landmarkName = "-photoStackView:contentForIndex:"
+            startingLineNumber = "1062"
+            endingLineNumber = "1062"
+            landmarkName = "-numberOfPhotosInPhotoStackView:"
+            landmarkType = "7">
+         </BreakpointContent>
+      </BreakpointProxy>
+      <BreakpointProxy
+         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
+         <BreakpointContent
+            shouldBeEnabled = "No"
+            ignoreCount = "0"
+            continueAfterRunningActions = "No"
+            filePath = "common/Functions/modelDetail/DetailViewController.m"
+            timestampString = "519641349.319399"
+            startingColumnNumber = "9223372036854775807"
+            endingColumnNumber = "9223372036854775807"
+            startingLineNumber = "1397"
+            endingLineNumber = "1397"
+            landmarkName = "-tableView:cellForRowAtIndexPath:"
             landmarkType = "7">
          </BreakpointContent>
       </BreakpointProxy>
@@ -42,11 +58,11 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "common/Functions/modelDetail/DetailHeaderCell.m"
-            timestampString = "519544763.00783"
+            timestampString = "519721547.756947"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "1062"
-            endingLineNumber = "1062"
+            startingLineNumber = "1063"
+            endingLineNumber = "1063"
             landmarkName = "-numberOfPhotosInPhotoStackView:"
             landmarkType = "7">
          </BreakpointContent>
@@ -57,61 +73,14 @@
             shouldBeEnabled = "No"
             ignoreCount = "0"
             continueAfterRunningActions = "No"
-            filePath = "common/Functions/modelDetail/DetailHeaderCell.m"
-            timestampString = "519544940.57881"
+            filePath = "common/Functions/modelDetail/DetailViewController.m"
+            timestampString = "519641349.319399"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "1070"
-            endingLineNumber = "1070"
-            landmarkName = "-photoStackView:contentForIndex:"
+            startingLineNumber = "1400"
+            endingLineNumber = "1400"
+            landmarkName = "-tableView:cellForRowAtIndexPath:"
             landmarkType = "7">
-            <Locations>
-               <Location
-                  shouldBeEnabled = "No"
-                  ignoreCount = "0"
-                  continueAfterRunningActions = "No"
-                  symbolName = "-[DetailHeaderCell photoStackView:contentForIndex:]"
-                  moduleName = "NPD Mobile"
-                  usesParentBreakpointCondition = "Yes"
-                  urlString = "file:///Users/ray/Documents/CODE_ERPSuiteIOS/RedAnt%20ERP%20Mobile/common/Functions/modelDetail/DetailHeaderCell.m"
-                  timestampString = "519544940.584431"
-                  startingColumnNumber = "9223372036854775807"
-                  endingColumnNumber = "9223372036854775807"
-                  startingLineNumber = "1071"
-                  endingLineNumber = "1071"
-                  offsetFromSymbolStart = "57">
-               </Location>
-               <Location
-                  shouldBeEnabled = "No"
-                  ignoreCount = "0"
-                  continueAfterRunningActions = "No"
-                  symbolName = "__Block_byref_object_copy_"
-                  moduleName = "NPD Mobile"
-                  usesParentBreakpointCondition = "Yes"
-                  urlString = "file:///Users/ray/Documents/CODE_ERPSuiteIOS/RedAnt%20ERP%20Mobile/common/Functions/modelDetail/DetailHeaderCell.m"
-                  timestampString = "519544940.590885"
-                  startingColumnNumber = "9223372036854775807"
-                  endingColumnNumber = "9223372036854775807"
-                  startingLineNumber = "1071"
-                  endingLineNumber = "1071"
-                  offsetFromSymbolStart = "16">
-               </Location>
-               <Location
-                  shouldBeEnabled = "No"
-                  ignoreCount = "0"
-                  continueAfterRunningActions = "No"
-                  symbolName = "__Block_byref_object_dispose_"
-                  moduleName = "NPD Mobile"
-                  usesParentBreakpointCondition = "Yes"
-                  urlString = "file:///Users/ray/Documents/CODE_ERPSuiteIOS/RedAnt%20ERP%20Mobile/common/Functions/modelDetail/DetailHeaderCell.m"
-                  timestampString = "519544940.595069"
-                  startingColumnNumber = "9223372036854775807"
-                  endingColumnNumber = "9223372036854775807"
-                  startingLineNumber = "1071"
-                  endingLineNumber = "1071"
-                  offsetFromSymbolStart = "16">
-               </Location>
-            </Locations>
          </BreakpointContent>
       </BreakpointProxy>
       <BreakpointProxy
@@ -121,11 +90,11 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "common/Functions/modelDetail/DetailViewController.m"
-            timestampString = "519553581.091549"
+            timestampString = "519641349.319399"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "1387"
-            endingLineNumber = "1387"
+            startingLineNumber = "1403"
+            endingLineNumber = "1403"
             landmarkName = "-tableView:cellForRowAtIndexPath:"
             landmarkType = "7">
          </BreakpointContent>
@@ -137,11 +106,27 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "common/Functions/modelDetail/DetailViewController.m"
-            timestampString = "519553581.091549"
+            timestampString = "519552550.118396"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "1416"
-            endingLineNumber = "1416"
+            startingLineNumber = "148"
+            endingLineNumber = "148"
+            landmarkName = "-reload"
+            landmarkType = "7">
+         </BreakpointContent>
+      </BreakpointProxy>
+      <BreakpointProxy
+         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
+         <BreakpointContent
+            shouldBeEnabled = "No"
+            ignoreCount = "0"
+            continueAfterRunningActions = "No"
+            filePath = "common/Functions/modelDetail/DetailViewController.m"
+            timestampString = "519641349.319399"
+            startingColumnNumber = "9223372036854775807"
+            endingColumnNumber = "9223372036854775807"
+            startingLineNumber = "1403"
+            endingLineNumber = "1403"
             landmarkName = "-tableView:cellForRowAtIndexPath:"
             landmarkType = "7">
          </BreakpointContent>
@@ -152,13 +137,13 @@
             shouldBeEnabled = "No"
             ignoreCount = "0"
             continueAfterRunningActions = "No"
-            filePath = "common/Functions/modelDetail/DetailHeaderCell.m"
-            timestampString = "519552793.217402"
+            filePath = "common/RAUtils.m"
+            timestampString = "519552926.61651"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "1378"
-            endingLineNumber = "1378"
-            landmarkName = "-AddStackContent:"
+            startingLineNumber = "921"
+            endingLineNumber = "921"
+            landmarkName = "+htmlForVideo:template:"
             landmarkType = "7">
          </BreakpointContent>
       </BreakpointProxy>
@@ -168,13 +153,13 @@
             shouldBeEnabled = "No"
             ignoreCount = "0"
             continueAfterRunningActions = "No"
-            filePath = "common/Functions/modelDetail/DetailHeaderCell.m"
-            timestampString = "519548249.348925"
+            filePath = "common/photoStack/PhotoStackView.m"
+            timestampString = "519638274.829035"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "1063"
-            endingLineNumber = "1063"
-            landmarkName = "-numberOfPhotosInPhotoStackView:"
+            startingLineNumber = "304"
+            endingLineNumber = "304"
+            landmarkName = "-indexOfTopPhoto"
             landmarkType = "7">
          </BreakpointContent>
       </BreakpointProxy>
@@ -184,13 +169,13 @@
             shouldBeEnabled = "No"
             ignoreCount = "0"
             continueAfterRunningActions = "No"
-            filePath = "common/Functions/modelDetail/DetailHeaderCell.m"
-            timestampString = "519552793.217402"
+            filePath = "common/photoStack/PhotoStackView.m"
+            timestampString = "519638274.829035"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "1427"
-            endingLineNumber = "1427"
-            landmarkName = "-setup"
+            startingLineNumber = "304"
+            endingLineNumber = "304"
+            landmarkName = "-indexOfTopPhoto"
             landmarkType = "7">
          </BreakpointContent>
       </BreakpointProxy>
@@ -200,13 +185,13 @@
             shouldBeEnabled = "No"
             ignoreCount = "0"
             continueAfterRunningActions = "No"
-            filePath = "common/Functions/modelDetail/DetailViewController.m"
-            timestampString = "519553581.091549"
+            filePath = "common/photoStack/PhotoStackView.m"
+            timestampString = "519638274.829035"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "1390"
-            endingLineNumber = "1390"
-            landmarkName = "-tableView:cellForRowAtIndexPath:"
+            startingLineNumber = "309"
+            endingLineNumber = "309"
+            landmarkName = "-topPhoto"
             landmarkType = "7">
          </BreakpointContent>
       </BreakpointProxy>
@@ -216,13 +201,13 @@
             shouldBeEnabled = "No"
             ignoreCount = "0"
             continueAfterRunningActions = "No"
-            filePath = "common/Functions/modelDetail/DetailViewController.m"
-            timestampString = "519553581.091549"
+            filePath = "common/photoStack/PhotoStackView.m"
+            timestampString = "519554395.604837"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "1441"
-            endingLineNumber = "1441"
-            landmarkName = "-tableView:cellForRowAtIndexPath:"
+            startingLineNumber = "114"
+            endingLineNumber = "114"
+            landmarkName = "-setHighlighted:"
             landmarkType = "7">
          </BreakpointContent>
       </BreakpointProxy>
@@ -232,13 +217,13 @@
             shouldBeEnabled = "No"
             ignoreCount = "0"
             continueAfterRunningActions = "No"
-            filePath = "common/Functions/modelDetail/DetailViewController.m"
-            timestampString = "519553581.091549"
+            filePath = "common/photoStack/PhotoStackView.m"
+            timestampString = "519638274.829035"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "1392"
-            endingLineNumber = "1392"
-            landmarkName = "-tableView:cellForRowAtIndexPath:"
+            startingLineNumber = "312"
+            endingLineNumber = "312"
+            landmarkName = "-topPhoto"
             landmarkType = "7">
          </BreakpointContent>
       </BreakpointProxy>
@@ -249,11 +234,11 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "common/Functions/modelDetail/DetailViewController.m"
-            timestampString = "519552550.118396"
+            timestampString = "519640112.69855"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "148"
-            endingLineNumber = "148"
+            startingLineNumber = "194"
+            endingLineNumber = "194"
             landmarkName = "-reload"
             landmarkType = "7">
          </BreakpointContent>
@@ -265,11 +250,11 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "common/Functions/modelDetail/DetailViewController.m"
-            timestampString = "519553581.091549"
+            timestampString = "519641349.319399"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "1395"
-            endingLineNumber = "1395"
+            startingLineNumber = "1384"
+            endingLineNumber = "1384"
             landmarkName = "-tableView:cellForRowAtIndexPath:"
             landmarkType = "7">
          </BreakpointContent>
@@ -280,13 +265,13 @@
             shouldBeEnabled = "No"
             ignoreCount = "0"
             continueAfterRunningActions = "No"
-            filePath = "common/RAUtils.m"
-            timestampString = "519552926.61651"
+            filePath = "common/Functions/modelDetail/DetailViewController.m"
+            timestampString = "519641349.319399"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "921"
-            endingLineNumber = "921"
-            landmarkName = "+htmlForVideo:template:"
+            startingLineNumber = "1382"
+            endingLineNumber = "1382"
+            landmarkName = "-tableView:cellForRowAtIndexPath:"
             landmarkType = "7">
          </BreakpointContent>
       </BreakpointProxy>
@@ -297,12 +282,12 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "common/Functions/modelDetail/DetailViewController.m"
-            timestampString = "519553581.091549"
+            timestampString = "519640105.74578"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "1397"
-            endingLineNumber = "1397"
-            landmarkName = "-tableView:cellForRowAtIndexPath:"
+            startingLineNumber = "182"
+            endingLineNumber = "182"
+            landmarkName = "-reload"
             landmarkType = "7">
          </BreakpointContent>
       </BreakpointProxy>
@@ -312,13 +297,13 @@
             shouldBeEnabled = "No"
             ignoreCount = "0"
             continueAfterRunningActions = "No"
-            filePath = "common/photoStack/PhotoStackView.m"
-            timestampString = "519555118.914511"
+            filePath = "common/Functions/modelDetail/DetailViewController.m"
+            timestampString = "519640109.941173"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "308"
-            endingLineNumber = "308"
-            landmarkName = "-indexOfTopPhoto"
+            startingLineNumber = "180"
+            endingLineNumber = "180"
+            landmarkName = "-reload"
             landmarkType = "7">
          </BreakpointContent>
       </BreakpointProxy>
@@ -328,61 +313,61 @@
             shouldBeEnabled = "No"
             ignoreCount = "0"
             continueAfterRunningActions = "No"
-            filePath = "common/photoStack/PhotoStackView.m"
-            timestampString = "519555118.914511"
+            filePath = "common/Functions/modelDetail/DetailHeaderCell.m"
+            timestampString = "519724140.460772"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "308"
-            endingLineNumber = "308"
-            landmarkName = "-indexOfTopPhoto"
+            startingLineNumber = "1144"
+            endingLineNumber = "1144"
+            landmarkName = "-photoStackView:didSelectPhotoAtIndex:"
             landmarkType = "7">
          </BreakpointContent>
       </BreakpointProxy>
       <BreakpointProxy
          BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
          <BreakpointContent
-            shouldBeEnabled = "No"
+            shouldBeEnabled = "Yes"
             ignoreCount = "0"
             continueAfterRunningActions = "No"
-            filePath = "common/photoStack/PhotoStackView.m"
-            timestampString = "519555118.914511"
+            filePath = "common/TextUtils.m"
+            timestampString = "519724208.041774"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "313"
-            endingLineNumber = "313"
-            landmarkName = "-topPhoto"
+            startingLineNumber = "147"
+            endingLineNumber = "147"
+            landmarkName = "+expression_varable:regex:"
             landmarkType = "7">
          </BreakpointContent>
       </BreakpointProxy>
       <BreakpointProxy
          BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
          <BreakpointContent
-            shouldBeEnabled = "No"
+            shouldBeEnabled = "Yes"
             ignoreCount = "0"
             continueAfterRunningActions = "No"
-            filePath = "common/photoStack/PhotoStackView.m"
-            timestampString = "519554395.604837"
+            filePath = "common/RAUtils.m"
+            timestampString = "519724376.016372"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "114"
-            endingLineNumber = "114"
-            landmarkName = "-setHighlighted:"
+            startingLineNumber = "191"
+            endingLineNumber = "191"
+            landmarkName = "+expression_varable:regex:"
             landmarkType = "7">
          </BreakpointContent>
       </BreakpointProxy>
       <BreakpointProxy
          BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
          <BreakpointContent
-            shouldBeEnabled = "No"
+            shouldBeEnabled = "Yes"
             ignoreCount = "0"
             continueAfterRunningActions = "No"
-            filePath = "common/photoStack/PhotoStackView.m"
-            timestampString = "519555118.914511"
+            filePath = "common/Functions/modelDetail/DetailHeaderCell.m"
+            timestampString = "519726561.523658"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "316"
-            endingLineNumber = "316"
-            landmarkName = "-topPhoto"
+            startingLineNumber = "1146"
+            endingLineNumber = "1146"
+            landmarkName = "-photoStackView:didSelectPhotoAtIndex:"
             landmarkType = "7">
          </BreakpointContent>
       </BreakpointProxy>

+ 10 - 11
RedAnt ERP Mobile/common/Functions/modelDetail/DetailHeaderCell.h

@@ -10,6 +10,8 @@
 #import "PhotoStackView.h"
 #import "RTLabel.h"
 #import "NotificationNameCenter.h"
+#import "YTPlayerView.h"
+
 
 @class DetailHeaderCell;
 @protocol DetailHeaderCellDelegate <NSObject>
@@ -37,11 +39,8 @@
 @property (strong, nonatomic) IBOutlet PhotoStackView *photoStack;
 @property (weak, nonatomic) IBOutlet UIPageControl *pageControl;
 -(void)setup;
-//@property (nonatomic, strong) NSArray *photos;
-
-@property (nonatomic, strong) NSArray *stack_content;
-//@property (nonatomic, strong) NSMutableDictionary *stack_content;
-
+@property (nonatomic, strong) NSArray *photos;
+@property (nonatomic, strong) NSDictionary *stack_contents;
 @property (nonatomic, strong) NSArray *urls;
 @property (nonatomic, strong) NSString *product_id;
 //@property long detailID;
@@ -86,18 +85,18 @@
 @property (nonatomic,copy)  void(^set_update_data)(bool bupdate);
 @property (nonatomic,copy)  void(^WatchlistBlock)(UIImageView *imageView);
 @property (nonatomic,copy)  void(^PortfolioBlock)(UIImageView *imageView);
-- (void)AddStackContent :(NSString*)html ;
-
--(void) resetStack;
-//-(void) setStack_content:(NSMutableDictionary*) content;
-//- (void)AddPhoto :(UIImage*)photo ;
+- (void)AddPhoto :(UIImage*)photo ;
 @property (strong, nonatomic) IBOutlet UILabel *selector_label;
 @property (strong, nonatomic) IBOutlet UIButton *introduceBtn;
 @property (nonatomic,copy) void(^introduceBlock)(UIButton *sender);
 -(void)Hide_selector:(bool) bhide;
 
-//-(void)ClearPhotos;
+-(void)ClearPhotos;
 @property int cqty;
 @property int step;
 @property int quantity;
+
+@property (weak, nonatomic) IBOutlet YTPlayerView *playerView;
+
+
 @end

+ 99 - 65
RedAnt ERP Mobile/common/Functions/modelDetail/DetailHeaderCell.m

@@ -17,6 +17,8 @@
 
 #import "ImageScrollerViewController.h"
 #import "CartUtils.h"
+#import "CustomIOSAlertView.h"
+#import "TextUtils.h"
 
 
 @implementation DetailHeaderCell
@@ -1058,38 +1060,18 @@
 #pragma mark Deck DataSource Protocol Methods
 
 -(NSUInteger)numberOfPhotosInPhotoStackView:(PhotoStackView *)photoStack {
-    
-  //  return [self.stack_content[@"count"] intValue];
-    return [self.stack_content count];
+    return [self.photos count];
 }
 
-
-- (NSString *)photoStackView:(PhotoStackView *)photoStack contentForIndex:(NSUInteger)index
-{
-    
+-(UIImage *)photoStackView:(PhotoStackView *)photoStack photoForIndex:(NSUInteger)index {
     
-        if(self.stack_content.count==0)
-            return nil;
-        return [self.stack_content objectAtIndex:index];
     
+    if(self.photos.count==0)
+        return nil;
+    return [self.photos objectAtIndex:index];
 }
 
 
--(UIImage *)photoStackView:(PhotoStackView *)photoStack photoForIndex:(NSUInteger)index {
-
-    return nil;
-    
-//    if(self.photos.count==0)
-//        return nil;
-//    return [self.photos objectAtIndex:index];
-}
-
-/*
-- (NSString *)photoStackView:(PhotoStackView *)photoStack contentForIndex:(NSUInteger)index
-{
- 
-}
-*/
 
 #pragma mark -
 #pragma mark Deck Delegate Protocol Methods
@@ -1106,10 +1088,84 @@
     self.pageControl.currentPage = index;
 }
 
+
+- (UIView *)createVideoView:(NSString*) videoID
+{
+    UIView *demoView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 640, 360)];
+    
+    //    UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(10, 10, 270, 180)];
+    //    [imageView setImage:[UIImage imageNamed:@"demo"]];
+    
+    UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 620, 10)];
+    titleLabel.font = [UIFont boldSystemFontOfSize:20];
+    titleLabel.text=@"a separated player will show here:";
+    titleLabel.numberOfLines = 0;
+    titleLabel.lineBreakMode =NSLineBreakByWordWrapping;
+    //    titleLabel.textAlignment = NSTextAlignmentRight;
+    titleLabel.textAlignment = NSTextAlignmentCenter;
+    CGSize constraintkey = CGSizeMake(620, 20000.0f);//key label width is 40% cell width;
+    CGSize sizetitle = [@"a separated player will show here:" sizeWithFont:[UIFont systemFontOfSize:20.0] constrainedToSize:constraintkey lineBreakMode:NSLineBreakByWordWrapping];
+    
+    //[titleLabel sizeToFit];
+    //    titleLabel.center.x = demoView.center.x;
+    titleLabel.frame = CGRectMake(10, 10, 620, sizetitle.height);
+    [demoView addSubview:titleLabel];
+    
+    UILabel *lineLabel = [[UILabel alloc] initWithFrame:CGRectMake(15, titleLabel.frame.origin.y+ titleLabel.frame.size.height+20, 610, 10)];
+    lineLabel.font = [UIFont boldSystemFontOfSize:17];
+    lineLabel.text=videoID;
+    lineLabel.numberOfLines = 0;
+    lineLabel.lineBreakMode =NSLineBreakByWordWrapping;
+    
+    CGSize sizemsg = [videoID sizeWithFont:[UIFont boldSystemFontOfSize:17.0] constrainedToSize:constraintkey lineBreakMode:NSLineBreakByWordWrapping];
+    
+    lineLabel.frame = CGRectMake(15, titleLabel.frame.origin.y+ titleLabel.frame.size.height+20, 630, sizemsg.height);
+    //[lineLabel sizeToFit];
+    
+    [demoView addSubview:lineLabel];
+    demoView.frame = CGRectMake(0, 0, 640, lineLabel.frame.origin.y+lineLabel.frame.size.height+25);
+    return demoView;
+}
+
+
 -(void)photoStackView:(PhotoStackView *)photoStackView didSelectPhotoAtIndex:(NSUInteger)index {
     DebugLog(@"selected %lu", (unsigned long)index);
     
     
+    NSDictionary* item= self.stack_contents[[NSString stringWithFormat:@"item_%d",index]];
+    
+    if([item[@"type"] isEqualToString:@"video"])
+    {
+        
+        NSString* video_code=item[@"code"];
+        
+        
+        
+//        [TextUtils expression_varable:video regex:<#(NSString *)#>]
+        
+        NSString* video_id=[self.playerView Embed2VID:video_code];
+        
+        NSDictionary *playerVars = @{
+                                     @"playsinline" : @1,
+                                     @"autoplay" : @(1),
+                                     @"rel":@0,
+                                     @"showinfo": @0,
+                                     @"modestbranding":@0,
+                                     @"enablejsapi":@1
+                                     };
+        
+        [self.playerView loadWithVideoId:video_id playerVars:playerVars];
+        for(UIView *v in self.playerView.subviews)
+        {
+            if([v isKindOfClass:[UIButton class] ])
+                [self.playerView bringSubviewToFront:v];
+        }
+        self.playerView.webView.allowsPictureInPictureMediaPlayback=false;
+        self.playerView.hidden=false;
+        
+        return;
+    }
+    
 //    NSMutableArray *imgList = [NSMutableArray arrayWithCapacity:_data.count];
 //    for (int i = 0; i < _data.count; i++) {
 //        UIImage *imgMod = _data[i];
@@ -1117,7 +1173,7 @@
 //    }
     
     // 调用展示窗口
-    ImageScrollerViewController *imgShow = [ImageScrollerViewController new];//[[ImageScrollerViewController alloc] initWithSourceData:[self.photos mutableCopy] withIndex:index];
+    ImageScrollerViewController *imgShow = [[ImageScrollerViewController alloc] initWithSourceData:[self.photos mutableCopy] withIndex:index];
     
     UIViewController *vc= [RAUtils getViewController:self];
     [vc.navigationController pushViewController:imgShow animated:true];
@@ -1350,7 +1406,6 @@
 
 #pragma mark -
 #pragma mark Actions
-/*
 -(void)ClearPhotos
 {
     NSMutableArray *photosMutable = [self.photos mutableCopy];
@@ -1361,32 +1416,6 @@
 
 
 }
-*/
-
--(void) resetStack
-{
-    
-    [[self.stack_content mutableCopy] removeAllObjects];
-
-        [self.photoStack reloadData];
-}
-
-
-
-- (void)AddStackContent :(NSString*)html {
-    
-    NSMutableArray *photosMutable = [self.stack_content mutableCopy];
-    [photosMutable addObject:html];
-    self.stack_content = photosMutable;
-    [self.photoStack reloadData];
-    self.pageControl.numberOfPages = [self.stack_content count];
-    if(self.stack_content.count<2)
-    self.photoStack.showBorder=false;
-    else
-        self.photoStack.showBorder=true;
-}
-
-/*
 - (void)AddPhoto :(UIImage*)photo {
     
     NSMutableArray *photosMutable = [self.photos mutableCopy];
@@ -1394,13 +1423,13 @@
     self.photos = photosMutable;
     [self.photoStack reloadData];
     self.pageControl.numberOfPages = [self.photos count];
-    if(self.photos.count<2-1)1 for qrcode;
+    if(self.photos.count<2-1)//1 for qrcode;
         self.photoStack.showBorder=false;
     else
         self.photoStack.showBorder=true;
         
 }
-*/
+
 
 
 #pragma mark -
@@ -1408,7 +1437,7 @@
 -(void)setup {
     
     
-    int availability = [self.count_Label.text intValue];
+//    int availability = [self.count_Label.text intValue];
     self.stepper.minimumValue= self.step;
     self.stepper.maximumValue = INTMAX_MAX;
     
@@ -1424,21 +1453,21 @@
     if(true)//(self.photos==nil)
     {
         
-        self.stack_content = [NSArray new];
+        self.photos = [NSArray array];
     }
-    if(true)
+    if(true)//(self.photoStack==nil)
     {
-         //      if(self.photoStack!=nil)
-       //            [self.photoStack removeFromSuperview];
-       //   self.photoStack = [[PhotoStackView alloc] initWithFrame:CGRectMake(0, 0, 300, 300)];
-      //          self.photoStack.backgroundColor =[UIColor redColor];
-      //      self.photoStack.center = CGPointMake(self.center.x, 220);
+        //       if(self.photoStack!=nil)
+        //           [self.photoStack removeFromSuperview];
+        //  self.photoStack = [[PhotoStackView alloc] initWithFrame:CGRectMake(0, 0, 300, 300)];
+        //        self.photoStack.backgroundColor =[UIColor redColor];
+        //    self.photoStack.center = CGPointMake(self.center.x, 220);
         self.photoStack.dataSource = self;
         self.photoStack.delegate = self;
         
         
-  //          [self addSubview:self.photoStack];
-        self.pageControl.numberOfPages = self.stack_content.count;
+        //    [self addSubview:self.photoStack];
+        self.pageControl.numberOfPages = [self.photos count];
     }
 }
 
@@ -1551,6 +1580,11 @@
     
 }
 
+- (IBAction)OnClosePlayer:(id)sender {
+    [self.playerView stopVideo];
+    
+    self.playerView.hidden=true;
+}
 
 
 @end

+ 39 - 28
RedAnt ERP Mobile/common/Functions/modelDetail/DetailViewController.m

@@ -114,7 +114,7 @@ dispatch_async(dispatch_get_main_queue(), ^{
     
     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
         
-        NSDictionary* detail_json = [iSalesNetwork request_ItemDetail:self.product_id model_name:self.model_name category_id:self.category_id use_name:self.use_model_name];
+        NSMutableDictionary* detail_json = [[iSalesNetwork request_ItemDetail:self.product_id model_name:self.model_name category_id:self.category_id use_name:self.use_model_name] mutableCopy];
         
         dispatch_async(dispatch_get_main_queue(), ^{
             [self.mum stopAnimating];
@@ -149,8 +149,6 @@ dispatch_async(dispatch_get_main_queue(), ^{
                 
                 NSMutableDictionary* contents = [NSMutableDictionary new];
                 int count = 0;
-                
-                
                 {
                     for(int ic=0;ic<[images[@"img_count"] intValue];ic++)
                     {
@@ -175,15 +173,26 @@ dispatch_async(dispatch_get_main_queue(), ^{
                     for(int ic=0;ic<[videos[@"count"] intValue];ic++)
                     {
                         NSString* videocode = videos[[NSString stringWithFormat:@"video_%d",ic]];
+                        
+
+//                         NSString*   path=[[NSBundle mainBundle] pathForResource:@"play" ofType:@"jpg"];
+//                        
+//                        NSMutableDictionary* item =[@{@"type":@"video",@"is_localfile":@(true),@"l":path,@"s":path} mutableCopy];
+//                        
+//                        contents[[NSString stringWithFormat:@"item_%d",count]]=item ;
+//                        count++;
+                        
                         NSMutableDictionary* item =[@{@"type":@"video",@"code":videocode} mutableCopy];
                         
                         contents[[NSString stringWithFormat:@"item_%d",count]]=item;
                         count++;
                     }
                 }
+                
+                
                 contents[@"count"]=@(count);
                 imgsection[@"stack_contents"]=contents;
-                self.detail_data[@"img_section"] = imgsection;
+                self.detail_data[@"img_section"]=imgsection;
 //                self.detail_data[@"img_section"][@"stack_contents"]=contents;
                 
                 NSMutableDictionary* item = [[NSMutableDictionary alloc]init];
@@ -1368,9 +1377,10 @@ self.isrefreshing=false;
         cell.product_id = self.product_id;
 //        cell.itemIDLabel.text = [NSString stringWithFormat:@"Item id:%d",3947105];
         [cell setup];
-        [cell resetStack];
+        [cell ClearPhotos];
 //         NSDictionary* imgs_json = [section_json objectForKey:@"images"];
-        NSMutableDictionary* contents_json = [section_json objectForKey:@"stack_contents"];
+        NSDictionary* contents_json = [section_json objectForKey:@"stack_contents"];
+        cell.stack_contents = contents_json;
         int count = [[contents_json valueForKey:@"count"]intValue];
         if(count<2)//1 for qrcode;
             cell.pageControl.hidden = true;
@@ -1381,22 +1391,21 @@ self.isrefreshing=false;
         
        // cell.urls = urls;
         
-//        cell.stack_content = contents_json;//
+        
         dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- 
+            
             for(int i=0;i<count;i++)
             {
                 
                 NSDictionary* item_json=[contents_json objectForKey:[NSString stringWithFormat:@"item_%d",i]];
                 
                 NSString* type = item_json[@"type"];
-                if([type isEqualToString:@"video"])
-                {
-                    NSString* code = [item_json valueForKey:@"code"];
-                    NSString* html=[RAUtils htmlForVideo:code template:nil];
-                    [cell AddStackContent:html];
-                }
-                else if([type isEqualToString:@"image"])
+//                if([type isEqualToString:@"video"])
+//                {
+//                    [cell AddPhoto:[UIImage imageNamed:@"play"]];
+//                    ;
+//                }
+//                else if([type isEqualToString:@"image"])
                 {
                     NSString* img_url = [item_json valueForKey:@"s"];
                     
@@ -1406,17 +1415,25 @@ self.isrefreshing=false;
                     //      [urls addObject:img_url_l];
                     NSString* file_name=[img_url lastPathComponent];
                     NSData* img_data=nil;
-                    if([item_json[@"is_localfile"] boolValue])
-                        img_data = [NSData dataWithContentsOfFile:img_url];
+                    
+                    if([type isEqualToString:@"video"])
+                    {
+                        img_data = UIImagePNGRepresentation([UIImage imageNamed:@"play"]);
+                    }
                     else
-                        [iSalesDB load_cached_img:file_name loadFrom:img_url];
+                    {
+                        if([item_json[@"is_localfile"] boolValue])
+                            img_data = [NSData dataWithContentsOfFile:img_url];
+                        else
+                            [iSalesDB load_cached_img:file_name loadFrom:img_url];
+                    }
+
                     if(img_data!=nil)
                     {
                         dispatch_async(dispatch_get_main_queue(), ^{
                             UIImage * img =[UIImage imageWithData:img_data];
+                            [cell AddPhoto:img];
                             
-                            NSString* html=[RAUtils htmlForImage:img template:nil];
-                            [cell AddStackContent:html];
                             
 //                            if(i==count-1)
 //                            {
@@ -1444,9 +1461,7 @@ self.isrefreshing=false;
                                 [iSalesDB cache_img:downloadimg_data filename:file_name saveTo:img_url];
                                 
                                 UIImage * img =[UIImage imageWithData:downloadimg_data];
-                                NSString* html=[RAUtils htmlForImage:img template:nil];
-                                [cell AddStackContent:html];
-
+                                [cell AddPhoto:img];
                                 
 //                                if(i==count-1)
 //                                {
@@ -1462,9 +1477,7 @@ self.isrefreshing=false;
                                 
                                 
                                 UIImage * img =[UIImage imageNamed:@"notfound_l"];
-                                NSString* html=[RAUtils htmlForImage:img template:nil];
-                                [cell AddStackContent:html];
-
+                                [cell AddPhoto:img];
                                 
 //                                if(i==count-1)
 //                                {
@@ -1490,8 +1503,6 @@ self.isrefreshing=false;
 
             
         });
-        
-        
        // cell.backgroundColor = [UIColor whiteColor];
         
         __weak typeof(self) weakself = self;

+ 1 - 6
RedAnt ERP Mobile/common/photoStack/PhotoStackView.h

@@ -14,11 +14,8 @@
 
     @required
     - (NSUInteger)numberOfPhotosInPhotoStackView:(PhotoStackView *)photoStack;
+    - (UIImage *)photoStackView:(PhotoStackView *)photoStack photoForIndex:(NSUInteger)index;
 
-//This method has been replaced by -(NSString *)photoStackView:(PhotoStackView *)photoStack contentForIndex:(NSUInteger)index
-//    - (UIImage *)photoStackView:(PhotoStackView *)photoStack photoForIndex:(NSUInteger)index NS_DEPRECATED_IOS(2_0, 6_0);
-
-    - (NSString *)photoStackView:(PhotoStackView *)photoStack contentForIndex:(NSUInteger)index;
     @optional
     - (CGSize)photoStackView:(PhotoStackView *)photoStack photoSizeForIndex:(NSUInteger)index;
 
@@ -64,6 +61,4 @@
     -(void)flipToNextPhoto;
     -(void)reloadData;
 
-
-//+ (NSString *)htmlForJPGImage:(UIImage *)image template:(NSString*) path;
 @end

+ 59 - 95
RedAnt ERP Mobile/common/photoStack/PhotoStackView.m

@@ -113,11 +113,7 @@ static CGFloat const PhotoRotationOffsetDefault = 4.0f;
 -(void)setHighlighted:(BOOL)highlighted {
     [super setHighlighted:highlighted];
     
-    // !!!!!!!!! BUG ATTENTION!!!!!!!!!!
-    return;
-//    UIImageView *topPhoto = [[self topPhoto].subviews lastObject];
-    
-    UIWebView *topPhoto = [[self topPhoto].subviews lastObject];
+    UIImageView *topPhoto = [[self topPhoto].subviews lastObject];
     
     if(highlighted) {
         
@@ -328,105 +324,73 @@ static CGFloat const PhotoRotationOffsetDefault = 4.0f;
 
 -(void)reloadData {
     
-    dispatch_async(dispatch_get_main_queue(), ^{
-        
-        
-        if (!self.dataSource) {
-            //exit if data source has not been set up yet
-            self.photoViews = nil;
-            return;
-        }
-        
-        NSInteger numberOfPhotos = [self.dataSource numberOfPhotosInPhotoStackView:self];
-        NSInteger topPhotoIndex  = [self indexOfTopPhoto]; // Keeping track of current photo's top index so that it remains on top if new photos are added
+    if (!self.dataSource) {
+        //exit if data source has not been set up yet
+        self.photoViews = nil;
+        return;
+    }
+    
+    NSInteger numberOfPhotos = [self.dataSource numberOfPhotosInPhotoStackView:self];
+    NSInteger topPhotoIndex  = [self indexOfTopPhoto]; // Keeping track of current photo's top index so that it remains on top if new photos are added
+    
+    if(numberOfPhotos >= 0) {
+
+        NSMutableArray *photoViewsMutable   = [[NSMutableArray alloc] initWithCapacity:numberOfPhotos];
+        UIImage *borderImage                = [self.borderImage resizableImageWithCapInsets:UIEdgeInsetsMake(self.borderWidth, self.borderWidth, self.borderWidth, self.borderWidth)];
         
-        if(numberOfPhotos >= 0) {
-            
-            NSMutableArray *photoViewsMutable   = [[NSMutableArray alloc] initWithCapacity:numberOfPhotos];
-            UIImage *borderImage                = [self.borderImage resizableImageWithCapInsets:UIEdgeInsetsMake(self.borderWidth, self.borderWidth, self.borderWidth, self.borderWidth)];
+        for (NSUInteger index = 0; index < numberOfPhotos; index++) {
+
             
-            for (NSUInteger index = 0; index < numberOfPhotos; index++) {
-                
-                
-                //            UIImage *image = [self.dataSource photoStackView:self photoForIndex:index];
-                
-                NSString* html =  [self.dataSource photoStackView:self contentForIndex:index];
-                //            CGSize imageSize = image.size;
-                //            if([self.dataSource respondsToSelector:@selector(photoStackView:photoSizeForIndex:)]){
-                //                imageSize = [self.dataSource photoStackView:self photoSizeForIndex:index];
-                //            }
-                //            UIImageView *photoImageView     = [[UIImageView alloc] initWithFrame:(CGRect){CGPointZero, self.frame.size}];
-                //            photoImageView.image            = image;
-                UIWebView* contentView = [[UIWebView alloc] initWithFrame:(CGRect){CGPointZero, self.frame.size}];
-                contentView.scrollView.scrollEnabled = false;
-                //            NSString *html = [self htmlForJPGImage:image template:nil];
-                
-                //
-                //            NSString *contentImg = [NSString stringWithFormat:@"%@", stringImage];
-                //            NSString *content =[NSString stringWithFormat:
-                //                                @"<html>"
-                //                                "<style type=\"text/css\">"
-                //                                "<!--"
-                //                                "body{font-size:40pt;line-height:60pt;}"
-                //                                "-->"
-                //                                "</style>"
-                //                                "<body>"
-                //                                "%@"
-                //                                "</body>"
-                //                                "</html>"
-                //                                , contentImg];
-                
-                //让self.contentWebView加载content
-                [contentView loadHTMLString:html baseURL:nil];
-                
-                
-                UIView *view                    = [[UIView alloc] initWithFrame:contentView.frame];
-                view.layer.rasterizationScale   = [[UIScreen mainScreen] scale];
-                view.layer.shouldRasterize      = YES; // rasterize the view for faster drawing and smooth edges
+            UIImage *image = [self.dataSource photoStackView:self photoForIndex:index];
+            CGSize imageSize = image.size;
+            if([self.dataSource respondsToSelector:@selector(photoStackView:photoSizeForIndex:)]){
+                imageSize = [self.dataSource photoStackView:self photoSizeForIndex:index];
+            }
+            UIImageView *photoImageView     = [[UIImageView alloc] initWithFrame:(CGRect){CGPointZero, self.frame.size}];
+            photoImageView.image            = image;
+            UIView *view                    = [[UIView alloc] initWithFrame:photoImageView.frame];
+            view.layer.rasterizationScale   = [[UIScreen mainScreen] scale];            
+            view.layer.shouldRasterize      = YES; // rasterize the view for faster drawing and smooth edges
+
+            if (self.showBorder) {
                 
-                if (self.showBorder) {
+                // Add the background image
+                if (borderImage) {
+                    // If there is a border image, we need to add a background image view, and add some padding around the photo for the border
+
+                    CGRect photoFrame                = photoImageView.frame;
+                    photoFrame.origin                = CGPointMake(self.borderWidth, self.borderWidth);
+                    photoImageView.frame             = photoFrame;
+
+                    view.frame                       = CGRectMake(0, 0, photoImageView.frame.size.width+(self.borderWidth*2), photoImageView.frame.size.height+(self.borderWidth*2));
+                    UIImageView *backgroundImageView = [[UIImageView alloc] initWithFrame:view.frame];
+                    backgroundImageView.image        = borderImage;
                     
-                    // Add the background image
-                    if (borderImage) {
-                        // If there is a border image, we need to add a background image view, and add some padding around the photo for the border
-                        
-                        CGRect photoFrame                = contentView.frame;
-                        photoFrame.origin                = CGPointMake(self.borderWidth, self.borderWidth);
-                        contentView.frame             = photoFrame;
-                        
-                        view.frame                       = CGRectMake(0, 0, contentView.frame.size.width+(self.borderWidth*2), contentView.frame.size.height+(self.borderWidth*2));
-                        UIImageView *backgroundImageView = [[UIImageView alloc] initWithFrame:view.frame];
-                        backgroundImageView.image        = borderImage;
-                        
-                        [view addSubview:backgroundImageView];
-                    } else {
-                        // if there is no boarder image draw one with the CALayer
-                        view.layer.borderWidth        = self.borderWidth;
-                        view.layer.borderColor        = [[UIColor whiteColor] CGColor];
-                        view.layer.shadowOffset       = CGSizeMake(0, 0);
-                        view.layer.shadowOpacity      = 0.5;
-                    }
+                    [view addSubview:backgroundImageView];
+                } else {
+                    // if there is no boarder image draw one with the CALayer
+                    view.layer.borderWidth        = self.borderWidth;
+                    view.layer.borderColor        = [[UIColor whiteColor] CGColor];
+                    view.layer.shadowOffset       = CGSizeMake(0, 0);
+                    view.layer.shadowOpacity      = 0.5;
                 }
-                
-                [view addSubview:contentView];
-                
-                view.tag    = index;
-                view.center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
-                
-                [photoViewsMutable addObject:view];
-                
             }
-            
-            // Photo views are added to subview in the photoView setter
-            self.photoViews = photoViewsMutable;
-            //photoViewsMutable = nil;
-            [self goToPhotoAtIndex:topPhotoIndex];
+
+            [view addSubview:photoImageView];
+
+            view.tag    = index;
+            view.center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
+
+            [photoViewsMutable addObject:view];
             
         }
 
-    });
-    
-    
+        // Photo views are added to subview in the photoView setter
+        self.photoViews = photoViewsMutable;
+        //photoViewsMutable = nil;
+        [self goToPhotoAtIndex:topPhotoIndex];
+        
+    }
     
 }
 

+ 90 - 0
RedAnt ERP Mobile/common/youtube/Assets.bundle/Assets/YTPlayerView-iframe-player.html

@@ -0,0 +1,90 @@
+<!--
+     Copyright 2014 Google Inc. All rights reserved.
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<!DOCTYPE html>
+<html>
+<head>
+    <style>
+    body { margin: 0; width:100%%; height:100%%;  background-color:#000000; }
+    html { width:100%%; height:100%%; background-color:#000000; }
+
+    .embed-container iframe,
+    .embed-container object,
+    .embed-container embed {
+        position: absolute;
+        top: 0;
+        left: 0;
+        width: 100%% !important;
+        height: 100%% !important;
+    }
+    </style>
+</head>
+<body>
+    <div class="embed-container">
+        <div id="player"></div>
+    </div>
+    <script src="https://www.youtube.com/iframe_api" onerror="window.location.href='ytplayer://onYouTubeIframeAPIFailedToLoad'"></script>
+    <script>
+    var player;
+    var error = false;
+
+    YT.ready(function() {
+        player = new YT.Player('player', %@);
+        player.setSize(window.innerWidth, window.innerHeight);
+        window.location.href = 'ytplayer://onYouTubeIframeAPIReady';
+
+        // this will transmit playTime frequently while playng
+        function getCurrentTime() {
+             var state = player.getPlayerState();
+             if (state == YT.PlayerState.PLAYING) {
+                 time = player.getCurrentTime()
+                 window.location.href = 'ytplayer://onPlayTime?data=' + time;
+             }
+        }
+        
+        window.setInterval(getCurrentTime, 500);
+             
+    });
+
+    function onReady(event) {
+        window.location.href = 'ytplayer://onReady?data=' + event.data;
+    }
+
+    function onStateChange(event) {
+        if (!error) {            
+            window.location.href = 'ytplayer://onStateChange?data=' + event.data;
+        }
+        else {
+            error = false;
+        }
+    }
+
+    function onPlaybackQualityChange(event) {
+        window.location.href = 'ytplayer://onPlaybackQualityChange?data=' + event.data;
+    }
+
+    function onPlayerError(event) {
+        if (event.data == 100) {
+            error = true;
+        }
+        window.location.href = 'ytplayer://onError?data=' + event.data;
+    }
+    
+    window.onresize = function() {
+        player.setSize(window.innerWidth, window.innerHeight);
+    }
+    </script>
+</body>
+</html>

+ 696 - 0
RedAnt ERP Mobile/common/youtube/YTPlayerView.h

@@ -0,0 +1,696 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import <UIKit/UIKit.h>
+
+@class YTPlayerView;
+
+/** These enums represent the state of the current video in the player. */
+typedef NS_ENUM(NSInteger, YTPlayerState) {
+    kYTPlayerStateUnstarted,
+    kYTPlayerStateEnded,
+    kYTPlayerStatePlaying,
+    kYTPlayerStatePaused,
+    kYTPlayerStateBuffering,
+    kYTPlayerStateQueued,
+    kYTPlayerStateUnknown
+};
+
+/** These enums represent the resolution of the currently loaded video. */
+typedef NS_ENUM(NSInteger, YTPlaybackQuality) {
+    kYTPlaybackQualitySmall,
+    kYTPlaybackQualityMedium,
+    kYTPlaybackQualityLarge,
+    kYTPlaybackQualityHD720,
+    kYTPlaybackQualityHD1080,
+    kYTPlaybackQualityHighRes,
+    kYTPlaybackQualityAuto, /** Addition for YouTube Live Events. */
+    kYTPlaybackQualityDefault,
+    kYTPlaybackQualityUnknown /** This should never be returned. It is here for future proofing. */
+};
+
+/** These enums represent error codes thrown by the player. */
+typedef NS_ENUM(NSInteger, YTPlayerError) {
+    kYTPlayerErrorInvalidParam,
+    kYTPlayerErrorHTML5Error,
+    kYTPlayerErrorVideoNotFound, // Functionally equivalent error codes 100 and
+    // 105 have been collapsed into |kYTPlayerErrorVideoNotFound|.
+    kYTPlayerErrorNotEmbeddable, // Functionally equivalent error codes 101 and
+    // 150 have been collapsed into |kYTPlayerErrorNotEmbeddable|.
+    kYTPlayerErrorUnknown
+};
+
+/**
+ * A delegate for ViewControllers to respond to YouTube player events outside
+ * of the view, such as changes to video playback state or playback errors.
+ * The callback functions correlate to the events fired by the IFrame API.
+ * For the full documentation, see the IFrame documentation here:
+ *     https://developers.google.com/youtube/iframe_api_reference#Events
+ */
+@protocol YTPlayerViewDelegate<NSObject>
+
+@optional
+/**
+ * Invoked when the player view is ready to receive API calls.
+ *
+ * @param playerView The YTPlayerView instance that has become ready.
+ */
+- (void)playerViewDidBecomeReady:(nonnull YTPlayerView *)playerView;
+
+/**
+ * Callback invoked when player state has changed, e.g. stopped or started playback.
+ *
+ * @param playerView The YTPlayerView instance where playback state has changed.
+ * @param state YTPlayerState designating the new playback state.
+ */
+- (void)playerView:(nonnull YTPlayerView *)playerView didChangeToState:(YTPlayerState)state;
+
+/**
+ * Callback invoked when playback quality has changed.
+ *
+ * @param playerView The YTPlayerView instance where playback quality has changed.
+ * @param quality YTPlaybackQuality designating the new playback quality.
+ */
+- (void)playerView:(nonnull YTPlayerView *)playerView didChangeToQuality:(YTPlaybackQuality)quality;
+
+/**
+ * Callback invoked when an error has occured.
+ *
+ * @param playerView The YTPlayerView instance where the error has occurred.
+ * @param error YTPlayerError containing the error state.
+ */
+- (void)playerView:(nonnull YTPlayerView *)playerView receivedError:(YTPlayerError)error;
+
+/**
+ * Callback invoked frequently when playBack is plaing.
+ *
+ * @param playerView The YTPlayerView instance where the error has occurred.
+ * @param playTime float containing curretn playback time.
+ */
+- (void)playerView:(nonnull YTPlayerView *)playerView didPlayTime:(float)playTime;
+
+/**
+ * Callback invoked when setting up the webview to allow custom colours so it fits in
+ * with app color schemes. If a transparent view is required specify clearColor and
+ * the code will handle the opacity etc.
+ * 
+ * @param playerView The YTPlayerView instance where the error has occurred.
+ * @return A color object that represents the background color of the webview.
+ */
+- (nonnull UIColor *)playerViewPreferredWebViewBackgroundColor:(nonnull YTPlayerView *)playerView;
+
+/**
+ * Callback invoked when initially loading the YouTube iframe to the webview to display a custom
+ * loading view while the player view is not ready. This loading view will be dismissed just before
+ * -playerViewDidBecomeReady: callback is invoked. The loading view will be automatically resized
+ * to cover the entire player view.
+ *
+ * The default implementation does not display any custom loading views so the player will display
+ * a blank view with a background color of (-playerViewPreferredWebViewBackgroundColor:).
+ * 
+ * Note that the custom loading view WILL NOT be displayed after iframe is loaded. It will be 
+ * handled by YouTube iframe API. This callback is just intended to tell users the view is actually
+ * doing something while iframe is being loaded, which will take some time if users are in poor networks.
+ *
+ * @param playerView The YTPlayerView instance where the error has occurred.
+ * @return A view object that will be displayed while YouTube iframe API is being loaded. 
+ *         Pass nil to display no custom loading view. Default implementation returns nil.
+ */
+- (nullable UIView *)playerViewPreferredInitialLoadingView:(nonnull YTPlayerView *)playerView;
+
+@end
+
+/**
+ * YTPlayerView is a custom UIView that client developers will use to include YouTube
+ * videos in their iOS applications. It can be instantiated programmatically, or via
+ * Interface Builder. Use the methods YTPlayerView::loadWithVideoId:,
+ * YTPlayerView::loadWithPlaylistId: or their variants to set the video or playlist
+ * to populate the view with.
+ */
+@interface YTPlayerView : UIView<UIWebViewDelegate>
+
+@property(nonatomic, strong, nullable, readonly) UIWebView *webView;
+
+/** A delegate to be notified on playback events. */
+@property(nonatomic, weak, nullable) id<YTPlayerViewDelegate> delegate;
+
+/**
+ * This method loads the player with the given video ID.
+ * This is a convenience method for calling YTPlayerView::loadPlayerWithVideoId:withPlayerVars:
+ * without player variables.
+ *
+ * This method reloads the entire contents of the UIWebView and regenerates its HTML contents.
+ * To change the currently loaded video without reloading the entire UIWebView, use the
+ * YTPlayerView::cueVideoById:startSeconds:suggestedQuality: family of methods.
+ *
+ * @param videoId The YouTube video ID of the video to load in the player view.
+ * @return YES if player has been configured correctly, NO otherwise.
+ */
+- (BOOL)loadWithVideoId:(nonnull NSString *)videoId;
+
+/**
+ * This method loads the player with the given playlist ID.
+ * This is a convenience method for calling YTPlayerView::loadWithPlaylistId:withPlayerVars:
+ * without player variables.
+ *
+ * This method reloads the entire contents of the UIWebView and regenerates its HTML contents.
+ * To change the currently loaded video without reloading the entire UIWebView, use the
+ * YTPlayerView::cuePlaylistByPlaylistId:index:startSeconds:suggestedQuality:
+ * family of methods.
+ *
+ * @param playlistId The YouTube playlist ID of the playlist to load in the player view.
+ * @return YES if player has been configured correctly, NO otherwise.
+ */
+- (BOOL)loadWithPlaylistId:(nonnull NSString *)playlistId;
+
+/**
+ * This method loads the player with the given video ID and player variables. Player variables
+ * specify optional parameters for video playback. For instance, to play a YouTube
+ * video inline, the following playerVars dictionary would be used:
+ *
+ * @code
+ * @{ @"playsinline" : @1 };
+ * @endcode
+ *
+ * Note that when the documentation specifies a valid value as a number (typically 0, 1 or 2),
+ * both strings and integers are valid values. The full list of parameters is defined at:
+ *   https://developers.google.com/youtube/player_parameters?playerVersion=HTML5.
+ *
+ * This method reloads the entire contents of the UIWebView and regenerates its HTML contents.
+ * To change the currently loaded video without reloading the entire UIWebView, use the
+ * YTPlayerView::cueVideoById:startSeconds:suggestedQuality: family of methods.
+ *
+ * @param videoId The YouTube video ID of the video to load in the player view.
+ * @param playerVars An NSDictionary of player parameters.
+ * @return YES if player has been configured correctly, NO otherwise.
+ */
+- (BOOL)loadWithVideoId:(nonnull NSString *)videoId playerVars:(nullable NSDictionary *)playerVars;
+
+/**
+ * This method loads the player with the given playlist ID and player variables. Player variables
+ * specify optional parameters for video playback. For instance, to play a YouTube
+ * video inline, the following playerVars dictionary would be used:
+ *
+ * @code
+ * @{ @"playsinline" : @1 };
+ * @endcode
+ *
+ * Note that when the documentation specifies a valid value as a number (typically 0, 1 or 2),
+ * both strings and integers are valid values. The full list of parameters is defined at:
+ *   https://developers.google.com/youtube/player_parameters?playerVersion=HTML5.
+ *
+ * This method reloads the entire contents of the UIWebView and regenerates its HTML contents.
+ * To change the currently loaded video without reloading the entire UIWebView, use the
+ * YTPlayerView::cuePlaylistByPlaylistId:index:startSeconds:suggestedQuality:
+ * family of methods.
+ *
+ * @param playlistId The YouTube playlist ID of the playlist to load in the player view.
+ * @param playerVars An NSDictionary of player parameters.
+ * @return YES if player has been configured correctly, NO otherwise.
+ */
+- (BOOL)loadWithPlaylistId:(nonnull NSString *)playlistId playerVars:(nullable NSDictionary *)playerVars;
+
+/**
+ * This method loads an iframe player with the given player parameters. Usually you may want to use
+ * -loadWithVideoId:playerVars: or -loadWithPlaylistId:playerVars: instead of this method does not handle
+ * video_id or playlist_id at all. The full list of parameters is defined at:
+ *   https://developers.google.com/youtube/player_parameters?playerVersion=HTML5.
+ *
+ * @param additionalPlayerParams An NSDictionary of parameters in addition to required parameters
+ *                               to instantiate the HTML5 player with. This differs depending on
+ *                               whether a single video or playlist is being loaded.
+ * @return YES if successful, NO if not.
+ */
+- (BOOL)loadWithPlayerParams:(nullable NSDictionary *)additionalPlayerParams;
+
+#pragma mark - Player controls
+
+// These methods correspond to their JavaScript equivalents as documented here:
+//   https://developers.google.com/youtube/iframe_api_reference#Playback_controls
+
+/**
+ * Starts or resumes playback on the loaded video. Corresponds to this method from
+ * the JavaScript API:
+ *   https://developers.google.com/youtube/iframe_api_reference#playVideo
+ */
+- (void)playVideo;
+
+/**
+ * Pauses playback on a playing video. Corresponds to this method from
+ * the JavaScript API:
+ *   https://developers.google.com/youtube/iframe_api_reference#pauseVideo
+ */
+- (void)pauseVideo;
+
+/**
+ * Stops playback on a playing video. Corresponds to this method from
+ * the JavaScript API:
+ *   https://developers.google.com/youtube/iframe_api_reference#stopVideo
+ */
+- (void)stopVideo;
+
+/**
+ * Seek to a given time on a playing video. Corresponds to this method from
+ * the JavaScript API:
+ *   https://developers.google.com/youtube/iframe_api_reference#seekTo
+ *
+ * @param seekToSeconds The time in seconds to seek to in the loaded video.
+ * @param allowSeekAhead Whether to make a new request to the server if the time is
+ *                       outside what is currently buffered. Recommended to set to YES.
+ */
+- (void)seekToSeconds:(float)seekToSeconds allowSeekAhead:(BOOL)allowSeekAhead;
+
+#pragma mark - Queuing videos
+
+// Queueing functions for videos. These methods correspond to their JavaScript
+// equivalents as documented here:
+//   https://developers.google.com/youtube/iframe_api_reference#Queueing_Functions
+
+/**
+ * Cues a given video by its video ID for playback starting at the given time and with the
+ * suggested quality. Cueing loads a video, but does not start video playback. This method
+ * corresponds with its JavaScript API equivalent as documented here:
+ *    https://developers.google.com/youtube/iframe_api_reference#cueVideoById
+ *
+ * @param videoId A video ID to cue.
+ * @param startSeconds Time in seconds to start the video when YTPlayerView::playVideo is called.
+ * @param suggestedQuality YTPlaybackQuality value suggesting a playback quality.
+ */
+- (void)cueVideoById:(nonnull NSString *)videoId
+        startSeconds:(float)startSeconds
+    suggestedQuality:(YTPlaybackQuality)suggestedQuality;
+
+/**
+ * Cues a given video by its video ID for playback starting and ending at the given times
+ * with the suggested quality. Cueing loads a video, but does not start video playback. This
+ * method corresponds with its JavaScript API equivalent as documented here:
+ *    https://developers.google.com/youtube/iframe_api_reference#cueVideoById
+ *
+ * @param videoId A video ID to cue.
+ * @param startSeconds Time in seconds to start the video when playVideo() is called.
+ * @param endSeconds Time in seconds to end the video after it begins playing.
+ * @param suggestedQuality YTPlaybackQuality value suggesting a playback quality.
+ */
+- (void)cueVideoById:(nonnull NSString *)videoId
+        startSeconds:(float)startSeconds
+          endSeconds:(float)endSeconds
+    suggestedQuality:(YTPlaybackQuality)suggestedQuality;
+
+/**
+ * Loads a given video by its video ID for playback starting at the given time and with the
+ * suggested quality. Loading a video both loads it and begins playback. This method
+ * corresponds with its JavaScript API equivalent as documented here:
+ *    https://developers.google.com/youtube/iframe_api_reference#loadVideoById
+ *
+ * @param videoId A video ID to load and begin playing.
+ * @param startSeconds Time in seconds to start the video when it has loaded.
+ * @param suggestedQuality YTPlaybackQuality value suggesting a playback quality.
+ */
+- (void)loadVideoById:(nonnull NSString *)videoId
+         startSeconds:(float)startSeconds
+     suggestedQuality:(YTPlaybackQuality)suggestedQuality;
+
+/**
+ * Loads a given video by its video ID for playback starting and ending at the given times
+ * with the suggested quality. Loading a video both loads it and begins playback. This method
+ * corresponds with its JavaScript API equivalent as documented here:
+ *    https://developers.google.com/youtube/iframe_api_reference#loadVideoById
+ *
+ * @param videoId A video ID to load and begin playing.
+ * @param startSeconds Time in seconds to start the video when it has loaded.
+ * @param endSeconds Time in seconds to end the video after it begins playing.
+ * @param suggestedQuality YTPlaybackQuality value suggesting a playback quality.
+ */
+- (void)loadVideoById:(nonnull NSString *)videoId
+         startSeconds:(float)startSeconds
+           endSeconds:(float)endSeconds
+     suggestedQuality:(YTPlaybackQuality)suggestedQuality;
+
+/**
+ * Cues a given video by its URL on YouTube.com for playback starting at the given time
+ * and with the suggested quality. Cueing loads a video, but does not start video playback.
+ * This method corresponds with its JavaScript API equivalent as documented here:
+ *    https://developers.google.com/youtube/iframe_api_reference#cueVideoByUrl
+ *
+ * @param videoURL URL of a YouTube video to cue for playback.
+ * @param startSeconds Time in seconds to start the video when YTPlayerView::playVideo is called.
+ * @param suggestedQuality YTPlaybackQuality value suggesting a playback quality.
+ */
+- (void)cueVideoByURL:(nonnull NSString *)videoURL
+         startSeconds:(float)startSeconds
+     suggestedQuality:(YTPlaybackQuality)suggestedQuality;
+
+/**
+ * Cues a given video by its URL on YouTube.com for playback starting at the given time
+ * and with the suggested quality. Cueing loads a video, but does not start video playback.
+ * This method corresponds with its JavaScript API equivalent as documented here:
+ *    https://developers.google.com/youtube/iframe_api_reference#cueVideoByUrl
+ *
+ * @param videoURL URL of a YouTube video to cue for playback.
+ * @param startSeconds Time in seconds to start the video when YTPlayerView::playVideo is called.
+ * @param endSeconds Time in seconds to end the video after it begins playing.
+ * @param suggestedQuality YTPlaybackQuality value suggesting a playback quality.
+ */
+- (void)cueVideoByURL:(nonnull NSString *)videoURL
+         startSeconds:(float)startSeconds
+           endSeconds:(float)endSeconds
+     suggestedQuality:(YTPlaybackQuality)suggestedQuality;
+
+/**
+ * Loads a given video by its video ID for playback starting at the given time
+ * with the suggested quality. Loading a video both loads it and begins playback. This method
+ * corresponds with its JavaScript API equivalent as documented here:
+ *    https://developers.google.com/youtube/iframe_api_reference#loadVideoByUrl
+ *
+ * @param videoURL URL of a YouTube video to load and play.
+ * @param startSeconds Time in seconds to start the video when it has loaded.
+ * @param suggestedQuality YTPlaybackQuality value suggesting a playback quality.
+ */
+- (void)loadVideoByURL:(nonnull NSString *)videoURL
+          startSeconds:(float)startSeconds
+      suggestedQuality:(YTPlaybackQuality)suggestedQuality;
+
+/**
+ * Loads a given video by its video ID for playback starting and ending at the given times
+ * with the suggested quality. Loading a video both loads it and begins playback. This method
+ * corresponds with its JavaScript API equivalent as documented here:
+ *    https://developers.google.com/youtube/iframe_api_reference#loadVideoByUrl
+ *
+ * @param videoURL URL of a YouTube video to load and play.
+ * @param startSeconds Time in seconds to start the video when it has loaded.
+ * @param endSeconds Time in seconds to end the video after it begins playing.
+ * @param suggestedQuality YTPlaybackQuality value suggesting a playback quality.
+ */
+- (void)loadVideoByURL:(nonnull NSString *)videoURL
+          startSeconds:(float)startSeconds
+            endSeconds:(float)endSeconds
+      suggestedQuality:(YTPlaybackQuality)suggestedQuality;
+
+#pragma mark - Queuing functions for playlists
+
+// Queueing functions for playlists. These methods correspond to
+// the JavaScript methods defined here:
+//    https://developers.google.com/youtube/js_api_reference#Playlist_Queueing_Functions
+
+/**
+ * Cues a given playlist with the given ID. The |index| parameter specifies the 0-indexed
+ * position of the first video to play, starting at the given time and with the
+ * suggested quality. Cueing loads a playlist, but does not start video playback. This method
+ * corresponds with its JavaScript API equivalent as documented here:
+ *    https://developers.google.com/youtube/iframe_api_reference#cuePlaylist
+ *
+ * @param playlistId Playlist ID of a YouTube playlist to cue.
+ * @param index A 0-indexed position specifying the first video to play.
+ * @param startSeconds Time in seconds to start the video when YTPlayerView::playVideo is called.
+ * @param suggestedQuality YTPlaybackQuality value suggesting a playback quality.
+ */
+- (void)cuePlaylistByPlaylistId:(nonnull NSString *)playlistId
+                          index:(int)index
+                   startSeconds:(float)startSeconds
+               suggestedQuality:(YTPlaybackQuality)suggestedQuality;
+
+/**
+ * Cues a playlist of videos with the given video IDs. The |index| parameter specifies the
+ * 0-indexed position of the first video to play, starting at the given time and with the
+ * suggested quality. Cueing loads a playlist, but does not start video playback. This method
+ * corresponds with its JavaScript API equivalent as documented here:
+ *    https://developers.google.com/youtube/iframe_api_reference#cuePlaylist
+ *
+ * @param videoIds An NSArray of video IDs to compose the playlist of.
+ * @param index A 0-indexed position specifying the first video to play.
+ * @param startSeconds Time in seconds to start the video when YTPlayerView::playVideo is called.
+ * @param suggestedQuality YTPlaybackQuality value suggesting a playback quality.
+ */
+- (void)cuePlaylistByVideos:(nonnull NSArray *)videoIds
+                      index:(int)index
+               startSeconds:(float)startSeconds
+           suggestedQuality:(YTPlaybackQuality)suggestedQuality;
+
+/**
+ * Loads a given playlist with the given ID. The |index| parameter specifies the 0-indexed
+ * position of the first video to play, starting at the given time and with the
+ * suggested quality. Loading a playlist starts video playback. This method
+ * corresponds with its JavaScript API equivalent as documented here:
+ *    https://developers.google.com/youtube/iframe_api_reference#loadPlaylist
+ *
+ * @param playlistId Playlist ID of a YouTube playlist to cue.
+ * @param index A 0-indexed position specifying the first video to play.
+ * @param startSeconds Time in seconds to start the video when YTPlayerView::playVideo is called.
+ * @param suggestedQuality YTPlaybackQuality value suggesting a playback quality.
+ */
+- (void)loadPlaylistByPlaylistId:(nonnull NSString *)playlistId
+                           index:(int)index
+                    startSeconds:(float)startSeconds
+                suggestedQuality:(YTPlaybackQuality)suggestedQuality;
+
+/**
+ * Loads a playlist of videos with the given video IDs. The |index| parameter specifies the
+ * 0-indexed position of the first video to play, starting at the given time and with the
+ * suggested quality. Loading a playlist starts video playback. This method
+ * corresponds with its JavaScript API equivalent as documented here:
+ *    https://developers.google.com/youtube/iframe_api_reference#loadPlaylist
+ *
+ * @param videoIds An NSArray of video IDs to compose the playlist of.
+ * @param index A 0-indexed position specifying the first video to play.
+ * @param startSeconds Time in seconds to start the video when YTPlayerView::playVideo is called.
+ * @param suggestedQuality YTPlaybackQuality value suggesting a playback quality.
+ */
+- (void)loadPlaylistByVideos:(nonnull NSArray *)videoIds
+                       index:(int)index
+                startSeconds:(float)startSeconds
+            suggestedQuality:(YTPlaybackQuality)suggestedQuality;
+
+#pragma mark - Playing a video in a playlist
+
+// These methods correspond to the JavaScript API as defined under the
+// "Playing a video in a playlist" section here:
+//    https://developers.google.com/youtube/iframe_api_reference#Playback_status
+
+/**
+ * Loads and plays the next video in the playlist. Corresponds to this method from
+ * the JavaScript API:
+ *   https://developers.google.com/youtube/iframe_api_reference#nextVideo
+ */
+- (void)nextVideo;
+
+/**
+ * Loads and plays the previous video in the playlist. Corresponds to this method from
+ * the JavaScript API:
+ *   https://developers.google.com/youtube/iframe_api_reference#previousVideo
+ */
+- (void)previousVideo;
+
+/**
+ * Loads and plays the video at the given 0-indexed position in the playlist.
+ * Corresponds to this method from the JavaScript API:
+ *   https://developers.google.com/youtube/iframe_api_reference#playVideoAt
+ *
+ * @param index The 0-indexed position of the video in the playlist to load and play.
+ */
+- (void)playVideoAt:(int)index;
+
+#pragma mark - Setting the playback rate
+
+/**
+ * Gets the playback rate. The default value is 1.0, which represents a video
+ * playing at normal speed. Other values may include 0.25 or 0.5 for slower
+ * speeds, and 1.5 or 2.0 for faster speeds. This method corresponds to the
+ * JavaScript API defined here:
+ *   https://developers.google.com/youtube/iframe_api_reference#getPlaybackRate
+ *
+ * @return An integer value between 0 and 100 representing the current volume.
+ */
+- (float)playbackRate;
+
+/**
+ * Sets the playback rate. The default value is 1.0, which represents a video
+ * playing at normal speed. Other values may include 0.25 or 0.5 for slower
+ * speeds, and 1.5 or 2.0 for faster speeds. To fetch a list of valid values for
+ * this method, call YTPlayerView::getAvailablePlaybackRates. This method does not
+ * guarantee that the playback rate will change.
+ * This method corresponds to the JavaScript API defined here:
+ *   https://developers.google.com/youtube/iframe_api_reference#setPlaybackRate
+ *
+ * @param suggestedRate A playback rate to suggest for the player.
+ */
+- (void)setPlaybackRate:(float)suggestedRate;
+
+/**
+ * Gets a list of the valid playback rates, useful in conjunction with
+ * YTPlayerView::setPlaybackRate. This method corresponds to the
+ * JavaScript API defined here:
+ *   https://developers.google.com/youtube/iframe_api_reference#getPlaybackRate
+ *
+ * @return An NSArray containing available playback rates. nil if there is an error.
+ */
+- (nullable NSArray *)availablePlaybackRates;
+
+#pragma mark - Setting playback behavior for playlists
+
+/**
+ * Sets whether the player should loop back to the first video in the playlist
+ * after it has finished playing the last video. This method corresponds to the
+ * JavaScript API defined here:
+ *   https://developers.google.com/youtube/iframe_api_reference#loopPlaylist
+ *
+ * @param loop A boolean representing whether the player should loop.
+ */
+- (void)setLoop:(BOOL)loop;
+
+/**
+ * Sets whether the player should shuffle through the playlist. This method
+ * corresponds to the JavaScript API defined here:
+ *   https://developers.google.com/youtube/iframe_api_reference#shufflePlaylist
+ *
+ * @param shuffle A boolean representing whether the player should
+ *                shuffle through the playlist.
+ */
+- (void)setShuffle:(BOOL)shuffle;
+
+#pragma mark - Playback status
+// These methods correspond to the JavaScript methods defined here:
+//    https://developers.google.com/youtube/js_api_reference#Playback_status
+
+/**
+ * Returns a number between 0 and 1 that specifies the percentage of the video
+ * that the player shows as buffered. This method corresponds to the
+ * JavaScript API defined here:
+ *   https://developers.google.com/youtube/iframe_api_reference#getVideoLoadedFraction
+ *
+ * @return A float value between 0 and 1 representing the percentage of the video
+ *         already loaded.
+ */
+- (float)videoLoadedFraction;
+
+/**
+ * Returns the state of the player. This method corresponds to the
+ * JavaScript API defined here:
+ *   https://developers.google.com/youtube/iframe_api_reference#getPlayerState
+ *
+ * @return |YTPlayerState| representing the state of the player.
+ */
+- (YTPlayerState)playerState;
+
+/**
+ * Returns the elapsed time in seconds since the video started playing. This
+ * method corresponds to the JavaScript API defined here:
+ *   https://developers.google.com/youtube/iframe_api_reference#getCurrentTime
+ *
+ * @return Time in seconds since the video started playing.
+ */
+- (float)currentTime;
+
+#pragma mark - Playback quality
+
+// Playback quality. These methods correspond to the JavaScript
+// methods defined here:
+//   https://developers.google.com/youtube/js_api_reference#Playback_quality
+
+/**
+ * Returns the playback quality. This method corresponds to the
+ * JavaScript API defined here:
+ *   https://developers.google.com/youtube/iframe_api_reference#getPlaybackQuality
+ *
+ * @return YTPlaybackQuality representing the current playback quality.
+ */
+- (YTPlaybackQuality)playbackQuality;
+
+/**
+ * Suggests playback quality for the video. It is recommended to leave this setting to
+ * |default|. This method corresponds to the JavaScript API defined here:
+ *   https://developers.google.com/youtube/iframe_api_reference#setPlaybackQuality
+ *
+ * @param quality YTPlaybackQuality value to suggest for the player.
+ */
+- (void)setPlaybackQuality:(YTPlaybackQuality)suggestedQuality;
+
+/**
+ * Gets a list of the valid playback quality values, useful in conjunction with
+ * YTPlayerView::setPlaybackQuality. This method corresponds to the
+ * JavaScript API defined here:
+ *   https://developers.google.com/youtube/iframe_api_reference#getAvailableQualityLevels
+ *
+ * @return An NSArray containing available playback quality levels. Returns nil if there is an error.
+ */
+- (nullable NSArray *)availableQualityLevels;
+
+#pragma mark - Retrieving video information
+
+// Retrieving video information. These methods correspond to the JavaScript
+// methods defined here:
+//   https://developers.google.com/youtube/js_api_reference#Retrieving_video_information
+
+/**
+ * Returns the duration in seconds since the video of the video. This
+ * method corresponds to the JavaScript API defined here:
+ *   https://developers.google.com/youtube/iframe_api_reference#getDuration
+ *
+ * @return Length of the video in seconds.
+ */
+- (NSTimeInterval)duration;
+
+/**
+ * Returns the YouTube.com URL for the video. This method corresponds
+ * to the JavaScript API defined here:
+ *   https://developers.google.com/youtube/iframe_api_reference#getVideoUrl
+ *
+ * @return The YouTube.com URL for the video. Returns nil if no video is loaded yet.
+ */
+- (nullable NSURL *)videoUrl;
+
+/**
+ * Returns the embed code for the current video. This method corresponds
+ * to the JavaScript API defined here:
+ *   https://developers.google.com/youtube/iframe_api_reference#getVideoEmbedCode
+ *
+ * @return The embed code for the current video. Returns nil if no video is loaded yet.
+ */
+- (nullable NSString *)videoEmbedCode;
+
+#pragma mark - Retrieving playlist information
+
+// Retrieving playlist information. These methods correspond to the
+// JavaScript defined here:
+//    https://developers.google.com/youtube/js_api_reference#Retrieving_playlist_information
+
+/**
+ * Returns an ordered array of video IDs in the playlist. This method corresponds
+ * to the JavaScript API defined here:
+ *   https://developers.google.com/youtube/iframe_api_reference#getPlaylist
+ *
+ * @return An NSArray containing all the video IDs in the current playlist. |nil| on error.
+ */
+- (nullable NSArray *)playlist;
+
+/**
+ * Returns the 0-based index of the currently playing item in the playlist.
+ * This method corresponds to the JavaScript API defined here:
+ *   https://developers.google.com/youtube/iframe_api_reference#getPlaylistIndex
+ *
+ * @return The 0-based index of the currently playing item in the playlist.
+ */
+- (int)playlistIndex;
+
+#pragma mark - Exposed for Testing
+
+/**
+ * Removes the internal web view from this player view.
+ * Intended to use for testing, should not be used in production code.
+ */
+- (void)removeWebView;
+
+
+
+-(NSString*) Embed2VID:(NSString*) iframe_embed;
+@end

+ 918 - 0
RedAnt ERP Mobile/common/youtube/YTPlayerView.m

@@ -0,0 +1,918 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import "YTPlayerView.h"
+
+// These are instances of NSString because we get them from parsing a URL. It would be silly to
+// convert these into an integer just to have to convert the URL query string value into an integer
+// as well for the sake of doing a value comparison. A full list of response error codes can be
+// found here:
+//      https://developers.google.com/youtube/iframe_api_reference
+NSString static *const kYTPlayerStateUnstartedCode = @"-1";
+NSString static *const kYTPlayerStateEndedCode = @"0";
+NSString static *const kYTPlayerStatePlayingCode = @"1";
+NSString static *const kYTPlayerStatePausedCode = @"2";
+NSString static *const kYTPlayerStateBufferingCode = @"3";
+NSString static *const kYTPlayerStateCuedCode = @"5";
+NSString static *const kYTPlayerStateUnknownCode = @"unknown";
+
+// Constants representing playback quality.
+NSString static *const kYTPlaybackQualitySmallQuality = @"small";
+NSString static *const kYTPlaybackQualityMediumQuality = @"medium";
+NSString static *const kYTPlaybackQualityLargeQuality = @"large";
+NSString static *const kYTPlaybackQualityHD720Quality = @"hd720";
+NSString static *const kYTPlaybackQualityHD1080Quality = @"hd1080";
+NSString static *const kYTPlaybackQualityHighResQuality = @"highres";
+NSString static *const kYTPlaybackQualityAutoQuality = @"auto";
+NSString static *const kYTPlaybackQualityDefaultQuality = @"default";
+NSString static *const kYTPlaybackQualityUnknownQuality = @"unknown";
+
+// Constants representing YouTube player errors.
+NSString static *const kYTPlayerErrorInvalidParamErrorCode = @"2";
+NSString static *const kYTPlayerErrorHTML5ErrorCode = @"5";
+NSString static *const kYTPlayerErrorVideoNotFoundErrorCode = @"100";
+NSString static *const kYTPlayerErrorNotEmbeddableErrorCode = @"101";
+NSString static *const kYTPlayerErrorCannotFindVideoErrorCode = @"105";
+NSString static *const kYTPlayerErrorSameAsNotEmbeddableErrorCode = @"150";
+
+// Constants representing player callbacks.
+NSString static *const kYTPlayerCallbackOnReady = @"onReady";
+NSString static *const kYTPlayerCallbackOnStateChange = @"onStateChange";
+NSString static *const kYTPlayerCallbackOnPlaybackQualityChange = @"onPlaybackQualityChange";
+NSString static *const kYTPlayerCallbackOnError = @"onError";
+NSString static *const kYTPlayerCallbackOnPlayTime = @"onPlayTime";
+
+NSString static *const kYTPlayerCallbackOnYouTubeIframeAPIReady = @"onYouTubeIframeAPIReady";
+NSString static *const kYTPlayerCallbackOnYouTubeIframeAPIFailedToLoad = @"onYouTubeIframeAPIFailedToLoad";
+
+NSString static *const kYTPlayerEmbedUrlRegexPattern = @"^http(s)://(www.)youtube.com/embed/(.*)$";
+NSString static *const kYTPlayerAdUrlRegexPattern = @"^http(s)://pubads.g.doubleclick.net/pagead/conversion/";
+NSString static *const kYTPlayerOAuthRegexPattern = @"^http(s)://accounts.google.com/o/oauth2/(.*)$";
+NSString static *const kYTPlayerStaticProxyRegexPattern = @"^https://content.googleapis.com/static/proxy.html(.*)$";
+NSString static *const kYTPlayerSyndicationRegexPattern = @"^https://tpc.googlesyndication.com/sodar/(.*).html$";
+
+@interface YTPlayerView()
+
+@property (nonatomic, strong) NSURL *originURL;
+@property (nonatomic, weak) UIView *initialLoadingView;
+
+@end
+
+@implementation YTPlayerView
+
+- (BOOL)loadWithVideoId:(NSString *)videoId {
+  return [self loadWithVideoId:videoId playerVars:nil];
+}
+
+- (BOOL)loadWithPlaylistId:(NSString *)playlistId {
+  return [self loadWithPlaylistId:playlistId playerVars:nil];
+}
+
+- (BOOL)loadWithVideoId:(NSString *)videoId playerVars:(NSDictionary *)playerVars {
+  if (!playerVars) {
+    playerVars = @{};
+  }
+  NSDictionary *playerParams = @{ @"videoId" : videoId, @"playerVars" : playerVars };
+  return [self loadWithPlayerParams:playerParams];
+}
+
+- (BOOL)loadWithPlaylistId:(NSString *)playlistId playerVars:(NSDictionary *)playerVars {
+
+  // Mutable copy because we may have been passed an immutable config dictionary.
+  NSMutableDictionary *tempPlayerVars = [[NSMutableDictionary alloc] init];
+  [tempPlayerVars setValue:@"playlist" forKey:@"listType"];
+  [tempPlayerVars setValue:playlistId forKey:@"list"];
+  if (playerVars) {
+    [tempPlayerVars addEntriesFromDictionary:playerVars];
+  }
+
+  NSDictionary *playerParams = @{ @"playerVars" : tempPlayerVars };
+  return [self loadWithPlayerParams:playerParams];
+}
+
+#pragma mark - Player methods
+
+- (void)playVideo {
+  [self stringFromEvaluatingJavaScript:@"player.playVideo();"];
+}
+
+- (void)pauseVideo {
+  [self notifyDelegateOfYouTubeCallbackUrl:[NSURL URLWithString:[NSString stringWithFormat:@"ytplayer://onStateChange?data=%@", kYTPlayerStatePausedCode]]];
+  [self stringFromEvaluatingJavaScript:@"player.pauseVideo();"];
+}
+
+- (void)stopVideo {
+  [self stringFromEvaluatingJavaScript:@"player.stopVideo();"];
+}
+
+- (void)seekToSeconds:(float)seekToSeconds allowSeekAhead:(BOOL)allowSeekAhead {
+  NSNumber *secondsValue = [NSNumber numberWithFloat:seekToSeconds];
+  NSString *allowSeekAheadValue = [self stringForJSBoolean:allowSeekAhead];
+  NSString *command = [NSString stringWithFormat:@"player.seekTo(%@, %@);", secondsValue, allowSeekAheadValue];
+  [self stringFromEvaluatingJavaScript:command];
+}
+
+#pragma mark - Cueing methods
+
+- (void)cueVideoById:(NSString *)videoId
+        startSeconds:(float)startSeconds
+    suggestedQuality:(YTPlaybackQuality)suggestedQuality {
+  NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds];
+  NSString *qualityValue = [YTPlayerView stringForPlaybackQuality:suggestedQuality];
+  NSString *command = [NSString stringWithFormat:@"player.cueVideoById('%@', %@, '%@');",
+      videoId, startSecondsValue, qualityValue];
+  [self stringFromEvaluatingJavaScript:command];
+}
+
+- (void)cueVideoById:(NSString *)videoId
+        startSeconds:(float)startSeconds
+          endSeconds:(float)endSeconds
+    suggestedQuality:(YTPlaybackQuality)suggestedQuality {
+  NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds];
+  NSNumber *endSecondsValue = [NSNumber numberWithFloat:endSeconds];
+  NSString *qualityValue = [YTPlayerView stringForPlaybackQuality:suggestedQuality];
+  NSString *command = [NSString stringWithFormat:@"player.cueVideoById({'videoId': '%@', 'startSeconds': %@, 'endSeconds': %@, 'suggestedQuality': '%@'});", videoId, startSecondsValue, endSecondsValue, qualityValue];
+  [self stringFromEvaluatingJavaScript:command];
+}
+
+- (void)loadVideoById:(NSString *)videoId
+         startSeconds:(float)startSeconds
+     suggestedQuality:(YTPlaybackQuality)suggestedQuality {
+  NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds];
+  NSString *qualityValue = [YTPlayerView stringForPlaybackQuality:suggestedQuality];
+  NSString *command = [NSString stringWithFormat:@"player.loadVideoById('%@', %@, '%@');",
+      videoId, startSecondsValue, qualityValue];
+  [self stringFromEvaluatingJavaScript:command];
+}
+
+- (void)loadVideoById:(NSString *)videoId
+         startSeconds:(float)startSeconds
+           endSeconds:(float)endSeconds
+     suggestedQuality:(YTPlaybackQuality)suggestedQuality {
+  NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds];
+  NSNumber *endSecondsValue = [NSNumber numberWithFloat:endSeconds];
+  NSString *qualityValue = [YTPlayerView stringForPlaybackQuality:suggestedQuality];
+  NSString *command = [NSString stringWithFormat:@"player.loadVideoById({'videoId': '%@', 'startSeconds': %@, 'endSeconds': %@, 'suggestedQuality': '%@'});",videoId, startSecondsValue, endSecondsValue, qualityValue];
+  [self stringFromEvaluatingJavaScript:command];
+}
+
+- (void)cueVideoByURL:(NSString *)videoURL
+         startSeconds:(float)startSeconds
+     suggestedQuality:(YTPlaybackQuality)suggestedQuality {
+  NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds];
+  NSString *qualityValue = [YTPlayerView stringForPlaybackQuality:suggestedQuality];
+  NSString *command = [NSString stringWithFormat:@"player.cueVideoByUrl('%@', %@, '%@');",
+      videoURL, startSecondsValue, qualityValue];
+  [self stringFromEvaluatingJavaScript:command];
+}
+
+- (void)cueVideoByURL:(NSString *)videoURL
+         startSeconds:(float)startSeconds
+           endSeconds:(float)endSeconds
+     suggestedQuality:(YTPlaybackQuality)suggestedQuality {
+  NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds];
+  NSNumber *endSecondsValue = [NSNumber numberWithFloat:endSeconds];
+  NSString *qualityValue = [YTPlayerView stringForPlaybackQuality:suggestedQuality];
+  NSString *command = [NSString stringWithFormat:@"player.cueVideoByUrl('%@', %@, %@, '%@');",
+      videoURL, startSecondsValue, endSecondsValue, qualityValue];
+  [self stringFromEvaluatingJavaScript:command];
+}
+
+- (void)loadVideoByURL:(NSString *)videoURL
+          startSeconds:(float)startSeconds
+      suggestedQuality:(YTPlaybackQuality)suggestedQuality {
+  NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds];
+  NSString *qualityValue = [YTPlayerView stringForPlaybackQuality:suggestedQuality];
+  NSString *command = [NSString stringWithFormat:@"player.loadVideoByUrl('%@', %@, '%@');",
+      videoURL, startSecondsValue, qualityValue];
+  [self stringFromEvaluatingJavaScript:command];
+}
+
+- (void)loadVideoByURL:(NSString *)videoURL
+          startSeconds:(float)startSeconds
+            endSeconds:(float)endSeconds
+      suggestedQuality:(YTPlaybackQuality)suggestedQuality {
+  NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds];
+  NSNumber *endSecondsValue = [NSNumber numberWithFloat:endSeconds];
+  NSString *qualityValue = [YTPlayerView stringForPlaybackQuality:suggestedQuality];
+  NSString *command = [NSString stringWithFormat:@"player.loadVideoByUrl('%@', %@, %@, '%@');",
+      videoURL, startSecondsValue, endSecondsValue, qualityValue];
+  [self stringFromEvaluatingJavaScript:command];
+}
+
+#pragma mark - Cueing methods for lists
+
+- (void)cuePlaylistByPlaylistId:(NSString *)playlistId
+                          index:(int)index
+                   startSeconds:(float)startSeconds
+               suggestedQuality:(YTPlaybackQuality)suggestedQuality {
+  NSString *playlistIdString = [NSString stringWithFormat:@"'%@'", playlistId];
+  [self cuePlaylist:playlistIdString
+                 index:index
+          startSeconds:startSeconds
+      suggestedQuality:suggestedQuality];
+}
+
+- (void)cuePlaylistByVideos:(NSArray *)videoIds
+                      index:(int)index
+               startSeconds:(float)startSeconds
+           suggestedQuality:(YTPlaybackQuality)suggestedQuality {
+  [self cuePlaylist:[self stringFromVideoIdArray:videoIds]
+                 index:index
+          startSeconds:startSeconds
+      suggestedQuality:suggestedQuality];
+}
+
+- (void)loadPlaylistByPlaylistId:(NSString *)playlistId
+                           index:(int)index
+                    startSeconds:(float)startSeconds
+                suggestedQuality:(YTPlaybackQuality)suggestedQuality {
+  NSString *playlistIdString = [NSString stringWithFormat:@"'%@'", playlistId];
+  [self loadPlaylist:playlistIdString
+                 index:index
+          startSeconds:startSeconds
+      suggestedQuality:suggestedQuality];
+}
+
+- (void)loadPlaylistByVideos:(NSArray *)videoIds
+                       index:(int)index
+                startSeconds:(float)startSeconds
+            suggestedQuality:(YTPlaybackQuality)suggestedQuality {
+  [self loadPlaylist:[self stringFromVideoIdArray:videoIds]
+                 index:index
+          startSeconds:startSeconds
+      suggestedQuality:suggestedQuality];
+}
+
+#pragma mark - Setting the playback rate
+
+- (float)playbackRate {
+  NSString *returnValue = [self stringFromEvaluatingJavaScript:@"player.getPlaybackRate();"];
+  return [returnValue floatValue];
+}
+
+- (void)setPlaybackRate:(float)suggestedRate {
+  NSString *command = [NSString stringWithFormat:@"player.setPlaybackRate(%f);", suggestedRate];
+  [self stringFromEvaluatingJavaScript:command];
+}
+
+- (NSArray *)availablePlaybackRates {
+  NSString *returnValue =
+      [self stringFromEvaluatingJavaScript:@"player.getAvailablePlaybackRates();"];
+
+  NSData *playbackRateData = [returnValue dataUsingEncoding:NSUTF8StringEncoding];
+  NSError *jsonDeserializationError;
+  NSArray *playbackRates = [NSJSONSerialization JSONObjectWithData:playbackRateData
+                                                           options:kNilOptions
+                                                             error:&jsonDeserializationError];
+  if (jsonDeserializationError) {
+    return nil;
+  }
+
+  return playbackRates;
+}
+
+#pragma mark - Setting playback behavior for playlists
+
+- (void)setLoop:(BOOL)loop {
+  NSString *loopPlayListValue = [self stringForJSBoolean:loop];
+  NSString *command = [NSString stringWithFormat:@"player.setLoop(%@);", loopPlayListValue];
+  [self stringFromEvaluatingJavaScript:command];
+}
+
+- (void)setShuffle:(BOOL)shuffle {
+  NSString *shufflePlayListValue = [self stringForJSBoolean:shuffle];
+  NSString *command = [NSString stringWithFormat:@"player.setShuffle(%@);", shufflePlayListValue];
+  [self stringFromEvaluatingJavaScript:command];
+}
+
+#pragma mark - Playback status
+
+- (float)videoLoadedFraction {
+  return [[self stringFromEvaluatingJavaScript:@"player.getVideoLoadedFraction();"] floatValue];
+}
+
+- (YTPlayerState)playerState {
+  NSString *returnValue = [self stringFromEvaluatingJavaScript:@"player.getPlayerState();"];
+  return [YTPlayerView playerStateForString:returnValue];
+}
+
+- (float)currentTime {
+  return [[self stringFromEvaluatingJavaScript:@"player.getCurrentTime();"] floatValue];
+}
+
+// Playback quality
+- (YTPlaybackQuality)playbackQuality {
+  NSString *qualityValue = [self stringFromEvaluatingJavaScript:@"player.getPlaybackQuality();"];
+  return [YTPlayerView playbackQualityForString:qualityValue];
+}
+
+- (void)setPlaybackQuality:(YTPlaybackQuality)suggestedQuality {
+  NSString *qualityValue = [YTPlayerView stringForPlaybackQuality:suggestedQuality];
+  NSString *command = [NSString stringWithFormat:@"player.setPlaybackQuality('%@');", qualityValue];
+  [self stringFromEvaluatingJavaScript:command];
+}
+
+#pragma mark - Video information methods
+
+- (NSTimeInterval)duration {
+  return [[self stringFromEvaluatingJavaScript:@"player.getDuration();"] doubleValue];
+}
+
+- (NSURL *)videoUrl {
+  return [NSURL URLWithString:[self stringFromEvaluatingJavaScript:@"player.getVideoUrl();"]];
+}
+
+- (NSString *)videoEmbedCode {
+  return [self stringFromEvaluatingJavaScript:@"player.getVideoEmbedCode();"];
+}
+
+#pragma mark - Playlist methods
+
+- (NSArray *)playlist {
+  NSString *returnValue = [self stringFromEvaluatingJavaScript:@"player.getPlaylist();"];
+
+  NSData *playlistData = [returnValue dataUsingEncoding:NSUTF8StringEncoding];
+  NSError *jsonDeserializationError;
+  NSArray *videoIds = [NSJSONSerialization JSONObjectWithData:playlistData
+                                                      options:kNilOptions
+                                                        error:&jsonDeserializationError];
+  if (jsonDeserializationError) {
+    return nil;
+  }
+
+  return videoIds;
+}
+
+- (int)playlistIndex {
+  NSString *returnValue = [self stringFromEvaluatingJavaScript:@"player.getPlaylistIndex();"];
+  return [returnValue intValue];
+}
+
+#pragma mark - Playing a video in a playlist
+
+- (void)nextVideo {
+  [self stringFromEvaluatingJavaScript:@"player.nextVideo();"];
+}
+
+- (void)previousVideo {
+  [self stringFromEvaluatingJavaScript:@"player.previousVideo();"];
+}
+
+- (void)playVideoAt:(int)index {
+  NSString *command =
+      [NSString stringWithFormat:@"player.playVideoAt(%@);", [NSNumber numberWithInt:index]];
+  [self stringFromEvaluatingJavaScript:command];
+}
+
+#pragma mark - Helper methods
+
+- (NSArray *)availableQualityLevels {
+  NSString *returnValue =
+      [self stringFromEvaluatingJavaScript:@"player.getAvailableQualityLevels().toString();"];
+  if(!returnValue) return nil;
+
+  NSArray *rawQualityValues = [returnValue componentsSeparatedByString:@","];
+  NSMutableArray *levels = [[NSMutableArray alloc] init];
+  for (NSString *rawQualityValue in rawQualityValues) {
+    YTPlaybackQuality quality = [YTPlayerView playbackQualityForString:rawQualityValue];
+    [levels addObject:[NSNumber numberWithInt:quality]];
+  }
+  return levels;
+}
+
+- (BOOL)webView:(UIWebView *)webView
+    shouldStartLoadWithRequest:(NSURLRequest *)request
+                navigationType:(UIWebViewNavigationType)navigationType {
+  if ([request.URL.host isEqual: self.originURL.host]) {
+    return YES;
+  } else if ([request.URL.scheme isEqual:@"ytplayer"]) {
+    [self notifyDelegateOfYouTubeCallbackUrl:request.URL];
+    return NO;
+  } else if ([request.URL.scheme isEqual: @"http"] || [request.URL.scheme isEqual:@"https"]) {
+    return [self handleHttpNavigationToUrl:request.URL];
+  }
+  return YES;
+}
+
+- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
+  if (self.initialLoadingView) {
+    [self.initialLoadingView removeFromSuperview];
+  }
+}
+
+/**
+ * Convert a quality value from NSString to the typed enum value.
+ *
+ * @param qualityString A string representing playback quality. Ex: "small", "medium", "hd1080".
+ * @return An enum value representing the playback quality.
+ */
++ (YTPlaybackQuality)playbackQualityForString:(NSString *)qualityString {
+  YTPlaybackQuality quality = kYTPlaybackQualityUnknown;
+
+  if ([qualityString isEqualToString:kYTPlaybackQualitySmallQuality]) {
+    quality = kYTPlaybackQualitySmall;
+  } else if ([qualityString isEqualToString:kYTPlaybackQualityMediumQuality]) {
+    quality = kYTPlaybackQualityMedium;
+  } else if ([qualityString isEqualToString:kYTPlaybackQualityLargeQuality]) {
+    quality = kYTPlaybackQualityLarge;
+  } else if ([qualityString isEqualToString:kYTPlaybackQualityHD720Quality]) {
+    quality = kYTPlaybackQualityHD720;
+  } else if ([qualityString isEqualToString:kYTPlaybackQualityHD1080Quality]) {
+    quality = kYTPlaybackQualityHD1080;
+  } else if ([qualityString isEqualToString:kYTPlaybackQualityHighResQuality]) {
+    quality = kYTPlaybackQualityHighRes;
+  } else if ([qualityString isEqualToString:kYTPlaybackQualityAutoQuality]) {
+    quality = kYTPlaybackQualityAuto;
+  }
+
+  return quality;
+}
+
+/**
+ * Convert a |YTPlaybackQuality| value from the typed value to NSString.
+ *
+ * @param quality A |YTPlaybackQuality| parameter.
+ * @return An |NSString| value to be used in the JavaScript bridge.
+ */
++ (NSString *)stringForPlaybackQuality:(YTPlaybackQuality)quality {
+  switch (quality) {
+    case kYTPlaybackQualitySmall:
+      return kYTPlaybackQualitySmallQuality;
+    case kYTPlaybackQualityMedium:
+      return kYTPlaybackQualityMediumQuality;
+    case kYTPlaybackQualityLarge:
+      return kYTPlaybackQualityLargeQuality;
+    case kYTPlaybackQualityHD720:
+      return kYTPlaybackQualityHD720Quality;
+    case kYTPlaybackQualityHD1080:
+      return kYTPlaybackQualityHD1080Quality;
+    case kYTPlaybackQualityHighRes:
+      return kYTPlaybackQualityHighResQuality;
+    case kYTPlaybackQualityAuto:
+      return kYTPlaybackQualityAutoQuality;
+    default:
+      return kYTPlaybackQualityUnknownQuality;
+  }
+}
+
+/**
+ * Convert a state value from NSString to the typed enum value.
+ *
+ * @param stateString A string representing player state. Ex: "-1", "0", "1".
+ * @return An enum value representing the player state.
+ */
++ (YTPlayerState)playerStateForString:(NSString *)stateString {
+  YTPlayerState state = kYTPlayerStateUnknown;
+  if ([stateString isEqualToString:kYTPlayerStateUnstartedCode]) {
+    state = kYTPlayerStateUnstarted;
+  } else if ([stateString isEqualToString:kYTPlayerStateEndedCode]) {
+    state = kYTPlayerStateEnded;
+  } else if ([stateString isEqualToString:kYTPlayerStatePlayingCode]) {
+    state = kYTPlayerStatePlaying;
+  } else if ([stateString isEqualToString:kYTPlayerStatePausedCode]) {
+    state = kYTPlayerStatePaused;
+  } else if ([stateString isEqualToString:kYTPlayerStateBufferingCode]) {
+    state = kYTPlayerStateBuffering;
+  } else if ([stateString isEqualToString:kYTPlayerStateCuedCode]) {
+    state = kYTPlayerStateQueued;
+  }
+  return state;
+}
+
+/**
+ * Convert a state value from the typed value to NSString.
+ *
+ * @param quality A |YTPlayerState| parameter.
+ * @return A string value to be used in the JavaScript bridge.
+ */
++ (NSString *)stringForPlayerState:(YTPlayerState)state {
+  switch (state) {
+    case kYTPlayerStateUnstarted:
+      return kYTPlayerStateUnstartedCode;
+    case kYTPlayerStateEnded:
+      return kYTPlayerStateEndedCode;
+    case kYTPlayerStatePlaying:
+      return kYTPlayerStatePlayingCode;
+    case kYTPlayerStatePaused:
+      return kYTPlayerStatePausedCode;
+    case kYTPlayerStateBuffering:
+      return kYTPlayerStateBufferingCode;
+    case kYTPlayerStateQueued:
+      return kYTPlayerStateCuedCode;
+    default:
+      return kYTPlayerStateUnknownCode;
+  }
+}
+
+#pragma mark - Private methods
+
+/**
+ * Private method to handle "navigation" to a callback URL of the format
+ * ytplayer://action?data=someData
+ * This is how the UIWebView communicates with the containing Objective-C code.
+ * Side effects of this method are that it calls methods on this class's delegate.
+ *
+ * @param url A URL of the format ytplayer://action?data=value.
+ */
+- (void)notifyDelegateOfYouTubeCallbackUrl: (NSURL *) url {
+    NSString *action = url.host;
+    
+    NSLog(@"action:%@",action);
+    
+    // We know the query can only be of the format ytplayer://action?data=SOMEVALUE,
+    // so we parse out the value.
+    NSString *query = url.query;
+    NSString *data;
+    if (query) {
+        data = [query componentsSeparatedByString:@"="][1];
+    }
+    
+    if ([action isEqual:kYTPlayerCallbackOnReady]) {
+        if (self.initialLoadingView) {
+            [self.initialLoadingView removeFromSuperview];
+        }
+        if ([self.delegate respondsToSelector:@selector(playerViewDidBecomeReady:)]) {
+            [self.delegate playerViewDidBecomeReady:self];
+        }
+    } else if ([action isEqual:kYTPlayerCallbackOnStateChange]) {
+        if ([self.delegate respondsToSelector:@selector(playerView:didChangeToState:)]) {
+            YTPlayerState state = kYTPlayerStateUnknown;
+            
+            if ([data isEqual:kYTPlayerStateEndedCode]) {
+                state = kYTPlayerStateEnded;
+            } else if ([data isEqual:kYTPlayerStatePlayingCode]) {
+                state = kYTPlayerStatePlaying;
+            } else if ([data isEqual:kYTPlayerStatePausedCode]) {
+                state = kYTPlayerStatePaused;
+            } else if ([data isEqual:kYTPlayerStateBufferingCode]) {
+                state = kYTPlayerStateBuffering;
+            } else if ([data isEqual:kYTPlayerStateCuedCode]) {
+                state = kYTPlayerStateQueued;
+            } else if ([data isEqual:kYTPlayerStateUnstartedCode]) {
+                state = kYTPlayerStateUnstarted;
+            }
+            
+            [self.delegate playerView:self didChangeToState:state];
+        }
+    } else if ([action isEqual:kYTPlayerCallbackOnPlaybackQualityChange]) {
+        if ([self.delegate respondsToSelector:@selector(playerView:didChangeToQuality:)]) {
+            YTPlaybackQuality quality = [YTPlayerView playbackQualityForString:data];
+            [self.delegate playerView:self didChangeToQuality:quality];
+        }
+    } else if ([action isEqual:kYTPlayerCallbackOnError]) {
+        if ([self.delegate respondsToSelector:@selector(playerView:receivedError:)]) {
+            YTPlayerError error = kYTPlayerErrorUnknown;
+            
+            if ([data isEqual:kYTPlayerErrorInvalidParamErrorCode]) {
+                error = kYTPlayerErrorInvalidParam;
+            } else if ([data isEqual:kYTPlayerErrorHTML5ErrorCode]) {
+                error = kYTPlayerErrorHTML5Error;
+            } else if ([data isEqual:kYTPlayerErrorNotEmbeddableErrorCode] ||
+                       [data isEqual:kYTPlayerErrorSameAsNotEmbeddableErrorCode]) {
+                error = kYTPlayerErrorNotEmbeddable;
+            } else if ([data isEqual:kYTPlayerErrorVideoNotFoundErrorCode] ||
+                       [data isEqual:kYTPlayerErrorCannotFindVideoErrorCode]) {
+                error = kYTPlayerErrorVideoNotFound;
+            }
+            
+            [self.delegate playerView:self receivedError:error];
+        }
+    } else if ([action isEqualToString:kYTPlayerCallbackOnPlayTime]) {
+        if ([self.delegate respondsToSelector:@selector(playerView:didPlayTime:)]) {
+            float time = [data floatValue];
+            [self.delegate playerView:self didPlayTime:time];
+        }
+    } else if ([action isEqualToString:kYTPlayerCallbackOnYouTubeIframeAPIFailedToLoad]) {
+        if (self.initialLoadingView) {
+            [self.initialLoadingView removeFromSuperview];
+        }
+    }
+    else if ([action isEqualToString:@"restriction"])
+    {
+        self.webView.hidden = true;
+    }
+    else
+    {
+        int a=0;
+    }
+}
+-(NSString*) Embed2VID:(NSString*) iframe_embed
+{
+
+    
+    NSString* content = iframe_embed;
+    NSString* pattern = @"(\\w+):\\/\\/([^/:]+)(:\\d*)?([^# ]*\\b)";
+    
+    NSLog(@"content: %@",content);
+    NSLog(@"pattern: %@",pattern);
+    
+    if(content==nil||pattern==nil)
+        return nil;
+    
+    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:nil error:nil];
+    
+    NSTextCheckingResult *match = [regex firstMatchInString:content options:nil range:NSMakeRange(0, content.length)];
+    
+    int i=0;
+    NSLog(@"%d->range with richtext label: %d,%d",i, [match rangeAtIndex:i].location,[match rangeAtIndex:i].length);
+    //    NSLog(@"%d-> range of text: %d,%d ",i+1, [match rangeAtIndex:i+1].location,[match rangeAtIndex:i+1].length);
+    NSLog(@"text: %@ ",[content substringWithRange:[match rangeAtIndex:i]]);
+    
+    return [[content substringWithRange:[match rangeAtIndex:i]] lastPathComponent];
+}
+- (BOOL)handleHttpNavigationToUrl:(NSURL *) url {
+  // Usually this means the user has clicked on the YouTube logo or an error message in the
+  // player. Most URLs should open in the browser. The only http(s) URL that should open in this
+  // UIWebView is the URL for the embed, which is of the format:
+  //     http(s)://www.youtube.com/embed/[VIDEO ID]?[PARAMETERS]
+  NSError *error = NULL;
+  NSRegularExpression *ytRegex =
+      [NSRegularExpression regularExpressionWithPattern:kYTPlayerEmbedUrlRegexPattern
+                                                options:NSRegularExpressionCaseInsensitive
+                                                  error:&error];
+  NSTextCheckingResult *ytMatch =
+      [ytRegex firstMatchInString:url.absoluteString
+                        options:0
+                          range:NSMakeRange(0, [url.absoluteString length])];
+    
+  NSRegularExpression *adRegex =
+      [NSRegularExpression regularExpressionWithPattern:kYTPlayerAdUrlRegexPattern
+                                                options:NSRegularExpressionCaseInsensitive
+                                                  error:&error];
+  NSTextCheckingResult *adMatch =
+      [adRegex firstMatchInString:url.absoluteString
+                        options:0
+                          range:NSMakeRange(0, [url.absoluteString length])];
+    
+  NSRegularExpression *syndicationRegex =
+      [NSRegularExpression regularExpressionWithPattern:kYTPlayerSyndicationRegexPattern
+                                                options:NSRegularExpressionCaseInsensitive
+                                                  error:&error];
+
+  NSTextCheckingResult *syndicationMatch =
+      [syndicationRegex firstMatchInString:url.absoluteString
+                                   options:0
+                                     range:NSMakeRange(0, [url.absoluteString length])];
+
+  NSRegularExpression *oauthRegex =
+      [NSRegularExpression regularExpressionWithPattern:kYTPlayerOAuthRegexPattern
+                                              options:NSRegularExpressionCaseInsensitive
+                                                error:&error];
+  NSTextCheckingResult *oauthMatch =
+    [oauthRegex firstMatchInString:url.absoluteString
+                           options:0
+                             range:NSMakeRange(0, [url.absoluteString length])];
+    
+  NSRegularExpression *staticProxyRegex =
+    [NSRegularExpression regularExpressionWithPattern:kYTPlayerStaticProxyRegexPattern
+                                              options:NSRegularExpressionCaseInsensitive
+                                                error:&error];
+  NSTextCheckingResult *staticProxyMatch =
+    [staticProxyRegex firstMatchInString:url.absoluteString
+                                  options:0
+                                    range:NSMakeRange(0, [url.absoluteString length])];
+
+  if (ytMatch || adMatch || oauthMatch || staticProxyMatch || syndicationMatch) {
+    return YES;
+  } else {
+    [[UIApplication sharedApplication] openURL:url];
+    return NO;
+  }
+}
+
+
+/**
+ * Private helper method to load an iframe player with the given player parameters.
+ *
+ * @param additionalPlayerParams An NSDictionary of parameters in addition to required parameters
+ *                               to instantiate the HTML5 player with. This differs depending on
+ *                               whether a single video or playlist is being loaded.
+ * @return YES if successful, NO if not.
+ */
+- (BOOL)loadWithPlayerParams:(NSDictionary *)additionalPlayerParams {
+  NSDictionary *playerCallbacks = @{
+        @"onReady" : @"onReady",
+        @"onStateChange" : @"onStateChange",
+        @"onPlaybackQualityChange" : @"onPlaybackQualityChange",
+        @"onError" : @"onPlayerError"
+  };
+  NSMutableDictionary *playerParams = [[NSMutableDictionary alloc] init];
+  if (additionalPlayerParams) {
+    [playerParams addEntriesFromDictionary:additionalPlayerParams];
+  }
+  if (![playerParams objectForKey:@"height"]) {
+    [playerParams setValue:@"100%" forKey:@"height"];
+  }
+  if (![playerParams objectForKey:@"width"]) {
+    [playerParams setValue:@"100%" forKey:@"width"];
+  }
+
+  [playerParams setValue:playerCallbacks forKey:@"events"];
+
+  if ([playerParams objectForKey:@"playerVars"]) {
+    NSMutableDictionary *playerVars = [[NSMutableDictionary alloc] init];
+    [playerVars addEntriesFromDictionary:[playerParams objectForKey:@"playerVars"]];
+      
+    if (![playerVars objectForKey:@"origin"]) {
+        self.originURL = [NSURL URLWithString:@"about:blank"];
+    } else {
+        self.originURL = [NSURL URLWithString: [playerVars objectForKey:@"origin"]];
+    }
+  } else {
+    // This must not be empty so we can render a '{}' in the output JSON
+    [playerParams setValue:[[NSDictionary alloc] init] forKey:@"playerVars"];
+  }
+
+  // Remove the existing webView to reset any state
+  [self.webView removeFromSuperview];
+  _webView = [self createNewWebView];
+  [self addSubview:self.webView];
+
+  NSError *error = nil;
+  NSString *path = [[NSBundle bundleForClass:[YTPlayerView class]] pathForResource:@"YTPlayerView-iframe-player"
+                                                   ofType:@"html"
+                                              inDirectory:@"Assets"];
+    
+  // in case of using Swift and embedded frameworks, resources included not in main bundle,
+  // but in framework bundle
+  if (!path) {
+      path = [[[self class] frameworkBundle] pathForResource:@"YTPlayerView-iframe-player"
+                                                     ofType:@"html"
+                                                inDirectory:@"Assets"];
+  }
+    
+  NSString *embedHTMLTemplate =
+      [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error];
+
+  if (error) {
+    NSLog(@"Received error rendering template: %@", error);
+    return NO;
+  }
+
+  // Render the playerVars as a JSON dictionary.
+  NSError *jsonRenderingError = nil;
+  NSData *jsonData = [NSJSONSerialization dataWithJSONObject:playerParams
+                                                     options:NSJSONWritingPrettyPrinted
+                                                       error:&jsonRenderingError];
+  if (jsonRenderingError) {
+    NSLog(@"Attempted configuration of player with invalid playerVars: %@ \tError: %@",
+          playerParams,
+          jsonRenderingError);
+    return NO;
+  }
+
+  NSString *playerVarsJsonString =
+      [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
+
+  NSString *embedHTML = [NSString stringWithFormat:embedHTMLTemplate, playerVarsJsonString];
+  [self.webView loadHTMLString:embedHTML baseURL: self.originURL];
+  [self.webView setDelegate:self];
+  self.webView.allowsInlineMediaPlayback = YES;
+  self.webView.mediaPlaybackRequiresUserAction = NO;
+  
+  if ([self.delegate respondsToSelector:@selector(playerViewPreferredInitialLoadingView:)]) {
+    UIView *initialLoadingView = [self.delegate playerViewPreferredInitialLoadingView:self];
+    if (initialLoadingView) {
+      initialLoadingView.frame = self.bounds;
+      initialLoadingView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+      [self addSubview:initialLoadingView];
+      self.initialLoadingView = initialLoadingView;
+    }
+  }
+  
+  return YES;
+}
+
+/**
+ * Private method for cueing both cases of playlist ID and array of video IDs. Cueing
+ * a playlist does not start playback.
+ *
+ * @param cueingString A JavaScript string representing an array, playlist ID or list of
+ *                     video IDs to play with the playlist player.
+ * @param index 0-index position of video to start playback on.
+ * @param startSeconds Seconds after start of video to begin playback.
+ * @param suggestedQuality Suggested YTPlaybackQuality to play the videos.
+ * @return The result of cueing the playlist.
+ */
+- (void)cuePlaylist:(NSString *)cueingString
+               index:(int)index
+        startSeconds:(float)startSeconds
+    suggestedQuality:(YTPlaybackQuality)suggestedQuality {
+  NSNumber *indexValue = [NSNumber numberWithInt:index];
+  NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds];
+  NSString *qualityValue = [YTPlayerView stringForPlaybackQuality:suggestedQuality];
+  NSString *command = [NSString stringWithFormat:@"player.cuePlaylist(%@, %@, %@, '%@');",
+      cueingString, indexValue, startSecondsValue, qualityValue];
+  [self stringFromEvaluatingJavaScript:command];
+}
+
+/**
+ * Private method for loading both cases of playlist ID and array of video IDs. Loading
+ * a playlist automatically starts playback.
+ *
+ * @param cueingString A JavaScript string representing an array, playlist ID or list of
+ *                     video IDs to play with the playlist player.
+ * @param index 0-index position of video to start playback on.
+ * @param startSeconds Seconds after start of video to begin playback.
+ * @param suggestedQuality Suggested YTPlaybackQuality to play the videos.
+ * @return The result of cueing the playlist.
+ */
+- (void)loadPlaylist:(NSString *)cueingString
+               index:(int)index
+        startSeconds:(float)startSeconds
+    suggestedQuality:(YTPlaybackQuality)suggestedQuality {
+  NSNumber *indexValue = [NSNumber numberWithInt:index];
+  NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds];
+  NSString *qualityValue = [YTPlayerView stringForPlaybackQuality:suggestedQuality];
+  NSString *command = [NSString stringWithFormat:@"player.loadPlaylist(%@, %@, %@, '%@');",
+      cueingString, indexValue, startSecondsValue, qualityValue];
+  [self stringFromEvaluatingJavaScript:command];
+}
+
+/**
+ * Private helper method for converting an NSArray of video IDs into its JavaScript equivalent.
+ *
+ * @param videoIds An array of video ID strings to convert into JavaScript format.
+ * @return A JavaScript array in String format containing video IDs.
+ */
+- (NSString *)stringFromVideoIdArray:(NSArray *)videoIds {
+  NSMutableArray *formattedVideoIds = [[NSMutableArray alloc] init];
+
+  for (id unformattedId in videoIds) {
+    [formattedVideoIds addObject:[NSString stringWithFormat:@"'%@'", unformattedId]];
+  }
+
+  return [NSString stringWithFormat:@"[%@]", [formattedVideoIds componentsJoinedByString:@", "]];
+}
+
+/**
+ * Private method for evaluating JavaScript in the WebView.
+ *
+ * @param jsToExecute The JavaScript code in string format that we want to execute.
+ * @return JavaScript response from evaluating code.
+ */
+- (NSString *)stringFromEvaluatingJavaScript:(NSString *)jsToExecute {
+  return [self.webView stringByEvaluatingJavaScriptFromString:jsToExecute];
+}
+
+/**
+ * Private method to convert a Objective-C BOOL value to JS boolean value.
+ *
+ * @param boolValue Objective-C BOOL value.
+ * @return JavaScript Boolean value, i.e. "true" or "false".
+ */
+- (NSString *)stringForJSBoolean:(BOOL)boolValue {
+  return boolValue ? @"true" : @"false";
+}
+
+#pragma mark - Exposed for Testing
+
+- (void)setWebView:(UIWebView *)webView {
+  _webView = webView;
+}
+
+- (UIWebView *)createNewWebView {
+    UIWebView *webView = [[UIWebView alloc] initWithFrame:self.bounds];
+    webView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
+    webView.scrollView.scrollEnabled = NO;
+    webView.scrollView.bounces = NO;
+    
+    if ([self.delegate respondsToSelector:@selector(playerViewPreferredWebViewBackgroundColor:)]) {
+        webView.backgroundColor = [self.delegate playerViewPreferredWebViewBackgroundColor:self];
+        if (webView.backgroundColor == [UIColor clearColor]) {
+            webView.opaque = NO;
+        }
+    }
+    
+    return webView;
+}
+
+- (void)removeWebView {
+  [self.webView removeFromSuperview];
+  self.webView = nil;
+}
+
++ (NSBundle *)frameworkBundle {
+    static NSBundle* frameworkBundle = nil;
+    static dispatch_once_t predicate;
+    dispatch_once(&predicate, ^{
+        NSString* mainBundlePath = [[NSBundle bundleForClass:[self class]] resourcePath];
+        NSString* frameworkBundlePath = [mainBundlePath stringByAppendingPathComponent:@"Assets.bundle"];
+        frameworkBundle = [NSBundle bundleWithPath:frameworkBundlePath];
+    });
+    return frameworkBundle;
+}
+
+@end

+ 16 - 0
RedAnt ERP Mobile/common/youtube/YTViewController.h

@@ -0,0 +1,16 @@
+//
+//  YTViewController.h
+//  test_autolayout
+//
+//  Created by Ray on 20/06/2017.
+//  Copyright © 2017 USAI. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+#import "YTPlayerView.h"
+
+
+@interface YTViewController : UIViewController<YTPlayerViewDelegate,UIWebViewDelegate>
+@property (strong, nonatomic) IBOutlet YTPlayerView *playerView;
+
+@end

+ 66 - 0
RedAnt ERP Mobile/common/youtube/YTViewController.m

@@ -0,0 +1,66 @@
+//
+//  YTViewController.m
+//  test_autolayout
+//
+//  Created by Ray on 20/06/2017.
+//  Copyright © 2017 USAI. All rights reserved.
+//
+
+#import "YTViewController.h"
+
+@interface YTViewController ()
+
+@end
+
+@implementation YTViewController
+
+- (void)viewDidLoad {
+    [super viewDidLoad];
+    
+    
+    NSDictionary *playerVars = @{
+                                 @"playsinline" : @1,
+                                 @"autoplay" : @(1),
+                                 @"rel":@0,
+                                 @"showinfo": @0,
+                                 @"modestbranding":@0,
+                                 @"enablejsapi":@1
+                                 };
+    
+    [self.playerView loadWithVideoId:@"5_tAA-D4tDc" playerVars:[NSDictionary new]];
+    
+    
+    // Do any additional setup after loading the view.
+}
+
+- (void)didReceiveMemoryWarning {
+    [super didReceiveMemoryWarning];
+    // Dispose of any resources that can be recreated.
+}
+
+
+
+- (void)playerView:(nonnull YTPlayerView *)playerView didChangeToState:(YTPlayerState)state
+{
+    NSLog(@"- (void)playerView:(nonnull YTPlayerView *)playerView didChangeToState:(YTPlayerState)state");
+}
+
+- (void)playerView:(nonnull YTPlayerView *)playerView didPlayTime:(float)playTime
+{
+    NSLog(@"- (void)playerView:(nonnull YTPlayerView *)playerView didPlayTime:(float)playTime");
+}
+
+
+
+
+/*
+#pragma mark - Navigation
+
+// In a storyboard-based application, you will often want to do a little preparation before navigation
+- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
+    // Get the new view controller using [segue destinationViewController].
+    // Pass the selected object to the new view controller.
+}
+*/
+
+@end

+ 19 - 0
RedAnt ERP Mobile/common/youtube/YouTubeiOSPlayerHelper.h

@@ -0,0 +1,19 @@
+//
+//  youtube-ios-player-helper.h
+//  youtube-ios-player-helper
+//
+//  Created by Niels van Hoorn on 02/09/15.
+//  Copyright © 2015 YouTube Developer Relations. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+//! Project version number for youtube-ios-player-helper.
+FOUNDATION_EXPORT double youtube_ios_player_helperVersionNumber;
+
+//! Project version string for youtube-ios-player-helper.
+FOUNDATION_EXPORT const unsigned char youtube_ios_player_helperVersionString[];
+
+// In this header, you should import all the public headers of your framework using statements like #import <youtube_ios_player_helper/PublicHeader.h>
+
+#import "YTPlayerView.h"

+ 26 - 0
RedAnt ERP Mobile/iSales-NPD.xcodeproj/project.pbxproj

@@ -214,6 +214,9 @@
 		71BF06FB1D2F3CAC00981938 /* OLDataProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 71BF06FA1D2F3CAC00981938 /* OLDataProvider.m */; };
 		71BF06FE1D2F3CBA00981938 /* OfflineSettingViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 71BF06FD1D2F3CBA00981938 /* OfflineSettingViewController.m */; };
 		71BF07081D2F3D2800981938 /* SyncControlPanelViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 71BF07071D2F3D2800981938 /* SyncControlPanelViewController.m */; };
+		71BF33011EFA1D6100842CB4 /* Assets.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 71BF32FB1EFA1D6100842CB4 /* Assets.bundle */; };
+		71BF33021EFA1D6100842CB4 /* YTPlayerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 71BF32FE1EFA1D6100842CB4 /* YTPlayerView.m */; };
+		71BF33031EFA1D6100842CB4 /* YTViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 71BF33001EFA1D6100842CB4 /* YTViewController.m */; };
 		71D01ADD1E08CB1C006620CE /* signature.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 71D01ADC1E08CB1C006620CE /* signature.storyboard */; };
 		71D01B1A1E0A2055006620CE /* ImageUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 71D01B151E0A2055006620CE /* ImageUtils.m */; };
 		71D01B1B1E0A2055006620CE /* PDFUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 71D01B171E0A2055006620CE /* PDFUtils.m */; };
@@ -646,6 +649,12 @@
 		71BF06FD1D2F3CBA00981938 /* OfflineSettingViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OfflineSettingViewController.m; path = common/Functions/offline/OfflineSettingViewController.m; sourceTree = SOURCE_ROOT; };
 		71BF07061D2F3D2800981938 /* SyncControlPanelViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SyncControlPanelViewController.h; path = common/Functions/offline/SyncControlPanelViewController.h; sourceTree = SOURCE_ROOT; };
 		71BF07071D2F3D2800981938 /* SyncControlPanelViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SyncControlPanelViewController.m; path = common/Functions/offline/SyncControlPanelViewController.m; sourceTree = SOURCE_ROOT; };
+		71BF32FB1EFA1D6100842CB4 /* Assets.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = Assets.bundle; path = common/youtube/Assets.bundle; sourceTree = SOURCE_ROOT; };
+		71BF32FC1EFA1D6100842CB4 /* YouTubeiOSPlayerHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = YouTubeiOSPlayerHelper.h; path = common/youtube/YouTubeiOSPlayerHelper.h; sourceTree = SOURCE_ROOT; };
+		71BF32FD1EFA1D6100842CB4 /* YTPlayerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = YTPlayerView.h; path = common/youtube/YTPlayerView.h; sourceTree = SOURCE_ROOT; };
+		71BF32FE1EFA1D6100842CB4 /* YTPlayerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = YTPlayerView.m; path = common/youtube/YTPlayerView.m; sourceTree = SOURCE_ROOT; };
+		71BF32FF1EFA1D6100842CB4 /* YTViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = YTViewController.h; path = common/youtube/YTViewController.h; sourceTree = SOURCE_ROOT; };
+		71BF33001EFA1D6100842CB4 /* YTViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = YTViewController.m; path = common/youtube/YTViewController.m; sourceTree = SOURCE_ROOT; };
 		71D01ADC1E08CB1C006620CE /* signature.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = signature.storyboard; path = common/Functions/signature/signature.storyboard; sourceTree = SOURCE_ROOT; };
 		71D01B141E0A2055006620CE /* ImageUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageUtils.h; path = common/ImageUtils.h; sourceTree = SOURCE_ROOT; };
 		71D01B151E0A2055006620CE /* ImageUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ImageUtils.m; path = common/ImageUtils.m; sourceTree = SOURCE_ROOT; };
@@ -1648,6 +1657,19 @@
 			name = AES;
 			sourceTree = "<group>";
 		};
+		71BF32FA1EFA1C0700842CB4 /* youtube player */ = {
+			isa = PBXGroup;
+			children = (
+				71BF32FB1EFA1D6100842CB4 /* Assets.bundle */,
+				71BF32FC1EFA1D6100842CB4 /* YouTubeiOSPlayerHelper.h */,
+				71BF32FD1EFA1D6100842CB4 /* YTPlayerView.h */,
+				71BF32FE1EFA1D6100842CB4 /* YTPlayerView.m */,
+				71BF32FF1EFA1D6100842CB4 /* YTViewController.h */,
+				71BF33001EFA1D6100842CB4 /* YTViewController.m */,
+			);
+			name = "youtube player";
+			sourceTree = "<group>";
+		};
 		71DBACEE1B5E401A00D8BD7E /* HomeView */ = {
 			isa = PBXGroup;
 			children = (
@@ -1677,6 +1699,7 @@
 		71DF74311C5757C800F2789C /* CommonUI */ = {
 			isa = PBXGroup;
 			children = (
+				71BF32FA1EFA1C0700842CB4 /* youtube player */,
 				4289800E1E24924D005F1BD8 /* sort */,
 				715EB9A6193866F2006D16A5 /* photoStack */,
 				718600B31936C385004B6CDE /* customUI */,
@@ -1827,6 +1850,7 @@
 				7142E87B1DBF577A0077EFA2 /* KALINGAB.TTF in Resources */,
 				713F76C21929F4A7006A7305 /* Images.xcassets in Resources */,
 				42E489611EE63F4E007256A0 /* more_color_64.png in Resources */,
+				71BF33011EFA1D6100842CB4 /* Assets.bundle in Resources */,
 				713F76B41929F4A7006A7305 /* InfoPlist.strings in Resources */,
 				7165662F1EF3ACD900CB897F /* photostack_video.html in Resources */,
 				7162A5A01C58733400AB630E /* status_filter_cadedate_open.json in Resources */,
@@ -2077,6 +2101,7 @@
 				42BEF3511E89FE1200632AB6 /* CHANGELOG.md in Sources */,
 				42BEF35A1E89FE1200632AB6 /* README.md in Sources */,
 				71DF748D1C57608F00F2789C /* TouchLabel.m in Sources */,
+				71BF33031EFA1D6100842CB4 /* YTViewController.m in Sources */,
 				428980171E24924D005F1BD8 /* SortItemViewController.m in Sources */,
 				7162A5711C58728D00AB630E /* DetailImageCell.m in Sources */,
 				7162A51A1C58704600AB630E /* ModelItemCell.m in Sources */,
@@ -2112,6 +2137,7 @@
 				7141DD3C1C5726B700F7DF59 /* ScanApiHelper.mm in Sources */,
 				7162A5261C58706C00AB630E /* CategoryViewController.m in Sources */,
 				7162A50A1C586FC100AB630E /* NSTimer+Addition.m in Sources */,
+				71BF33021EFA1D6100842CB4 /* YTPlayerView.m in Sources */,
 				71DF745E1C575E7900F2789C /* CommonEditorCellImg.m in Sources */,
 				712AFED91DBA050200254965 /* GroupDrawable.m in Sources */,
 			);

+ 28 - 4
RedAnt ERP Mobile/iSales-NPD/Base.lproj/Main.storyboard

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12120" systemVersion="16A323" targetRuntime="iOS.CocoaTouch.iPad" propertyAccessControl="none" colorMatched="YES" initialViewController="jsC-F8-zYF">
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12120" systemVersion="16F73" targetRuntime="iOS.CocoaTouch.iPad" propertyAccessControl="none" colorMatched="YES" initialViewController="jsC-F8-zYF">
     <device id="ipad9_7" orientation="portrait">
         <adaptation id="fullscreen"/>
     </device>
@@ -1189,6 +1189,29 @@
                                                         <action selector="notifyMeButtonClicked:" destination="PCZ-Ci-oQf" eventType="touchUpInside" id="KXc-5S-RRJ"/>
                                                     </connections>
                                                 </button>
+                                                <view hidden="YES" contentMode="scaleToFill" misplaced="YES" id="0nt-dh-BF2" customClass="YTPlayerView">
+                                                    <rect key="frame" x="0.0" y="0.0" width="768" height="550"/>
+                                                    <autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
+                                                    <subviews>
+                                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="Video player will show here" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="o27-8h-cXw" userLabel="Video player will be there">
+                                                            <rect key="frame" x="146" y="233" width="476" height="83"/>
+                                                            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                                                            <fontDescription key="fontDescription" type="system" pointSize="32"/>
+                                                            <color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                                                            <nil key="highlightedColor"/>
+                                                        </label>
+                                                        <button opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="bJa-aI-AfH">
+                                                            <rect key="frame" x="716" y="8" width="52" height="45"/>
+                                                            <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
+                                                            <color key="tintColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                                                            <state key="normal" image="close"/>
+                                                            <connections>
+                                                                <action selector="OnClosePlayer:" destination="PCZ-Ci-oQf" eventType="touchUpInside" id="qn4-nv-S6u"/>
+                                                            </connections>
+                                                        </button>
+                                                    </subviews>
+                                                    <color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
+                                                </view>
                                             </subviews>
                                         </tableViewCellContentView>
                                         <inset key="separatorInset" minX="15" minY="0.0" maxX="0.0" maxY="0.0"/>
@@ -1208,6 +1231,7 @@
                                             <outlet property="model_label" destination="ec3-Qs-dVj" id="8WN-ol-ISC"/>
                                             <outlet property="pageControl" destination="dlZ-DL-v21" id="sVb-8O-74F"/>
                                             <outlet property="photoStack" destination="xCF-6W-Mpc" id="0en-Ck-CXV"/>
+                                            <outlet property="playerView" destination="0nt-dh-BF2" id="1uP-r6-4Mp"/>
                                             <outlet property="priceLabel" destination="fD8-Km-IT3" id="KJA-dI-71O"/>
                                             <outlet property="property_nameLabel" destination="v0v-80-iIZ" id="o0x-Gl-UCM"/>
                                             <outlet property="property_valLabel" destination="EvS-UE-8Nb" id="tHF-49-cMX"/>
@@ -1373,7 +1397,7 @@
                                 </collectionViewFlowLayout>
                                 <cells>
                                     <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="CategoryCellNPD" id="cah-A8-bXi" customClass="CategoryCellNPD">
-                                        <rect key="frame" x="0.0" y="94.5" width="180" height="251"/>
+                                        <rect key="frame" x="0.0" y="95" width="180" height="251"/>
                                         <autoresizingMask key="autoresizingMask" flexibleMinY="YES"/>
                                         <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
                                             <rect key="frame" x="0.0" y="0.0" width="180" height="251"/>
@@ -2165,7 +2189,7 @@
                                         <rect key="frame" x="0.0" y="88" width="768" height="44"/>
                                         <autoresizingMask key="autoresizingMask"/>
                                         <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Pol-lE-P45" id="FAm-1c-nX0">
-                                            <rect key="frame" x="0.0" y="0.0" width="702" height="44"/>
+                                            <rect key="frame" x="0.0" y="0.0" width="730" height="44"/>
                                             <autoresizingMask key="autoresizingMask"/>
                                             <subviews>
                                                 <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Clean Cache" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="AHm-p1-51h">
@@ -7925,7 +7949,7 @@ Email: redantsupport@united-us.net</string>
                                             <autoresizingMask key="autoresizingMask"/>
                                             <subviews>
                                                 <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="HwF-54-hzr">
-                                                    <rect key="frame" x="28" y="0.0" width="672" height="43.5"/>
+                                                    <rect key="frame" x="15" y="0.0" width="698" height="43.5"/>
                                                     <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                                                     <fontDescription key="fontDescription" type="system" pointSize="16"/>
                                                     <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>

+ 23 - 0
RedAnt ERP Mobile/iSales-NPD/Images.xcassets/play.imageset/Contents.json

@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "appicon.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "appicon-1.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "appicon-2.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}

BIN
RedAnt ERP Mobile/iSales-NPD/Images.xcassets/play.imageset/appicon-1.png


BIN
RedAnt ERP Mobile/iSales-NPD/Images.xcassets/play.imageset/appicon-2.png


BIN
RedAnt ERP Mobile/iSales-NPD/Images.xcassets/play.imageset/appicon.png