Best JavaScript code snippet using redwood
corba.js
Source:corba.js  
...422    return false;423};424handle_message = function() {425    //Util.Debug(">> handle_message ws.rQlen(): " + ws.rQlen());426    //Util.Debug("ws.rQslice(0,20): " + ws.rQslice(0,20) + " (" + ws.rQlen() + ")");427    if (ws.rQlen() === 0) {428        Util.Warn("handle_message called on empty receive queue");429        return;430    }431    switch (corba_state) {432    case 'disconnected':433    case 'failed':434        Util.Error("Got data while disconnected");435        break;436    case 'normal':437        if (normal_msg() && ws.rQlen() > 0) {438            // true means we can continue processing439            // Give other events a chance to run440            if (msgTimer === null) {441                Util.Debug("More data to process, creating timer");442                msgTimer = setTimeout(function () {443                            msgTimer = null;444                            handle_message();445                        }, 10);446            } else {447                Util.Debug("More data to process, existing timer");448            }449        }450        break;451    default:452        init_msg();453        break;454    }455};456function genDES(password, challenge) {457    var i, passwd = [];458    for (i=0; i < password.length; i += 1) {459        passwd.push(password.charCodeAt(i));460    }461    return (new DES(passwd)).encrypt(challenge);462}463function flushClient() {464    if (mouse_arr.length > 0) {465        //send(mouse_arr.concat(fbUpdateRequests()));466        ws.send(mouse_arr);467        setTimeout(function() {468                ws.send(fbUpdateRequests());469            }, 50);470        mouse_arr = [];471        return true;472    } else {473        return false;474    }475}476// overridable for testing477checkEvents = function() {478    var now;479    if (corba_state === 'normal' && !viewportDragging) {480        if (! flushClient()) {481            now = new Date().getTime();482            if (now > last_req_time + conf.fbu_req_rate) {483                last_req_time = now;484                ws.send(fbUpdateRequests());485            }486        }487    }488    setTimeout(checkEvents, conf.check_rate);489};490keyPress = function(keysym, down) {491    var arr;492    if (conf.view_only) { return; } // View only, skip keyboard events493    arr = keyEvent(keysym, down);494    arr = arr.concat(fbUpdateRequests());495    ws.send(arr);496};497mouseButton = function(x, y, down, bmask) {498    if (down) {499        mouse_buttonMask |= bmask;500    } else {501        mouse_buttonMask ^= bmask;502    }503    if (conf.viewportDrag) {504        if (down && !viewportDragging) {505            viewportDragging = true;506            viewportDragPos = {'x': x, 'y': y};507            // Skip sending mouse events508            return;509        } else {510            viewportDragging = false;511            ws.send(fbUpdateRequests()); // Force immediate redraw512        }513    }514    if (conf.view_only) { return; } // View only, skip mouse events515    mouse_arr = mouse_arr.concat(516            pointerEvent(display.absX(x), display.absY(y)) );517    flushClient();518};519mouseMove = function(x, y) {520    //Util.Debug('>> mouseMove ' + x + "," + y);521    var deltaX, deltaY;522    if (viewportDragging) {523        //deltaX = x - viewportDragPos.x; // drag viewport524        deltaX = viewportDragPos.x - x; // drag frame buffer525        //deltaY = y - viewportDragPos.y; // drag viewport526        deltaY = viewportDragPos.y - y; // drag frame buffer527        viewportDragPos = {'x': x, 'y': y};528        display.viewportChange(deltaX, deltaY);529        // Skip sending mouse events530        return;531    }532    if (conf.view_only) { return; } // View only, skip mouse events533    mouse_arr = mouse_arr.concat(534            pointerEvent(display.absX(x), display.absY(y)) );535};536//537// Server message handlers538//539// CORBA/mirror4cast initialisation message handler540init_msg = function() {541    //Util.Debug(">> init_msg [corba_state '" + corba_state + "']");542    var strlen, reason, length, sversion, cversion, repeaterID,543        i, types, num_types, challenge, response, bpp, depth,544        big_endian, red_max, green_max, blue_max, red_shift,545        green_shift, blue_shift, true_color, name_length, is_repeater;546    //Util.Debug("ws.rQ (" + ws.rQlen() + ") " + ws.rQslice(0));547    switch (corba_state) {548    case 'ProtocolVersion' :549        if (ws.rQlen() < 12) {550            return fail("Incomplete protocol version");551        }552        sversion = ws.rQshiftStr(12).substr(4,7);553        Util.Info("Server ProtocolVersion: " + sversion);554        is_repeater = 0;555        switch (sversion) {556            case "000.000": is_repeater = 1; break; // Ultramirror4cast repeater557            case "003.003": corba_version = 3.3; break;558            case "003.006": corba_version = 3.3; break;  // Ultramirror4cast559            case "003.889": corba_version = 3.3; break;  // Apple Remote Desktop560            case "003.007": corba_version = 3.7; break;561            case "003.008": corba_version = 3.8; break;562            case "004.000": corba_version = 3.8; break;  // Intel AMT KVM563            case "004.001": corba_version = 3.8; break;  // Realmirror4cast 4.6564            default:565                return fail("Invalid server version " + sversion);566        }567        if (is_repeater) { 568            repeaterID = conf.repeaterID;569            while (repeaterID.length < 250) {570                repeaterID += "\0";571            }572            ws.send_string(repeaterID);573            break;574        }575        if (corba_version > corba_max_version) { 576            corba_version = corba_max_version;577        }578        if (! test_mode) {579            sendTimer = setInterval(function() {580                    // Send updates either at a rate of one update581                    // every 50ms, or whatever slower rate the network582                    // can handle.583                    ws.flush();584                }, 50);585        }586        cversion = "00" + parseInt(corba_version,10) +587                   ".00" + ((corba_version * 10) % 10);588        ws.send_string("RFB " + cversion + "\n");589        updateState('Security', "Sent ProtocolVersion: " + cversion);590        break;591    case 'Security' :592        if (corba_version >= 3.7) {593            // Server sends supported list, client decides 594            num_types = ws.rQshift8();595            if (ws.rQwait("security type", num_types, 1)) { return false; }596            if (num_types === 0) {597                strlen = ws.rQshift32();598                reason = ws.rQshiftStr(strlen);599                return fail("Security failure: " + reason);600            }601            corba_auth_scheme = 0;602            types = ws.rQshiftBytes(num_types);603            Util.Debug("Server security types: " + types);604            for (i=0; i < types.length; i+=1) {605                if ((types[i] > corba_auth_scheme) && (types[i] < 3)) {606                    corba_auth_scheme = types[i];607                }608            }609            if (corba_auth_scheme === 0) {610                return fail("Unsupported security types: " + types);611            }612            613            ws.send([corba_auth_scheme]);614        } else {615            // Server decides616            if (ws.rQwait("security scheme", 4)) { return false; }617            corba_auth_scheme = ws.rQshift32();618        }619        updateState('Authentication',620                "Authenticating using scheme: " + corba_auth_scheme);621        init_msg();  // Recursive fallthrough (workaround JSLint complaint)622        break;623    // Triggered by fallthough, not by server message624    case 'Authentication' :625        //Util.Debug("Security auth scheme: " + corba_auth_scheme);626        switch (corba_auth_scheme) {627            case 0:  // connection failed628                if (ws.rQwait("auth reason", 4)) { return false; }629                strlen = ws.rQshift32();630                reason = ws.rQshiftStr(strlen);631                return fail("Auth failure: " + reason);632            case 1:  // no authentication633                if (corba_version >= 3.8) {634                    updateState('SecurityResult');635                    return;636                }637                // Fall through to ClientInitialisation638                break;639            case 2:  // mirror4cast authentication640                if (corba_password.length === 0) {641                    // Notify via both callbacks since it is kind of642                    // a CORBA state change and a UI interface issue.643                    updateState('password', "Password Required");644                    conf.onPasswordRequired(that);645                    return;646                }647                if (ws.rQwait("auth challenge", 16)) { return false; }648                challenge = ws.rQshiftBytes(16);649                //Util.Debug("Password: " + corba_password);650                //Util.Debug("Challenge: " + challenge +651                //           " (" + challenge.length + ")");652                response = genDES(corba_password, challenge);653                //Util.Debug("Response: " + response +654                //           " (" + response.length + ")");655                656                //Util.Debug("Sending DES encrypted auth response");657                ws.send(response);658                updateState('SecurityResult');659                return;660            default:661                fail("Unsupported auth scheme: " + corba_auth_scheme);662                return;663        }664        updateState('ClientInitialisation', "No auth required");665        init_msg();  // Recursive fallthrough (workaround JSLint complaint)666        break;667    case 'SecurityResult' :668        if (ws.rQwait("Mirror4cast auth response ", 4)) { return false; }669        switch (ws.rQshift32()) {670            case 0:  // OK671                // Fall through to ClientInitialisation672                break;673            case 1:  // failed674                if (corba_version >= 3.8) {675                    length = ws.rQshift32();676                    if (ws.rQwait("SecurityResult reason", length, 8)) {677                        return false;678                    }679                    reason = ws.rQshiftStr(length);680                    fail(reason);681                } else {682                    fail("Authentication failed");683                }684                return;685            case 2:  // too-many686                return fail("Too many auth attempts");687        }688        updateState('ClientInitialisation', "Authentication OK");689        init_msg();  // Recursive fallthrough (workaround JSLint complaint)690        break;691    // Triggered by fallthough, not by server message692    case 'ClientInitialisation' :693        ws.send([conf.shared ? 1 : 0]); // ClientInitialisation694        updateState('ServerInitialisation', "Authentication OK");695        break;696    case 'ServerInitialisation' :697        if (ws.rQwait("server initialization", 24)) { return false; }698        /* Screen size */699        fb_width  = ws.rQshift16();700        fb_height = ws.rQshift16();701        /* PIXEL_FORMAT */702        bpp            = ws.rQshift8();703        depth          = ws.rQshift8();704        big_endian     = ws.rQshift8();705        true_color     = ws.rQshift8();706        red_max        = ws.rQshift16();707        green_max      = ws.rQshift16();708        blue_max       = ws.rQshift16();709        red_shift      = ws.rQshift8();710        green_shift    = ws.rQshift8();711        blue_shift     = ws.rQshift8();712        ws.rQshiftStr(3); // padding713        Util.Info("Screen: " + fb_width + "x" + fb_height + 714                  ", bpp: " + bpp + ", depth: " + depth +715                  ", big_endian: " + big_endian +716                  ", true_color: " + true_color +717                  ", red_max: " + red_max +718                  ", green_max: " + green_max +719                  ", blue_max: " + blue_max +720                  ", red_shift: " + red_shift +721                  ", green_shift: " + green_shift +722                  ", blue_shift: " + blue_shift);723        if (big_endian !== 0) {724            Util.Warn("Server native endian is not little endian");725        }726        if (red_shift !== 16) {727            Util.Warn("Server native red-shift is not 16");728        }729        if (blue_shift !== 0) {730            Util.Warn("Server native blue-shift is not 0");731        }732        /* Connection name/title */733        name_length   = ws.rQshift32();734        fb_name = ws.rQshiftStr(name_length);735        736        if (conf.true_color && fb_name === "Intel(r) AMT KVM")737        {738            Util.Warn("Intel AMT KVM only support 8/16 bit depths. Disabling true color");739            conf.true_color = false;740        }741        display.set_true_color(conf.true_color);742        display.resize(fb_width, fb_height);743        keyboard.grab();744        mouse.grab();745        if (conf.true_color) {746            fb_Bpp           = 4;747            fb_depth         = 3;748        } else {749            fb_Bpp           = 1;750            fb_depth         = 1;751        }752        response = pixelFormat();753        response = response.concat(clientEncodings());754        response = response.concat(fbUpdateRequests());755        timing.fbu_rt_start = (new Date()).getTime();756        timing.pixels = 0;757        ws.send(response);758        759        /* Start pushing/polling */760        setTimeout(checkEvents, conf.check_rate);761        if (conf.encrypt) {762            updateState('normal', "Connected (encrypted) to: " + fb_name);763        } else {764            updateState('normal', "Connected (unencrypted) to: " + fb_name);765        }766        break;767    }768    //Util.Debug("<< init_msg");769};770/* Normal CORBA/mirror4cast server message handler */771normal_msg = function() {772    //Util.Debug(">> normal_msg");773    var ret = true, msg_type, length, text,774        c, first_colour, num_colours, red, green, blue;775    if (FBU.rects > 0) {776        msg_type = 0;777    } else {778        msg_type = ws.rQshift8();779    }780    switch (msg_type) {781    case 0:  // FramebufferUpdate782        ret = framebufferUpdate(); // false means need more data783        break;784    case 1:  // SetColourMapEntries785        Util.Debug("SetColourMapEntries");786        ws.rQshift8();  // Padding787        first_colour = ws.rQshift16(); // First colour788        num_colours = ws.rQshift16();789        if (ws.rQwait("SetColourMapEntries", num_colours*6, 6)) { return false; }790        791        for (c=0; c < num_colours; c+=1) { 792            red = ws.rQshift16();793            //Util.Debug("red before: " + red);794            red = parseInt(red / 256, 10);795            //Util.Debug("red after: " + red);796            green = parseInt(ws.rQshift16() / 256, 10);797            blue = parseInt(ws.rQshift16() / 256, 10);798            display.set_colourMap([blue, green, red], first_colour + c);799        }800        Util.Debug("colourMap: " + display.get_colourMap());801        Util.Info("Registered " + num_colours + " colourMap entries");802        //Util.Debug("colourMap: " + display.get_colourMap());803        break;804    case 2:  // Bell805        Util.Debug("Bell");806        conf.onBell(that);807        break;808    case 3:  // ServerCutText809        Util.Debug("ServerCutText");810        if (ws.rQwait("ServerCutText header", 7, 1)) { return false; }811        ws.rQshiftBytes(3);  // Padding812        length = ws.rQshift32();813        if (ws.rQwait("ServerCutText", length, 8)) { return false; }814        text = ws.rQshiftStr(length);815        conf.clipboardReceive(that, text); // Obsolete816        conf.onClipboard(that, text);817        break;818    default:819        fail("Disconnected: illegal server message type " + msg_type);820        Util.Debug("ws.rQslice(0,30):" + ws.rQslice(0,30));821        break;822    }823    //Util.Debug("<< normal_msg");824    return ret;825};826framebufferUpdate = function() {827    var now, hdr, fbu_rt_diff, ret = true;828    if (FBU.rects === 0) {829        //Util.Debug("New FBU: ws.rQslice(0,20): " + ws.rQslice(0,20));830        if (ws.rQwait("FBU header", 3)) {831            ws.rQunshift8(0);  // FBU msg_type832            return false;833        }834        ws.rQshift8();  // padding835        FBU.rects = ws.rQshift16();836        //Util.Debug("FramebufferUpdate, rects:" + FBU.rects);837        FBU.bytes = 0;838        timing.cur_fbu = 0;839        if (timing.fbu_rt_start > 0) {840            now = (new Date()).getTime();841            Util.Info("First FBU latency: " + (now - timing.fbu_rt_start));842        }843    }844    while (FBU.rects > 0) {845        if (corba_state !== "normal") {846            return false;847        }848        if (ws.rQwait("FBU", FBU.bytes)) { return false; }849        if (FBU.bytes === 0) {850            if (ws.rQwait("rect header", 12)) { return false; }851            /* New FramebufferUpdate */852            hdr = ws.rQshiftBytes(12);853            FBU.x      = (hdr[0] << 8) + hdr[1];854            FBU.y      = (hdr[2] << 8) + hdr[3];855            FBU.width  = (hdr[4] << 8) + hdr[5];856            FBU.height = (hdr[6] << 8) + hdr[7];857            FBU.encoding = parseInt((hdr[8] << 24) + (hdr[9] << 16) +858                                    (hdr[10] << 8) +  hdr[11], 10);859            conf.onFBUReceive(that,860                    {'x': FBU.x, 'y': FBU.y,861                     'width': FBU.width, 'height': FBU.height,862                     'encoding': FBU.encoding,863                     'encodingName': encNames[FBU.encoding]});864            if (encNames[FBU.encoding]) {865                // Debug:866                /*867                var msg =  "FramebufferUpdate rects:" + FBU.rects;868                msg += " x: " + FBU.x + " y: " + FBU.y;869                msg += " width: " + FBU.width + " height: " + FBU.height;870                msg += " encoding:" + FBU.encoding;871                msg += "(" + encNames[FBU.encoding] + ")";872                msg += ", ws.rQlen(): " + ws.rQlen();873                Util.Debug(msg);874                */875            } else {876                fail("Disconnected: unsupported encoding " +877                    FBU.encoding);878                return false;879            }880        }881        timing.last_fbu = (new Date()).getTime();882        ret = encHandlers[FBU.encoding]();883        now = (new Date()).getTime();884        timing.cur_fbu += (now - timing.last_fbu);885        if (ret) {886            encStats[FBU.encoding][0] += 1;887            encStats[FBU.encoding][1] += 1;888            timing.pixels += FBU.width * FBU.height;889        }890        if (timing.pixels >= (fb_width * fb_height)) {891            if (((FBU.width === fb_width) &&892                        (FBU.height === fb_height)) ||893                    (timing.fbu_rt_start > 0)) {894                timing.full_fbu_total += timing.cur_fbu;895                timing.full_fbu_cnt += 1;896                Util.Info("Timing of full FBU, cur: " +897                          timing.cur_fbu + ", total: " +898                          timing.full_fbu_total + ", cnt: " +899                          timing.full_fbu_cnt + ", avg: " +900                          (timing.full_fbu_total /901                              timing.full_fbu_cnt));902            }903            if (timing.fbu_rt_start > 0) {904                fbu_rt_diff = now - timing.fbu_rt_start;905                timing.fbu_rt_total += fbu_rt_diff;906                timing.fbu_rt_cnt += 1;907                Util.Info("full FBU round-trip, cur: " +908                          fbu_rt_diff + ", total: " +909                          timing.fbu_rt_total + ", cnt: " +910                          timing.fbu_rt_cnt + ", avg: " +911                          (timing.fbu_rt_total /912                              timing.fbu_rt_cnt));913                timing.fbu_rt_start = 0;914            }915        }916        if (! ret) {917            return ret; // false ret means need more data918        }919    }920    conf.onFBUComplete(that,921            {'x': FBU.x, 'y': FBU.y,922                'width': FBU.width, 'height': FBU.height,923                'encoding': FBU.encoding,924                'encodingName': encNames[FBU.encoding]});925    return true; // We finished this FBU926};927//928// FramebufferUpdate encodings929//930encHandlers.RAW = function display_raw() {931    //Util.Debug(">> display_raw (" + ws.rQlen() + " bytes)");932    var cur_y, cur_height;933    if (FBU.lines === 0) {934        FBU.lines = FBU.height;935    }936    FBU.bytes = FBU.width * fb_Bpp; // At least a line937    if (ws.rQwait("RAW", FBU.bytes)) { return false; }938    cur_y = FBU.y + (FBU.height - FBU.lines);939    cur_height = Math.min(FBU.lines,940                          Math.floor(ws.rQlen()/(FBU.width * fb_Bpp)));941    display.blitImage(FBU.x, cur_y, FBU.width, cur_height,942            ws.get_rQ(), ws.get_rQi());943    ws.rQshiftBytes(FBU.width * cur_height * fb_Bpp);944    FBU.lines -= cur_height;945    if (FBU.lines > 0) {946        FBU.bytes = FBU.width * fb_Bpp; // At least another line947    } else {948        FBU.rects -= 1;949        FBU.bytes = 0;950    }951    //Util.Debug("<< display_raw (" + ws.rQlen() + " bytes)");952    return true;953};954encHandlers.COPYRECT = function display_copy_rect() {955    //Util.Debug(">> display_copy_rect");956    var old_x, old_y;957    if (ws.rQwait("COPYRECT", 4)) { return false; }958    display.renderQ_push({959            'type': 'copy',960            'old_x': ws.rQshift16(),961            'old_y': ws.rQshift16(),962            'x': FBU.x,963            'y': FBU.y,964            'width': FBU.width,965            'height': FBU.height});966    FBU.rects -= 1;967    FBU.bytes = 0;968    return true;969};970encHandlers.RRE = function display_rre() {971    //Util.Debug(">> display_rre (" + ws.rQlen() + " bytes)");972    var color, x, y, width, height, chunk;973    if (FBU.subrects === 0) {974        if (ws.rQwait("RRE", 4+fb_Bpp)) { return false; }975        FBU.subrects = ws.rQshift32();976        color = ws.rQshiftBytes(fb_Bpp); // Background977        display.fillRect(FBU.x, FBU.y, FBU.width, FBU.height, color);978    }979    while ((FBU.subrects > 0) && (ws.rQlen() >= (fb_Bpp + 8))) {980        color = ws.rQshiftBytes(fb_Bpp);981        x = ws.rQshift16();982        y = ws.rQshift16();983        width = ws.rQshift16();984        height = ws.rQshift16();985        display.fillRect(FBU.x + x, FBU.y + y, width, height, color);986        FBU.subrects -= 1;987    }988    //Util.Debug("   display_rre: rects: " + FBU.rects +989    //           ", FBU.subrects: " + FBU.subrects);990    if (FBU.subrects > 0) {991        chunk = Math.min(rre_chunk_sz, FBU.subrects);992        FBU.bytes = (fb_Bpp + 8) * chunk;993    } else {994        FBU.rects -= 1;995        FBU.bytes = 0;996    }997    //Util.Debug("<< display_rre, FBU.bytes: " + FBU.bytes);998    return true;999};1000encHandlers.HEXTILE = function display_hextile() {1001    //Util.Debug(">> display_hextile");1002    var subencoding, subrects, color, cur_tile,1003        tile_x, x, w, tile_y, y, h, xy, s, sx, sy, wh, sw, sh,1004        rQ = ws.get_rQ(), rQi = ws.get_rQi(); 1005    if (FBU.tiles === 0) {1006        FBU.tiles_x = Math.ceil(FBU.width/16);1007        FBU.tiles_y = Math.ceil(FBU.height/16);1008        FBU.total_tiles = FBU.tiles_x * FBU.tiles_y;1009        FBU.tiles = FBU.total_tiles;1010    }1011    /* FBU.bytes comes in as 1, ws.rQlen() at least 1 */1012    while (FBU.tiles > 0) {1013        FBU.bytes = 1;1014        if (ws.rQwait("HEXTILE subencoding", FBU.bytes)) { return false; }1015        subencoding = rQ[rQi];  // Peek1016        if (subencoding > 30) { // Raw1017            fail("Disconnected: illegal hextile subencoding " + subencoding);1018            //Util.Debug("ws.rQslice(0,30):" + ws.rQslice(0,30));1019            return false;1020        }1021        subrects = 0;1022        cur_tile = FBU.total_tiles - FBU.tiles;1023        tile_x = cur_tile % FBU.tiles_x;1024        tile_y = Math.floor(cur_tile / FBU.tiles_x);1025        x = FBU.x + tile_x * 16;1026        y = FBU.y + tile_y * 16;1027        w = Math.min(16, (FBU.x + FBU.width) - x);1028        h = Math.min(16, (FBU.y + FBU.height) - y);1029        /* Figure out how much we are expecting */1030        if (subencoding & 0x01) { // Raw1031            //Util.Debug("   Raw subencoding");1032            FBU.bytes += w * h * fb_Bpp;1033        } else {1034            if (subencoding & 0x02) { // Background1035                FBU.bytes += fb_Bpp;1036            }1037            if (subencoding & 0x04) { // Foreground1038                FBU.bytes += fb_Bpp;1039            }1040            if (subencoding & 0x08) { // AnySubrects1041                FBU.bytes += 1;   // Since we aren't shifting it off1042                if (ws.rQwait("hextile subrects header", FBU.bytes)) { return false; }1043                subrects = rQ[rQi + FBU.bytes-1]; // Peek1044                if (subencoding & 0x10) { // SubrectsColoured1045                    FBU.bytes += subrects * (fb_Bpp + 2);1046                } else {1047                    FBU.bytes += subrects * 2;1048                }1049            }1050        }1051        /*1052        Util.Debug("   tile:" + cur_tile + "/" + (FBU.total_tiles - 1) +1053              " (" + tile_x + "," + tile_y + ")" +1054              " [" + x + "," + y + "]@" + w + "x" + h +1055              ", subenc:" + subencoding +1056              "(last: " + FBU.lastsubencoding + "), subrects:" +1057              subrects +1058              ", ws.rQlen():" + ws.rQlen() + ", FBU.bytes:" + FBU.bytes +1059              " last:" + ws.rQslice(FBU.bytes-10, FBU.bytes) +1060              " next:" + ws.rQslice(FBU.bytes-1, FBU.bytes+10));1061        */1062        if (ws.rQwait("hextile", FBU.bytes)) { return false; }1063        /* We know the encoding and have a whole tile */1064        FBU.subencoding = rQ[rQi];1065        rQi += 1;1066        if (FBU.subencoding === 0) {1067            if (FBU.lastsubencoding & 0x01) {1068                /* Weird: ignore blanks after RAW */1069                Util.Debug("     Ignoring blank after RAW");1070            } else {1071                display.fillRect(x, y, w, h, FBU.background);1072            }1073        } else if (FBU.subencoding & 0x01) { // Raw1074            display.blitImage(x, y, w, h, rQ, rQi);1075            rQi += FBU.bytes - 1;1076        } else {1077            if (FBU.subencoding & 0x02) { // Background1078                FBU.background = rQ.slice(rQi, rQi + fb_Bpp);1079                rQi += fb_Bpp;1080            }1081            if (FBU.subencoding & 0x04) { // Foreground1082                FBU.foreground = rQ.slice(rQi, rQi + fb_Bpp);1083                rQi += fb_Bpp;1084            }1085            display.startTile(x, y, w, h, FBU.background);1086            if (FBU.subencoding & 0x08) { // AnySubrects1087                subrects = rQ[rQi];1088                rQi += 1;1089                for (s = 0; s < subrects; s += 1) {1090                    if (FBU.subencoding & 0x10) { // SubrectsColoured1091                        color = rQ.slice(rQi, rQi + fb_Bpp);1092                        rQi += fb_Bpp;1093                    } else {1094                        color = FBU.foreground;1095                    }1096                    xy = rQ[rQi];1097                    rQi += 1;1098                    sx = (xy >> 4);1099                    sy = (xy & 0x0f);1100                    wh = rQ[rQi];1101                    rQi += 1;1102                    sw = (wh >> 4)   + 1;1103                    sh = (wh & 0x0f) + 1;1104                    display.subTile(sx, sy, sw, sh, color);1105                }1106            }1107            display.finishTile();1108        }1109        ws.set_rQi(rQi);1110        FBU.lastsubencoding = FBU.subencoding;1111        FBU.bytes = 0;1112        FBU.tiles -= 1;1113    }1114    if (FBU.tiles === 0) {1115        FBU.rects -= 1;1116    }1117    //Util.Debug("<< display_hextile");1118    return true;1119};1120// Get 'compact length' header and data size1121getTightCLength = function (arr) {1122    var header = 1, data = 0;1123    data += arr[0] & 0x7f;1124    if (arr[0] & 0x80) {1125        header += 1;1126        data += (arr[1] & 0x7f) << 7;1127        if (arr[1] & 0x80) {1128            header += 1;1129            data += arr[2] << 14;1130        }1131    }1132    return [header, data];1133};1134function display_tight(isTightPNG) {1135    //Util.Debug(">> display_tight");1136    if (fb_depth === 1) {1137        fail("Tight protocol handler only implements true color mode");1138    }1139    var ctl, cmode, clength, color, img, data;1140    var filterId = -1, resetStreams = 0, streamId = -1;1141    var rQ = ws.get_rQ(), rQi = ws.get_rQi(); 1142    FBU.bytes = 1; // compression-control byte1143    if (ws.rQwait("TIGHT compression-control", FBU.bytes)) { return false; }1144    var checksum = function(data) {1145        var sum=0, i;1146        for (i=0; i<data.length;i++) {1147            sum += data[i];1148            if (sum > 65536) sum -= 65536;1149        }1150        return sum;1151    }1152    var decompress = function(data) {1153        for (var i=0; i<4; i++) {1154            if ((resetStreams >> i) & 1) {1155                FBU.zlibs[i].reset();1156                Util.Info("Reset zlib stream " + i);1157            }1158        }1159        var uncompressed = FBU.zlibs[streamId].uncompress(data, 0);1160        if (uncompressed.status !== 0) {1161            Util.Error("Invalid data in zlib stream");1162        }1163        //Util.Warn("Decompressed " + data.length + " to " +1164        //    uncompressed.data.length + " checksums " +1165        //    checksum(data) + ":" + checksum(uncompressed.data));1166        return uncompressed.data;1167    }1168    var handlePalette = function() {1169        var numColors = rQ[rQi + 2] + 1;1170        var paletteSize = numColors * fb_depth; 1171        FBU.bytes += paletteSize;1172        if (ws.rQwait("TIGHT palette " + cmode, FBU.bytes)) { return false; }1173        var bpp = (numColors <= 2) ? 1 : 8;1174        var rowSize = Math.floor((FBU.width * bpp + 7) / 8);1175        var raw = false;1176        if (rowSize * FBU.height < 12) {1177            raw = true;1178            clength = [0, rowSize * FBU.height];1179        } else {1180            clength = getTightCLength(ws.rQslice(3 + paletteSize,1181                                                 3 + paletteSize + 3));1182        }1183        FBU.bytes += clength[0] + clength[1];1184        if (ws.rQwait("TIGHT " + cmode, FBU.bytes)) { return false; }1185        // Shift ctl, filter id, num colors, palette entries, and clength off1186        ws.rQshiftBytes(3); 1187        var palette = ws.rQshiftBytes(paletteSize);1188        ws.rQshiftBytes(clength[0]);1189        if (raw) {1190            data = ws.rQshiftBytes(clength[1]);1191        } else {1192            data = decompress(ws.rQshiftBytes(clength[1]));1193        }1194        // Convert indexed (palette based) image data to RGB1195        // TODO: reduce number of calculations inside loop1196        var dest = [];1197        var x, y, b, w, w1, dp, sp;1198        if (numColors === 2) {1199            w = Math.floor((FBU.width + 7) / 8);1200            w1 = Math.floor(FBU.width / 8);1201            for (y = 0; y < FBU.height; y++) {1202                for (x = 0; x < w1; x++) {1203                    for (b = 7; b >= 0; b--) {1204                        dp = (y*FBU.width + x*8 + 7-b) * 3;1205                        sp = (data[y*w + x] >> b & 1) * 3;1206                        dest[dp  ] = palette[sp  ];1207                        dest[dp+1] = palette[sp+1];1208                        dest[dp+2] = palette[sp+2];1209                    }1210                }1211                for (b = 7; b >= 8 - FBU.width % 8; b--) {1212                    dp = (y*FBU.width + x*8 + 7-b) * 3;1213                    sp = (data[y*w + x] >> b & 1) * 3;1214                    dest[dp  ] = palette[sp  ];1215                    dest[dp+1] = palette[sp+1];1216                    dest[dp+2] = palette[sp+2];1217                }1218            }1219        } else {1220            for (y = 0; y < FBU.height; y++) {1221                for (x = 0; x < FBU.width; x++) {1222                    dp = (y*FBU.width + x) * 3;1223                    sp = data[y*FBU.width + x] * 3;1224                    dest[dp  ] = palette[sp  ];1225                    dest[dp+1] = palette[sp+1];1226                    dest[dp+2] = palette[sp+2];1227                }1228            }1229        }1230        display.renderQ_push({1231                'type': 'blitRgb',1232                'data': dest,1233                'x': FBU.x,1234                'y': FBU.y,1235                'width': FBU.width,1236                'height': FBU.height});1237        return true;1238    }1239    var handleCopy = function() {1240        var raw = false;1241        var uncompressedSize = FBU.width * FBU.height * fb_depth;1242        if (uncompressedSize < 12) {1243            raw = true;1244            clength = [0, uncompressedSize];1245        } else {1246            clength = getTightCLength(ws.rQslice(1, 4));1247        }1248        FBU.bytes = 1 + clength[0] + clength[1];1249        if (ws.rQwait("TIGHT " + cmode, FBU.bytes)) { return false; }1250        // Shift ctl, clength off1251        ws.rQshiftBytes(1 + clength[0]);1252        if (raw) {1253            data = ws.rQshiftBytes(clength[1]);1254        } else {1255            data = decompress(ws.rQshiftBytes(clength[1]));1256        }1257        display.renderQ_push({1258                'type': 'blitRgb',1259                'data': data,1260                'x': FBU.x,1261                'y': FBU.y,1262                'width': FBU.width,1263                'height': FBU.height});1264        return true;1265    }1266    ctl = ws.rQpeek8();1267    // Keep tight reset bits1268    resetStreams = ctl & 0xF;1269    // Figure out filter1270    ctl = ctl >> 4; 1271    streamId = ctl & 0x3;1272    if (ctl === 0x08)      cmode = "fill";1273    else if (ctl === 0x09) cmode = "jpeg";1274    else if (ctl === 0x0A) cmode = "png";1275    else if (ctl & 0x04)   cmode = "filter";1276    else if (ctl < 0x04)   cmode = "copy";1277    else return fail("Illegal tight compression received, ctl: " + ctl);1278    if (isTightPNG && (cmode === "filter" || cmode === "copy")) {1279        return fail("filter/copy received in tightPNG mode");1280    }1281    switch (cmode) {1282        // fill uses fb_depth because TPIXELs drop the padding byte1283        case "fill":   FBU.bytes += fb_depth; break; // TPIXEL1284        case "jpeg":   FBU.bytes += 3;        break; // max clength1285        case "png":    FBU.bytes += 3;        break; // max clength1286        case "filter": FBU.bytes += 2;        break; // filter id + num colors if palette1287        case "copy":                          break;1288    }1289    if (ws.rQwait("TIGHT " + cmode, FBU.bytes)) { return false; }1290    //Util.Debug("   ws.rQslice(0,20): " + ws.rQslice(0,20) + " (" + ws.rQlen() + ")");1291    //Util.Debug("   cmode: " + cmode);1292    // Determine FBU.bytes1293    switch (cmode) {1294    case "fill":1295        ws.rQshift8(); // shift off ctl1296        color = ws.rQshiftBytes(fb_depth);1297        display.renderQ_push({1298                'type': 'fill',1299                'x': FBU.x,1300                'y': FBU.y,1301                'width': FBU.width,1302                'height': FBU.height,1303                'color': [color[2], color[1], color[0]] });1304        break;1305    case "png":1306    case "jpeg":1307        clength = getTightCLength(ws.rQslice(1, 4));1308        FBU.bytes = 1 + clength[0] + clength[1]; // ctl + clength size + jpeg-data1309        if (ws.rQwait("TIGHT " + cmode, FBU.bytes)) { return false; }1310        // We have everything, render it1311        //Util.Debug("   jpeg, ws.rQlen(): " + ws.rQlen() + ", clength[0]: " +1312        //           clength[0] + ", clength[1]: " + clength[1]);1313        ws.rQshiftBytes(1 + clength[0]); // shift off ctl + compact length1314        img = new Image();1315        img.src = "data:image/" + cmode +1316            extract_data_uri(ws.rQshiftBytes(clength[1]));1317        display.renderQ_push({1318                'type': 'img',1319                'img': img,1320                'x': FBU.x,1321                'y': FBU.y});1322        img = null;1323        break;1324    case "filter":1325        filterId = rQ[rQi + 1];1326        if (filterId === 1) {1327            if (!handlePalette()) { return false; }1328        } else {1329            // Filter 0, Copy could be valid here, but servers don't send it as an explicit filter1330            // Filter 2, Gradient is valid but not used if jpeg is enabled1331            throw("Unsupported tight subencoding received, filter: " + filterId);1332        }1333        break;1334    case "copy":1335        if (!handleCopy()) { return false; }1336        break;1337    }1338    FBU.bytes = 0;1339    FBU.rects -= 1;1340    //Util.Debug("   ending ws.rQslice(0,20): " + ws.rQslice(0,20) + " (" + ws.rQlen() + ")");1341    //Util.Debug("<< display_tight_png");1342    return true;1343}1344extract_data_uri = function(arr) {1345    //var i, stra = [];1346    //for (i=0; i< arr.length; i += 1) {1347    //    stra.push(String.fromCharCode(arr[i]));1348    //}1349    //return "," + escape(stra.join(''));1350    return ";base64," + Base64.encode(arr);1351};1352encHandlers.TIGHT = function () { return display_tight(false); };1353encHandlers.TIGHT_PNG = function () { return display_tight(true); };1354encHandlers.last_rect = function last_rect() {...rfb.js
Source:rfb.js  
...432    return false;433};434handle_message = function() {435    //Util.Debug(">> handle_message ws.rQlen(): " + ws.rQlen());436    //Util.Debug("ws.rQslice(0,20): " + ws.rQslice(0,20) + " (" + ws.rQlen() + ")");437    if (ws.rQlen() === 0) {438        Util.Warn("handle_message called on empty receive queue");439        return;440    }441    switch (rfb_state) {442    case 'disconnected':443    case 'failed':444        Util.Error("Got data while disconnected");445        break;446    case 'normal':447        if (normal_msg() && ws.rQlen() > 0) {448            // true means we can continue processing449            // Give other events a chance to run450            if (msgTimer === null) {451                Util.Debug("More data to process, creating timer");452                msgTimer = setTimeout(function () {453                            msgTimer = null;454                            handle_message();455                        }, 10);456            } else {457                Util.Debug("More data to process, existing timer");458            }459        }460        break;461    default:462        init_msg();463        break;464    }465};466function genDES(password, challenge) {467    var i, passwd = [];468    for (i=0; i < password.length; i += 1) {469        passwd.push(password.charCodeAt(i));470    }471    return (new DES(passwd)).encrypt(challenge);472}473function flushClient() {474    if (mouse_arr.length > 0) {475        //send(mouse_arr.concat(fbUpdateRequests()));476        ws.send(mouse_arr);477        setTimeout(function() {478                ws.send(fbUpdateRequests());479            }, 50);480        mouse_arr = [];481        return true;482    } else {483        return false;484    }485}486// overridable for testing487checkEvents = function() {488    var now;489    if (rfb_state === 'normal' && !viewportDragging) {490        if (! flushClient()) {491            now = new Date().getTime();492            if (now > last_req_time + conf.fbu_req_rate) {493                last_req_time = now;494                ws.send(fbUpdateRequests());495            }496        }497    }498    setTimeout(checkEvents, conf.check_rate);499};500keyPress = function(keysym, down) {501    var arr;502    if (conf.view_only) { return; } // View only, skip keyboard events503    arr = keyEvent(keysym, down);504    arr = arr.concat(fbUpdateRequests());505    ws.send(arr);506};507mouseButton = function(x, y, down, bmask) {508    if (down) {509        mouse_buttonMask |= bmask;510    } else {511        mouse_buttonMask ^= bmask;512    }513    if (conf.viewportDrag) {514        if (down && !viewportDragging) {515            viewportDragging = true;516            viewportDragPos = {'x': x, 'y': y};517            // Skip sending mouse events518            return;519        } else {520            viewportDragging = false;521            ws.send(fbUpdateRequests()); // Force immediate redraw522        }523    }524    if (conf.view_only) { return; } // View only, skip mouse events525    mouse_arr = mouse_arr.concat(526            pointerEvent(display.absX(x), display.absY(y)) );527    flushClient();528};529mouseMove = function(x, y) {530    //Util.Debug('>> mouseMove ' + x + "," + y);531    var deltaX, deltaY;532    if (viewportDragging) {533        //deltaX = x - viewportDragPos.x; // drag viewport534        deltaX = viewportDragPos.x - x; // drag frame buffer535        //deltaY = y - viewportDragPos.y; // drag viewport536        deltaY = viewportDragPos.y - y; // drag frame buffer537        viewportDragPos = {'x': x, 'y': y};538        display.viewportChange(deltaX, deltaY);539        // Skip sending mouse events540        return;541    }542    if (conf.view_only) { return; } // View only, skip mouse events543    mouse_arr = mouse_arr.concat(544            pointerEvent(display.absX(x), display.absY(y)) );545};546//547// Server message handlers548//549// RFB/VNC initialisation message handler550init_msg = function() {551    //Util.Debug(">> init_msg [rfb_state '" + rfb_state + "']");552    var strlen, reason, length, sversion, cversion, repeaterID,553        i, types, num_types, challenge, response, bpp, depth,554        big_endian, red_max, green_max, blue_max, red_shift,555        green_shift, blue_shift, true_color, name_length, is_repeater;556    //Util.Debug("ws.rQ (" + ws.rQlen() + ") " + ws.rQslice(0));557    switch (rfb_state) {558    case 'ProtocolVersion' :559        if (ws.rQlen() < 12) {560            return fail("Incomplete protocol version");561        }562        sversion = ws.rQshiftStr(12).substr(4,7);563        Util.Info("Server ProtocolVersion: " + sversion);564        is_repeater = 0;565        switch (sversion) {566            case "000.000": is_repeater = 1; break; // UltraVNC repeater567            case "003.003": rfb_version = 3.3; break;568            case "003.006": rfb_version = 3.3; break;  // UltraVNC569            case "003.889": rfb_version = 3.3; break;  // Apple Remote Desktop570            case "003.007": rfb_version = 3.7; break;571            case "003.008": rfb_version = 3.8; break;572            case "004.000": rfb_version = 3.8; break;  // Intel AMT KVM573            case "004.001": rfb_version = 3.8; break;  // RealVNC 4.6574            default:575                return fail("Invalid server version " + sversion);576        }577        if (is_repeater) { 578            repeaterID = conf.repeaterID;579            while (repeaterID.length < 250) {580                repeaterID += "\0";581            }582            ws.send_string(repeaterID);583            break;584        }585        if (rfb_version > rfb_max_version) { 586            rfb_version = rfb_max_version;587        }588        if (! test_mode) {589            sendTimer = setInterval(function() {590                    // Send updates either at a rate of one update591                    // every 50ms, or whatever slower rate the network592                    // can handle.593                    ws.flush();594                }, 50);595        }596        cversion = "00" + parseInt(rfb_version,10) +597                   ".00" + ((rfb_version * 10) % 10);598        ws.send_string("RFB " + cversion + "\n");599        updateState('Security', "Sent ProtocolVersion: " + cversion);600        break;601    case 'Security' :602        if (rfb_version >= 3.7) {603            // Server sends supported list, client decides 604            num_types = ws.rQshift8();605            if (ws.rQwait("security type", num_types, 1)) { return false; }606            if (num_types === 0) {607                strlen = ws.rQshift32();608                reason = ws.rQshiftStr(strlen);609                return fail("Security failure: " + reason);610            }611            rfb_auth_scheme = 0;612            types = ws.rQshiftBytes(num_types);613            Util.Debug("Server security types: " + types);614            for (i=0; i < types.length; i+=1) {615                if ((types[i] > rfb_auth_scheme) && (types[i] < 3)) {616                    rfb_auth_scheme = types[i];617                }618            }619            if (rfb_auth_scheme === 0) {620                return fail("Unsupported security types: " + types);621            }622            623            ws.send([rfb_auth_scheme]);624        } else {625            // Server decides626            if (ws.rQwait("security scheme", 4)) { return false; }627            rfb_auth_scheme = ws.rQshift32();628        }629        updateState('Authentication',630                "Authenticating using scheme: " + rfb_auth_scheme);631        init_msg();  // Recursive fallthrough (workaround JSLint complaint)632        break;633    // Triggered by fallthough, not by server message634    case 'Authentication' :635        //Util.Debug("Security auth scheme: " + rfb_auth_scheme);636        switch (rfb_auth_scheme) {637            case 0:  // connection failed638                if (ws.rQwait("auth reason", 4)) { return false; }639                strlen = ws.rQshift32();640                reason = ws.rQshiftStr(strlen);641                return fail("Auth failure: " + reason);642            case 1:  // no authentication643                if (rfb_version >= 3.8) {644                    updateState('SecurityResult');645                    return;646                }647                // Fall through to ClientInitialisation648                break;649            case 2:  // VNC authentication650                if (rfb_password.length === 0) {651                    // Notify via both callbacks since it is kind of652                    // a RFB state change and a UI interface issue.653                    updateState('password', "Password Required");654                    conf.onPasswordRequired(that);655                    return;656                }657                if (ws.rQwait("auth challenge", 16)) { return false; }658                challenge = ws.rQshiftBytes(16);659                //Util.Debug("Password: " + rfb_password);660                //Util.Debug("Challenge: " + challenge +661                //           " (" + challenge.length + ")");662                response = genDES(rfb_password, challenge);663                //Util.Debug("Response: " + response +664                //           " (" + response.length + ")");665                666                //Util.Debug("Sending DES encrypted auth response");667                ws.send(response);668                updateState('SecurityResult');669                return;670            default:671                fail("Unsupported auth scheme: " + rfb_auth_scheme);672                return;673        }674        updateState('ClientInitialisation', "No auth required");675        init_msg();  // Recursive fallthrough (workaround JSLint complaint)676        break;677    case 'SecurityResult' :678        if (ws.rQwait("VNC auth response ", 4)) { return false; }679        switch (ws.rQshift32()) {680            case 0:  // OK681                // Fall through to ClientInitialisation682                break;683            case 1:  // failed684                if (rfb_version >= 3.8) {685                    length = ws.rQshift32();686                    if (ws.rQwait("SecurityResult reason", length, 8)) {687                        return false;688                    }689                    reason = ws.rQshiftStr(length);690                    fail(reason);691                } else {692                    fail("Authentication failed");693                }694                return;695            case 2:  // too-many696                return fail("Too many auth attempts");697        }698        updateState('ClientInitialisation', "Authentication OK");699        init_msg();  // Recursive fallthrough (workaround JSLint complaint)700        break;701    // Triggered by fallthough, not by server message702    case 'ClientInitialisation' :703        ws.send([conf.shared ? 1 : 0]); // ClientInitialisation704        updateState('ServerInitialisation', "Authentication OK");705        break;706    case 'ServerInitialisation' :707        if (ws.rQwait("server initialization", 24)) { return false; }708        /* Screen size */709        fb_width  = ws.rQshift16();710        fb_height = ws.rQshift16();711        /* PIXEL_FORMAT */712        bpp            = ws.rQshift8();713        depth          = ws.rQshift8();714        big_endian     = ws.rQshift8();715        true_color     = ws.rQshift8();716        red_max        = ws.rQshift16();717        green_max      = ws.rQshift16();718        blue_max       = ws.rQshift16();719        red_shift      = ws.rQshift8();720        green_shift    = ws.rQshift8();721        blue_shift     = ws.rQshift8();722        ws.rQshiftStr(3); // padding723        Util.Info("Screen: " + fb_width + "x" + fb_height + 724                  ", bpp: " + bpp + ", depth: " + depth +725                  ", big_endian: " + big_endian +726                  ", true_color: " + true_color +727                  ", red_max: " + red_max +728                  ", green_max: " + green_max +729                  ", blue_max: " + blue_max +730                  ", red_shift: " + red_shift +731                  ", green_shift: " + green_shift +732                  ", blue_shift: " + blue_shift);733        if (big_endian !== 0) {734            Util.Warn("Server native endian is not little endian");735        }736        if (red_shift !== 16) {737            Util.Warn("Server native red-shift is not 16");738        }739        if (blue_shift !== 0) {740            Util.Warn("Server native blue-shift is not 0");741        }742        /* Connection name/title */743        name_length   = ws.rQshift32();744        fb_name = ws.rQshiftStr(name_length);745        746        if (conf.true_color && fb_name === "Intel(r) AMT KVM")747        {748            Util.Warn("Intel AMT KVM only support 8/16 bit depths. Disabling true color");749            conf.true_color = false;750        }751        display.set_true_color(conf.true_color);752        display.resize(fb_width, fb_height);753        keyboard.grab();754        mouse.grab();755        if (conf.true_color) {756            fb_Bpp           = 4;757            fb_depth         = 3;758        } else {759            fb_Bpp           = 1;760            fb_depth         = 1;761        }762        response = pixelFormat();763        response = response.concat(clientEncodings());764        response = response.concat(fbUpdateRequests());765        timing.fbu_rt_start = (new Date()).getTime();766        timing.pixels = 0;767        ws.send(response);768        769        /* Start pushing/polling */770        setTimeout(checkEvents, conf.check_rate);771        if (conf.encrypt) {772            updateState('normal', "Connected (encrypted) to: " + fb_name);773        } else {774            updateState('normal', "Connected (unencrypted) to: " + fb_name);775        }776        break;777    }778    //Util.Debug("<< init_msg");779};780/* Normal RFB/VNC server message handler */781normal_msg = function() {782    //Util.Debug(">> normal_msg");783    var ret = true, msg_type, length, text,784        c, first_colour, num_colours, red, green, blue;785    if (FBU.rects > 0) {786        msg_type = 0;787    } else {788        msg_type = ws.rQshift8();789    }790    switch (msg_type) {791    case 0:  // FramebufferUpdate792        ret = framebufferUpdate(); // false means need more data793        break;794    case 1:  // SetColourMapEntries795        Util.Debug("SetColourMapEntries");796        ws.rQshift8();  // Padding797        first_colour = ws.rQshift16(); // First colour798        num_colours = ws.rQshift16();799        if (ws.rQwait("SetColourMapEntries", num_colours*6, 6)) { return false; }800        801        for (c=0; c < num_colours; c+=1) { 802            red = ws.rQshift16();803            //Util.Debug("red before: " + red);804            red = parseInt(red / 256, 10);805            //Util.Debug("red after: " + red);806            green = parseInt(ws.rQshift16() / 256, 10);807            blue = parseInt(ws.rQshift16() / 256, 10);808            display.set_colourMap([blue, green, red], first_colour + c);809        }810        Util.Debug("colourMap: " + display.get_colourMap());811        Util.Info("Registered " + num_colours + " colourMap entries");812        //Util.Debug("colourMap: " + display.get_colourMap());813        break;814    case 2:  // Bell815        Util.Debug("Bell");816        conf.onBell(that);817        break;818    case 3:  // ServerCutText819        Util.Debug("ServerCutText");820        if (ws.rQwait("ServerCutText header", 7, 1)) { return false; }821        ws.rQshiftBytes(3);  // Padding822        length = ws.rQshift32();823        if (ws.rQwait("ServerCutText", length, 8)) { return false; }824        text = ws.rQshiftStr(length);825        conf.clipboardReceive(that, text); // Obsolete826        conf.onClipboard(that, text);827        break;828    default:829        fail("Disconnected: illegal server message type " + msg_type);830        Util.Debug("ws.rQslice(0,30):" + ws.rQslice(0,30));831        break;832    }833    //Util.Debug("<< normal_msg");834    return ret;835};836framebufferUpdate = function() {837    var now, hdr, fbu_rt_diff, ret = true;838    if (FBU.rects === 0) {839        //Util.Debug("New FBU: ws.rQslice(0,20): " + ws.rQslice(0,20));840        if (ws.rQwait("FBU header", 3)) {841            ws.rQunshift8(0);  // FBU msg_type842            return false;843        }844        ws.rQshift8();  // padding845        FBU.rects = ws.rQshift16();846        //Util.Debug("FramebufferUpdate, rects:" + FBU.rects);847        FBU.bytes = 0;848        timing.cur_fbu = 0;849        if (timing.fbu_rt_start > 0) {850            now = (new Date()).getTime();851            Util.Info("First FBU latency: " + (now - timing.fbu_rt_start));852        }853    }854    while (FBU.rects > 0) {855        if (rfb_state !== "normal") {856            return false;857        }858        if (ws.rQwait("FBU", FBU.bytes)) { return false; }859        if (FBU.bytes === 0) {860            if (ws.rQwait("rect header", 12)) { return false; }861            /* New FramebufferUpdate */862            hdr = ws.rQshiftBytes(12);863            FBU.x      = (hdr[0] << 8) + hdr[1];864            FBU.y      = (hdr[2] << 8) + hdr[3];865            FBU.width  = (hdr[4] << 8) + hdr[5];866            FBU.height = (hdr[6] << 8) + hdr[7];867            FBU.encoding = parseInt((hdr[8] << 24) + (hdr[9] << 16) +868                                    (hdr[10] << 8) +  hdr[11], 10);869            conf.onFBUReceive(that,870                    {'x': FBU.x, 'y': FBU.y,871                     'width': FBU.width, 'height': FBU.height,872                     'encoding': FBU.encoding,873                     'encodingName': encNames[FBU.encoding]});874            if (encNames[FBU.encoding]) {875                // Debug:876                /*877                var msg =  "FramebufferUpdate rects:" + FBU.rects;878                msg += " x: " + FBU.x + " y: " + FBU.y;879                msg += " width: " + FBU.width + " height: " + FBU.height;880                msg += " encoding:" + FBU.encoding;881                msg += "(" + encNames[FBU.encoding] + ")";882                msg += ", ws.rQlen(): " + ws.rQlen();883                Util.Debug(msg);884                */885            } else {886                fail("Disconnected: unsupported encoding " +887                    FBU.encoding);888                return false;889            }890        }891        timing.last_fbu = (new Date()).getTime();892        ret = encHandlers[FBU.encoding]();893        now = (new Date()).getTime();894        timing.cur_fbu += (now - timing.last_fbu);895        if (ret) {896            encStats[FBU.encoding][0] += 1;897            encStats[FBU.encoding][1] += 1;898            timing.pixels += FBU.width * FBU.height;899        }900        if (timing.pixels >= (fb_width * fb_height)) {901            if (((FBU.width === fb_width) &&902                        (FBU.height === fb_height)) ||903                    (timing.fbu_rt_start > 0)) {904                timing.full_fbu_total += timing.cur_fbu;905                timing.full_fbu_cnt += 1;906                Util.Info("Timing of full FBU, cur: " +907                          timing.cur_fbu + ", total: " +908                          timing.full_fbu_total + ", cnt: " +909                          timing.full_fbu_cnt + ", avg: " +910                          (timing.full_fbu_total /911                              timing.full_fbu_cnt));912            }913            if (timing.fbu_rt_start > 0) {914                fbu_rt_diff = now - timing.fbu_rt_start;915                timing.fbu_rt_total += fbu_rt_diff;916                timing.fbu_rt_cnt += 1;917                Util.Info("full FBU round-trip, cur: " +918                          fbu_rt_diff + ", total: " +919                          timing.fbu_rt_total + ", cnt: " +920                          timing.fbu_rt_cnt + ", avg: " +921                          (timing.fbu_rt_total /922                              timing.fbu_rt_cnt));923                timing.fbu_rt_start = 0;924            }925        }926        if (! ret) {927            return ret; // false ret means need more data928        }929    }930    conf.onFBUComplete(that,931            {'x': FBU.x, 'y': FBU.y,932                'width': FBU.width, 'height': FBU.height,933                'encoding': FBU.encoding,934                'encodingName': encNames[FBU.encoding]});935    return true; // We finished this FBU936};937//938// FramebufferUpdate encodings939//940encHandlers.RAW = function display_raw() {941    //Util.Debug(">> display_raw (" + ws.rQlen() + " bytes)");942    var cur_y, cur_height;943    if (FBU.lines === 0) {944        FBU.lines = FBU.height;945    }946    FBU.bytes = FBU.width * fb_Bpp; // At least a line947    if (ws.rQwait("RAW", FBU.bytes)) { return false; }948    cur_y = FBU.y + (FBU.height - FBU.lines);949    cur_height = Math.min(FBU.lines,950                          Math.floor(ws.rQlen()/(FBU.width * fb_Bpp)));951    display.blitImage(FBU.x, cur_y, FBU.width, cur_height,952            ws.get_rQ(), ws.get_rQi());953    ws.rQshiftBytes(FBU.width * cur_height * fb_Bpp);954    FBU.lines -= cur_height;955    if (FBU.lines > 0) {956        FBU.bytes = FBU.width * fb_Bpp; // At least another line957    } else {958        FBU.rects -= 1;959        FBU.bytes = 0;960    }961    //Util.Debug("<< display_raw (" + ws.rQlen() + " bytes)");962    return true;963};964encHandlers.COPYRECT = function display_copy_rect() {965    //Util.Debug(">> display_copy_rect");966    var old_x, old_y;967    if (ws.rQwait("COPYRECT", 4)) { return false; }968    display.renderQ_push({969            'type': 'copy',970            'old_x': ws.rQshift16(),971            'old_y': ws.rQshift16(),972            'x': FBU.x,973            'y': FBU.y,974            'width': FBU.width,975            'height': FBU.height});976    FBU.rects -= 1;977    FBU.bytes = 0;978    return true;979};980encHandlers.RRE = function display_rre() {981    //Util.Debug(">> display_rre (" + ws.rQlen() + " bytes)");982    var color, x, y, width, height, chunk;983    if (FBU.subrects === 0) {984        if (ws.rQwait("RRE", 4+fb_Bpp)) { return false; }985        FBU.subrects = ws.rQshift32();986        color = ws.rQshiftBytes(fb_Bpp); // Background987        display.fillRect(FBU.x, FBU.y, FBU.width, FBU.height, color);988    }989    while ((FBU.subrects > 0) && (ws.rQlen() >= (fb_Bpp + 8))) {990        color = ws.rQshiftBytes(fb_Bpp);991        x = ws.rQshift16();992        y = ws.rQshift16();993        width = ws.rQshift16();994        height = ws.rQshift16();995        display.fillRect(FBU.x + x, FBU.y + y, width, height, color);996        FBU.subrects -= 1;997    }998    //Util.Debug("   display_rre: rects: " + FBU.rects +999    //           ", FBU.subrects: " + FBU.subrects);1000    if (FBU.subrects > 0) {1001        chunk = Math.min(rre_chunk_sz, FBU.subrects);1002        FBU.bytes = (fb_Bpp + 8) * chunk;1003    } else {1004        FBU.rects -= 1;1005        FBU.bytes = 0;1006    }1007    //Util.Debug("<< display_rre, FBU.bytes: " + FBU.bytes);1008    return true;1009};1010encHandlers.HEXTILE = function display_hextile() {1011    //Util.Debug(">> display_hextile");1012    var subencoding, subrects, color, cur_tile,1013        tile_x, x, w, tile_y, y, h, xy, s, sx, sy, wh, sw, sh,1014        rQ = ws.get_rQ(), rQi = ws.get_rQi(); 1015    if (FBU.tiles === 0) {1016        FBU.tiles_x = Math.ceil(FBU.width/16);1017        FBU.tiles_y = Math.ceil(FBU.height/16);1018        FBU.total_tiles = FBU.tiles_x * FBU.tiles_y;1019        FBU.tiles = FBU.total_tiles;1020    }1021    /* FBU.bytes comes in as 1, ws.rQlen() at least 1 */1022    while (FBU.tiles > 0) {1023        FBU.bytes = 1;1024        if (ws.rQwait("HEXTILE subencoding", FBU.bytes)) { return false; }1025        subencoding = rQ[rQi];  // Peek1026        if (subencoding > 30) { // Raw1027            fail("Disconnected: illegal hextile subencoding " + subencoding);1028            //Util.Debug("ws.rQslice(0,30):" + ws.rQslice(0,30));1029            return false;1030        }1031        subrects = 0;1032        cur_tile = FBU.total_tiles - FBU.tiles;1033        tile_x = cur_tile % FBU.tiles_x;1034        tile_y = Math.floor(cur_tile / FBU.tiles_x);1035        x = FBU.x + tile_x * 16;1036        y = FBU.y + tile_y * 16;1037        w = Math.min(16, (FBU.x + FBU.width) - x);1038        h = Math.min(16, (FBU.y + FBU.height) - y);1039        /* Figure out how much we are expecting */1040        if (subencoding & 0x01) { // Raw1041            //Util.Debug("   Raw subencoding");1042            FBU.bytes += w * h * fb_Bpp;1043        } else {1044            if (subencoding & 0x02) { // Background1045                FBU.bytes += fb_Bpp;1046            }1047            if (subencoding & 0x04) { // Foreground1048                FBU.bytes += fb_Bpp;1049            }1050            if (subencoding & 0x08) { // AnySubrects1051                FBU.bytes += 1;   // Since we aren't shifting it off1052                if (ws.rQwait("hextile subrects header", FBU.bytes)) { return false; }1053                subrects = rQ[rQi + FBU.bytes-1]; // Peek1054                if (subencoding & 0x10) { // SubrectsColoured1055                    FBU.bytes += subrects * (fb_Bpp + 2);1056                } else {1057                    FBU.bytes += subrects * 2;1058                }1059            }1060        }1061        /*1062        Util.Debug("   tile:" + cur_tile + "/" + (FBU.total_tiles - 1) +1063              " (" + tile_x + "," + tile_y + ")" +1064              " [" + x + "," + y + "]@" + w + "x" + h +1065              ", subenc:" + subencoding +1066              "(last: " + FBU.lastsubencoding + "), subrects:" +1067              subrects +1068              ", ws.rQlen():" + ws.rQlen() + ", FBU.bytes:" + FBU.bytes +1069              " last:" + ws.rQslice(FBU.bytes-10, FBU.bytes) +1070              " next:" + ws.rQslice(FBU.bytes-1, FBU.bytes+10));1071        */1072        if (ws.rQwait("hextile", FBU.bytes)) { return false; }1073        /* We know the encoding and have a whole tile */1074        FBU.subencoding = rQ[rQi];1075        rQi += 1;1076        if (FBU.subencoding === 0) {1077            if (FBU.lastsubencoding & 0x01) {1078                /* Weird: ignore blanks after RAW */1079                Util.Debug("     Ignoring blank after RAW");1080            } else {1081                display.fillRect(x, y, w, h, FBU.background);1082            }1083        } else if (FBU.subencoding & 0x01) { // Raw1084            display.blitImage(x, y, w, h, rQ, rQi);1085            rQi += FBU.bytes - 1;1086        } else {1087            if (FBU.subencoding & 0x02) { // Background1088                FBU.background = rQ.slice(rQi, rQi + fb_Bpp);1089                rQi += fb_Bpp;1090            }1091            if (FBU.subencoding & 0x04) { // Foreground1092                FBU.foreground = rQ.slice(rQi, rQi + fb_Bpp);1093                rQi += fb_Bpp;1094            }1095            display.startTile(x, y, w, h, FBU.background);1096            if (FBU.subencoding & 0x08) { // AnySubrects1097                subrects = rQ[rQi];1098                rQi += 1;1099                for (s = 0; s < subrects; s += 1) {1100                    if (FBU.subencoding & 0x10) { // SubrectsColoured1101                        color = rQ.slice(rQi, rQi + fb_Bpp);1102                        rQi += fb_Bpp;1103                    } else {1104                        color = FBU.foreground;1105                    }1106                    xy = rQ[rQi];1107                    rQi += 1;1108                    sx = (xy >> 4);1109                    sy = (xy & 0x0f);1110                    wh = rQ[rQi];1111                    rQi += 1;1112                    sw = (wh >> 4)   + 1;1113                    sh = (wh & 0x0f) + 1;1114                    display.subTile(sx, sy, sw, sh, color);1115                }1116            }1117            display.finishTile();1118        }1119        ws.set_rQi(rQi);1120        FBU.lastsubencoding = FBU.subencoding;1121        FBU.bytes = 0;1122        FBU.tiles -= 1;1123    }1124    if (FBU.tiles === 0) {1125        FBU.rects -= 1;1126    }1127    //Util.Debug("<< display_hextile");1128    return true;1129};1130// Get 'compact length' header and data size1131getTightCLength = function (arr) {1132    var header = 1, data = 0;1133    data += arr[0] & 0x7f;1134    if (arr[0] & 0x80) {1135        header += 1;1136        data += (arr[1] & 0x7f) << 7;1137        if (arr[1] & 0x80) {1138            header += 1;1139            data += arr[2] << 14;1140        }1141    }1142    return [header, data];1143};1144function display_tight(isTightPNG) {1145    //Util.Debug(">> display_tight");1146    if (fb_depth === 1) {1147        fail("Tight protocol handler only implements true color mode");1148    }1149    var ctl, cmode, clength, color, img, data;1150    var filterId = -1, resetStreams = 0, streamId = -1;1151    var rQ = ws.get_rQ(), rQi = ws.get_rQi(); 1152    FBU.bytes = 1; // compression-control byte1153    if (ws.rQwait("TIGHT compression-control", FBU.bytes)) { return false; }1154    var checksum = function(data) {1155        var sum=0, i;1156        for (i=0; i<data.length;i++) {1157            sum += data[i];1158            if (sum > 65536) sum -= 65536;1159        }1160        return sum;1161    }1162    var decompress = function(data) {1163        for (var i=0; i<4; i++) {1164            if ((resetStreams >> i) & 1) {1165                FBU.zlibs[i].reset();1166                Util.Info("Reset zlib stream " + i);1167            }1168        }1169        var uncompressed = FBU.zlibs[streamId].uncompress(data, 0);1170        if (uncompressed.status !== 0) {1171            Util.Error("Invalid data in zlib stream");1172        }1173        //Util.Warn("Decompressed " + data.length + " to " +1174        //    uncompressed.data.length + " checksums " +1175        //    checksum(data) + ":" + checksum(uncompressed.data));1176        return uncompressed.data;1177    }1178    var handlePalette = function() {1179        var numColors = rQ[rQi + 2] + 1;1180        var paletteSize = numColors * fb_depth; 1181        FBU.bytes += paletteSize;1182        if (ws.rQwait("TIGHT palette " + cmode, FBU.bytes)) { return false; }1183        var bpp = (numColors <= 2) ? 1 : 8;1184        var rowSize = Math.floor((FBU.width * bpp + 7) / 8);1185        var raw = false;1186        if (rowSize * FBU.height < 12) {1187            raw = true;1188            clength = [0, rowSize * FBU.height];1189        } else {1190            clength = getTightCLength(ws.rQslice(3 + paletteSize,1191                                                 3 + paletteSize + 3));1192        }1193        FBU.bytes += clength[0] + clength[1];1194        if (ws.rQwait("TIGHT " + cmode, FBU.bytes)) { return false; }1195        // Shift ctl, filter id, num colors, palette entries, and clength off1196        ws.rQshiftBytes(3); 1197        var palette = ws.rQshiftBytes(paletteSize);1198        ws.rQshiftBytes(clength[0]);1199        if (raw) {1200            data = ws.rQshiftBytes(clength[1]);1201        } else {1202            data = decompress(ws.rQshiftBytes(clength[1]));1203        }1204        // Convert indexed (palette based) image data to RGB1205        // TODO: reduce number of calculations inside loop1206        var dest = [];1207        var x, y, b, w, w1, dp, sp;1208        if (numColors === 2) {1209            w = Math.floor((FBU.width + 7) / 8);1210            w1 = Math.floor(FBU.width / 8);1211            for (y = 0; y < FBU.height; y++) {1212                for (x = 0; x < w1; x++) {1213                    for (b = 7; b >= 0; b--) {1214                        dp = (y*FBU.width + x*8 + 7-b) * 3;1215                        sp = (data[y*w + x] >> b & 1) * 3;1216                        dest[dp  ] = palette[sp  ];1217                        dest[dp+1] = palette[sp+1];1218                        dest[dp+2] = palette[sp+2];1219                    }1220                }1221                for (b = 7; b >= 8 - FBU.width % 8; b--) {1222                    dp = (y*FBU.width + x*8 + 7-b) * 3;1223                    sp = (data[y*w + x] >> b & 1) * 3;1224                    dest[dp  ] = palette[sp  ];1225                    dest[dp+1] = palette[sp+1];1226                    dest[dp+2] = palette[sp+2];1227                }1228            }1229        } else {1230            for (y = 0; y < FBU.height; y++) {1231                for (x = 0; x < FBU.width; x++) {1232                    dp = (y*FBU.width + x) * 3;1233                    sp = data[y*FBU.width + x] * 3;1234                    dest[dp  ] = palette[sp  ];1235                    dest[dp+1] = palette[sp+1];1236                    dest[dp+2] = palette[sp+2];1237                }1238            }1239        }1240        display.renderQ_push({1241                'type': 'blitRgb',1242                'data': dest,1243                'x': FBU.x,1244                'y': FBU.y,1245                'width': FBU.width,1246                'height': FBU.height});1247        return true;1248    }1249    var handleCopy = function() {1250        var raw = false;1251        var uncompressedSize = FBU.width * FBU.height * fb_depth;1252        if (uncompressedSize < 12) {1253            raw = true;1254            clength = [0, uncompressedSize];1255        } else {1256            clength = getTightCLength(ws.rQslice(1, 4));1257        }1258        FBU.bytes = 1 + clength[0] + clength[1];1259        if (ws.rQwait("TIGHT " + cmode, FBU.bytes)) { return false; }1260        // Shift ctl, clength off1261        ws.rQshiftBytes(1 + clength[0]);1262        if (raw) {1263            data = ws.rQshiftBytes(clength[1]);1264        } else {1265            data = decompress(ws.rQshiftBytes(clength[1]));1266        }1267        display.renderQ_push({1268                'type': 'blitRgb',1269                'data': data,1270                'x': FBU.x,1271                'y': FBU.y,1272                'width': FBU.width,1273                'height': FBU.height});1274        return true;1275    }1276    ctl = ws.rQpeek8();1277    // Keep tight reset bits1278    resetStreams = ctl & 0xF;1279    // Figure out filter1280    ctl = ctl >> 4; 1281    streamId = ctl & 0x3;1282    if (ctl === 0x08)      cmode = "fill";1283    else if (ctl === 0x09) cmode = "jpeg";1284    else if (ctl === 0x0A) cmode = "png";1285    else if (ctl & 0x04)   cmode = "filter";1286    else if (ctl < 0x04)   cmode = "copy";1287    else return fail("Illegal tight compression received, ctl: " + ctl);1288    if (isTightPNG && (cmode === "filter" || cmode === "copy")) {1289        return fail("filter/copy received in tightPNG mode");1290    }1291    switch (cmode) {1292        // fill uses fb_depth because TPIXELs drop the padding byte1293        case "fill":   FBU.bytes += fb_depth; break; // TPIXEL1294        case "jpeg":   FBU.bytes += 3;        break; // max clength1295        case "png":    FBU.bytes += 3;        break; // max clength1296        case "filter": FBU.bytes += 2;        break; // filter id + num colors if palette1297        case "copy":                          break;1298    }1299    if (ws.rQwait("TIGHT " + cmode, FBU.bytes)) { return false; }1300    //Util.Debug("   ws.rQslice(0,20): " + ws.rQslice(0,20) + " (" + ws.rQlen() + ")");1301    //Util.Debug("   cmode: " + cmode);1302    // Determine FBU.bytes1303    switch (cmode) {1304    case "fill":1305        ws.rQshift8(); // shift off ctl1306        color = ws.rQshiftBytes(fb_depth);1307        display.renderQ_push({1308                'type': 'fill',1309                'x': FBU.x,1310                'y': FBU.y,1311                'width': FBU.width,1312                'height': FBU.height,1313                'color': [color[2], color[1], color[0]] });1314        break;1315    case "png":1316    case "jpeg":1317        clength = getTightCLength(ws.rQslice(1, 4));1318        FBU.bytes = 1 + clength[0] + clength[1]; // ctl + clength size + jpeg-data1319        if (ws.rQwait("TIGHT " + cmode, FBU.bytes)) { return false; }1320        // We have everything, render it1321        //Util.Debug("   jpeg, ws.rQlen(): " + ws.rQlen() + ", clength[0]: " +1322        //           clength[0] + ", clength[1]: " + clength[1]);1323        ws.rQshiftBytes(1 + clength[0]); // shift off ctl + compact length1324        img = new Image();1325        img.src = "data:image/" + cmode +1326            extract_data_uri(ws.rQshiftBytes(clength[1]));1327        display.renderQ_push({1328                'type': 'img',1329                'img': img,1330                'x': FBU.x,1331                'y': FBU.y});1332        img = null;1333        break;1334    case "filter":1335        filterId = rQ[rQi + 1];1336        if (filterId === 1) {1337            if (!handlePalette()) { return false; }1338        } else {1339            // Filter 0, Copy could be valid here, but servers don't send it as an explicit filter1340            // Filter 2, Gradient is valid but not used if jpeg is enabled1341            throw("Unsupported tight subencoding received, filter: " + filterId);1342        }1343        break;1344    case "copy":1345        if (!handleCopy()) { return false; }1346        break;1347    }1348    FBU.bytes = 0;1349    FBU.rects -= 1;1350    //Util.Debug("   ending ws.rQslice(0,20): " + ws.rQslice(0,20) + " (" + ws.rQlen() + ")");1351    //Util.Debug("<< display_tight_png");1352    return true;1353}1354extract_data_uri = function(arr) {1355    //var i, stra = [];1356    //for (i=0; i< arr.length; i += 1) {1357    //    stra.push(String.fromCharCode(arr[i]));1358    //}1359    //return "," + escape(stra.join(''));1360    return ";base64," + Base64.encode(arr);1361};1362encHandlers.TIGHT = function () { return display_tight(false); };1363encHandlers.TIGHT_PNG = function () { return display_tight(true); };1364encHandlers.last_rect = function last_rect() {...Using AI Code Generation
1var redwood = require("./redwood");2var rQ = redwood.rQ;3var rQslice = redwood.rQslice;4var data = [1,2,3,4,5,6,7,8,9,10];5var rq = rQ(data);6var sliced = rQslice(rq, 2, 5);7console.log("rq = " + rq);8console.log("sliced = " + sliced);9var sliced2 = rQslice(rq, 2, 5, 2);10console.log("sliced2 = " + sliced2);11var sliced3 = rQslice(rq, 2, 5, 3);12console.log("sliced3 = " + sliced3);13var sliced4 = rQslice(rq, 2, 5, 4);14console.log("sliced4 = " + sliced4);15var sliced5 = rQslice(rq, 2, 5, 5);16console.log("sliced5 = " + sliced5);17var sliced6 = rQslice(rq, 2, 5, 6);18console.log("sliced6 = " + sliced6);19var sliced7 = rQslice(rq, 2, 5, 7);20console.log("sliced7 = " + sliced7);21var sliced8 = rQslice(rq, 2, 5, 8);22console.log("sliced8 = " + sliced8);23var sliced9 = rQslice(rq, 2, 5, 9);24console.log("sliced9 = " + sliced9);25var sliced10 = rQslice(rq, 2, 5, 10);26console.log("sliced10 = " + sliced10);27var sliced11 = rQslice(rq, 2, 5, 11);28console.log("sliced11 = " + sliced11);29var sliced12 = rQslice(rq, 2, 5, 12);30console.log("sliced12 = " + sliced12);31var sliced13 = rQslice(rq, 2, 5, 13);32console.log("sliced13 = " + sliced13);33var sliced14 = rQslice(rq, 2, 5, 14);34console.log("Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!
